Components
kibbleforms has two layers. Headless primitives (kibbleforms/headless) carry behavior and accessibility with no styling: form state, validation, conditional logic, page navigation, partial saves, focus management, labels and aria. Styled components (kibbleforms) sit on top and look finished out of the box, authored in Tailwind and themed with --kb-* CSS variables. Both are replaceable: restyle with the theme, size, layout and classNames props, variables or classes, or start from the primitives.
Zero-markup form
import { Form } from 'kibbleforms';
import { kibble } from '@/kibble/client';
import { waitlist } from '@/kibble/forms/waitlist';
<Form schema={waitlist} client={kibble} />
With no children, Form renders every page from the schema: one page gives you the fields and a submit button; several pages give you MultiStep with a progress line, Back and Next. After submit it shows the settings.success message, or redirects. Wrap the app once in KibbleProvider to drop the client prop.
Composing
Children replace the automatic layout. Field renders any field by key (label, help, the right control, error); the controls, Label, Help, FieldError, Submit, Page, Progress and MultiStep can be arranged freely, mixed with your own JSX.
import { Form, Field, Submit } from 'kibbleforms';
<Form schema={contact} client={kibble} className="grid gap-6">
<div className="grid grid-cols-2 gap-4">
<Field name="first_name" />
<Field name="last_name" />
</div>
<Field name="email" />
<Field name="message" />
<Submit className="w-auto justify-self-end" />
</Form>
Every styled component accepts className (merged with tailwind-merge, so your utilities win) and renders data-kb="<name>" for CSS. Hidden fields and pages (showWhen) render nothing. See Styling for the three depths of customization.
All components
| Component | One line |
|---|---|
| Form | The whole form from a schema; auto-renders or takes children. |
| Field | Label + help + the control for the field type + error, from one name. |
| Input | Single-line control for short_text, email, phone, url, number. |
| Textarea | Multi-line control for long_text. |
| Select | Styled native select for dropdown. |
| Choice | Radio group (single_choice) or checkbox group (multiple_choice). |
| YesNo | Two segmented buttons with radio semantics for yes_no. |
| Rating | 1..max buttons with radio semantics and arrow keys for rating. |
| DatePicker | Styled native date input for date. |
| Consent | Checkbox with inline label for consent. |
| Submit | Submit button with the label from settings.submitLabel and a loading state. |
| Success | The thank-you card from settings.success. |
| MultiStep | One page at a time with Progress, Back/Next and Submit; also covers Page and Progress. |
| Label, Help, FieldError | The text pieces around a control, and the requiredIndicator option. |
| KibbleProvider | Provide the client and display options once. |
| Headless | useKibbleForm, FormProvider, useForm, useField, usePage, useFormStatus, Honeypot, Turnstile, with a reference table of everything the hook returns. |
Behavior you get for free
- Client validation from the schema, with the same validator the server runs; format errors on blur, "required" on submit; focus moves to the first invalid control.
- Submit with an idempotency key and retries on network errors and 5xx; one logical submit never lands twice.
- Page URL, referrer and UTM parameters captured; a honeypot and time-to-submit as spam signals; Turnstile when a site key is set (Kibble checks it once it has the widget's secret key); agents that declare themselves (
actor) are labeled. - Partial saves when a visitor moves to the next step, never on keystrokes.
- Prefill from
initialValues,prefill: { query }anddefaultValue;autocompleteattributes from the schema. - Conditional fields and pages (Conditional logic), and
schedulesuccesses that land on a booking page with name and email filled in.
Component previews
The whole form from a schema: fields, submit, inline errors and the success state, with zero markup.
Label, help text, the right control for the field's type, and the inline error, from one `name`.
The single-line text control for short_text, email, phone, url and number fields.
The multi-line control for long_text fields.
A styled native select for dropdown fields.
A radio group for single_choice or a checkbox group for multiple_choice.
Two segmented buttons with radio semantics for yes_no fields.
Numbered buttons from 1 to max with radio semantics for rating fields.
A styled native date input for date fields.
A checkbox with its label inline, for consent fields that must be ticked.
The submit button, labelled from settings.submitLabel, with a loading state.
The thank-you card rendered from settings.success after a submission.
One page at a time with a progress line, Back and Next, and Submit on the last page. Covers MultiStep, Page and Progress.
The three text pieces around a control, usable on their own when you compose a field by hand.
Provide the client and display options once so every Form can omit them.
Behavior and accessibility with no markup: useKibbleForm, FormProvider, useForm, useField, usePage, useFormStatus, Honeypot, Turnstile.