Docs
ComponentsKibbleProvider
View as Markdown(opens in a new tab)

KibbleProvider

Provide the client and display options once so every Form can omit them.

Wrap your app (or a layout) once with KibbleProvider and every Form below it finds the client through context. It also carries site-wide display options: theme, size, classNames, requiredIndicator and optionalIndicator.

Preview

KibbleProvider renders nothing visible. See the required indicator preview for its effect.

Usage

// app/providers.tsx (Next.js app router: a client component, because the client holds functions)
'use client';
import { KibbleProvider } from 'kibbleforms';
import { kibble } from '@/kibble/client';

export function Providers({ children }: { children: React.ReactNode }) {
  return <KibbleProvider client={kibble}>{children}</KibbleProvider>;
}

// any page, even a server component: the schema is plain data
import { Form } from 'kibbleforms';
import { waitlist } from '@/kibble/forms/waitlist';

export default function Page() {
  return <Form schema={waitlist} />;
}

Props

Prop Type Default Description
client KibbleClient inherited from a parent provider From createClient. Optional when nesting: a provider without client keeps the parent's.
requiredIndicator ReactNode | null inherited, else nothing What Label appends to required fields.
optionalIndicator ReactNode | null inherited, else muted "Optional" What Label appends to optional fields. null for nothing.
theme KibbleTheme inherited Colors, radius, font, colorScheme for every form below; merged per key with a parent provider's and with each form's (the form wins). See Theming.
size 'sm' | 'md' | 'lg' inherited, else 'md' Default size for every form below; a form's size wins.
classNames Partial<Record<Slot, string>> inherited Classes per part for every form below. Per slot, the parent's, this provider's and the form's classes are all kept, in that order, then the component's own className.
children ReactNode —

Renders no DOM element and has no className or data-kb.

Hooks: useKibbleClient() returns the nearest client or null; useFormOptions() returns the merged display options; useSlot(slot) returns the merged classes for one slot (for your own components). layout is a Form prop only. Form scopes its own options (merged over the provider's) to its subtree.

Variants and states

Nesting merges: an inner provider can change one option and keep the rest.

<KibbleProvider client={kibble} theme={{ primary: 'var(--brand)', radius: 'lg' }} size="sm" classNames={{ label: 'uppercase tracking-wide' }}>
  <Form schema={waitlist} />                                    {/* brand primary, lg radius, sm, uppercase labels */}
  <Form schema={demo} size="md" theme={{ colorScheme: 'dark' }} /> {/* same, but md and dark */}
</KibbleProvider>
<KibbleProvider client={kibble} optionalIndicator={<span> (optional)</span>}>
  …most forms…
  <KibbleProvider requiredIndicator={<span> (required)</span>} optionalIndicator={null}>
    <Form schema={application} />   {/* same client, different marks */}
  </KibbleProvider>
</KibbleProvider>

A Form with its own client prop ignores the context client; requiredIndicator on Form overrides the provider's for that form only.

Customization

Renders no DOM. theme becomes inline variables on each Form root; you can also set --kb-* variables on :root or a wrapper (see Styling).

Accessibility

No effect on its own; see Label for how the required indicator is exposed (aria-hidden mark, aria-required on the control).

Form, Theming, Label, Help, FieldError, Client SDK