Checkbox Field
Checkbox component integrated with TanStack Form via React context for boolean field management.
npx shadcn@latest add @shuip/tsf-checkbox-field
pnpm dlx shadcn@latest add @shuip/tsf-checkbox-field
bun x shadcn@latest add @shuip/tsf-checkbox-field
'use client';
import { Checkbox } from '@/components/ui/checkbox';
import { Field, FieldDescription, FieldError, FieldLabel } from '@/components/ui/field';
import { useFieldContext } from '@/components/ui/shuip/tanstack-form/form-context';
export interface CheckboxFieldProps {
label: string;
description?: string;
fieldProps?: React.ComponentProps<typeof Field>;
props?: React.ComponentProps<typeof Checkbox>;
}
export function CheckboxField({ label, description, fieldProps, props }: CheckboxFieldProps) {
const field = useFieldContext<boolean>();
const { isValid, errors } = field.state.meta;
return (
<Field className='gap-2' data-invalid={!isValid} {...fieldProps}>
<div className='flex items-center gap-2'>
<Checkbox
id={field.name}
name={field.name}
checked={field.state.value}
onCheckedChange={(checked) => field.handleChange(checked === true)}
onBlur={field.handleBlur}
aria-invalid={!isValid}
{...props}
/>
<FieldLabel htmlFor={field.name} className='text-sm cursor-pointer'>
{label}
</FieldLabel>
</div>
{!isValid && (
<FieldError
className='text-xs text-left'
errors={errors.map((error) => ({ message: typeof error === 'string' ? error : error?.message }))}
/>
)}
{description && <FieldDescription className='text-xs'>{description}</FieldDescription>}
</Field>
);
}
The command is the same for Base UI and Radix: the CLI reads the style in your components.json and installs the matching variant.
Checkboxes are binary inputs for opting in or accepting terms. CheckboxField combines Radix UI's Checkbox with an inline clickable label, making the entire label area interactive — not just the checkbox itself.
The component reads the surrounding field via useFieldContext, so you compose it inside a <form.AppField>. Common patterns include required checkboxes for terms acceptance (validated with !value check) or grouped checkboxes for multi-select options (using nested field paths like features.notifications).
Built-in features
- Context-bound field state via
useFieldContext— no prop drilling - Clickable inline label positioned next to checkbox
- Boolean field type for true/false state
- Required validation for terms acceptance
- Nested paths for grouped checkboxes
Setup
Field components are bound via React context. In your project, create lib/form.ts once:
// lib/form.ts
import { createFormHook } from '@tanstack/react-form';
import { fieldContext, formContext } from '@/components/ui/shuip/tanstack-form/form-context';
import { CheckboxField } from '@/components/ui/shuip/tanstack-form/checkbox-field';
import { SubmitButton } from '@/components/ui/shuip/tanstack-form/submit-button';
export const { useAppForm } = createFormHook({
fieldContext,
formContext,
fieldComponents: { CheckboxField },
formComponents: { SubmitButton },
});See the form-context item for details.
Required checkbox
import { useAppForm } from '@/lib/form';
const form = useAppForm({
defaultValues: { terms: false },
onSubmit: async ({ value }) => {
await saveData(value);
},
});
<form.AppField
name='terms'
validators={{
onChange: ({ value }) =>
!value ? 'You must accept the terms' : undefined,
}}
children={(field) => (
<field.CheckboxField label='I accept the terms and conditions' />
)}
/>Examples
Dependent Fields
'use client';
import { createFormHook, useStore } from '@tanstack/react-form';
import { CheckboxField } from '@/components/ui/shuip/tanstack-form/checkbox-field';
import { fieldContext, formContext } from '@/components/ui/shuip/tanstack-form/form-context';
import { InputField } from '@/components/ui/shuip/tanstack-form/input-field';
import { SubmitButton } from '@/components/ui/shuip/tanstack-form/submit-button';
const { useAppForm } = createFormHook({
fieldContext,
formContext,
fieldComponents: { CheckboxField, InputField },
formComponents: { SubmitButton },
});
export default function TsfCheckboxFieldDependentFieldsExample() {
const form = useAppForm({
defaultValues: {
customShipping: false,
shippingAddress: '',
sameAsBilling: false,
billingAddress: '',
},
onSubmit: async ({ value }) => {
await new Promise((resolve) => setTimeout(resolve, 1000));
alert(JSON.stringify(value, null, 2));
},
});
const customShipping = useStore(form.store, (state) => state.values.customShipping);
const sameAsBilling = useStore(form.store, (state) => state.values.sameAsBilling);
return (
<form
onSubmit={(e) => {
e.preventDefault();
form.handleSubmit();
}}
className='space-y-4'
>
<form.AppField
name='billingAddress'
validators={{
onChange: ({ value }) => (!value ? 'Billing address is required' : undefined),
}}
children={(field) => <field.InputField label='Billing Address' />}
/>
<form.AppField
name='customShipping'
listeners={{
onChange: ({ value }) => {
if (!value) {
form.setFieldValue('shippingAddress', '');
form.setFieldValue('sameAsBilling', false);
}
},
}}
children={(field) => <field.CheckboxField label='Use a different shipping address' />}
/>
{customShipping && (
<div className='space-y-4 pl-6 border-l-2'>
<form.AppField
name='sameAsBilling'
listeners={{
onChange: ({ value }) => {
if (!value) {
form.setFieldValue('shippingAddress', '');
} else {
form.setFieldValue('shippingAddress', form.getFieldValue('billingAddress'));
}
},
}}
children={(field) => <field.CheckboxField label='Same as billing address' />}
/>
<form.AppField
name='shippingAddress'
validators={{
onChange: ({ value }) => {
if (!value && customShipping) return 'Shipping address is required';
return undefined;
},
}}
children={(field) => <field.InputField label='Shipping Address' props={{ disabled: sameAsBilling }} />}
/>
</div>
)}
<form.AppForm>
<form.SubmitButton>Submit Order</form.SubmitButton>
</form.AppForm>
</form>
);
}
Default
'use client';
import { createFormHook } from '@tanstack/react-form';
import { CheckboxField } from '@/components/ui/shuip/tanstack-form/checkbox-field';
import { fieldContext, formContext } from '@/components/ui/shuip/tanstack-form/form-context';
import { SubmitButton } from '@/components/ui/shuip/tanstack-form/submit-button';
const { useAppForm } = createFormHook({
fieldContext,
formContext,
fieldComponents: { CheckboxField },
formComponents: { SubmitButton },
});
export default function TsfCheckboxFieldExample() {
const form = useAppForm({
defaultValues: {
termsExample: false,
marketingExample: false,
},
onSubmit: async ({ value }) => {
await new Promise((resolve) => setTimeout(resolve, 1000));
alert(JSON.stringify(value, null, 2));
},
});
return (
<form
onSubmit={(e) => {
e.preventDefault();
form.handleSubmit();
}}
className='space-y-4'
>
<form.AppField
name='termsExample'
validators={{
onChange: ({ value }) => (!value ? 'You must accept the terms and conditions' : undefined),
}}
children={(field) => <field.CheckboxField label='I accept the terms and conditions' />}
/>
<form.AppField
name='marketingExample'
children={(field) => (
<field.CheckboxField
label='Send me promotional emails'
description='Optional: Receive updates about new features'
/>
)}
/>
<form.AppForm>
<form.SubmitButton />
</form.AppForm>
</form>
);
}
Group
'use client';
import { createFormHook } from '@tanstack/react-form';
import { CheckboxField } from '@/components/ui/shuip/tanstack-form/checkbox-field';
import { fieldContext, formContext } from '@/components/ui/shuip/tanstack-form/form-context';
import { SubmitButton } from '@/components/ui/shuip/tanstack-form/submit-button';
const { useAppForm } = createFormHook({
fieldContext,
formContext,
fieldComponents: { CheckboxField },
formComponents: { SubmitButton },
});
export default function TsfCheckboxFieldGroupExample() {
const form = useAppForm({
defaultValues: {
features: {
notifications: false,
analytics: false,
darkMode: false,
apiAccess: false,
},
},
onSubmit: async ({ value }) => {
await new Promise((resolve) => setTimeout(resolve, 1000));
alert(JSON.stringify(value, null, 2));
},
});
return (
<form
onSubmit={(e) => {
e.preventDefault();
form.handleSubmit();
}}
className='space-y-4'
>
<div className='space-y-3'>
<h3 className='font-semibold'>Features</h3>
<p className='text-sm text-muted-foreground'>Select the features you want to enable</p>
<div className='space-y-3'>
<form.AppField
name='features.notifications'
children={(field) => (
<field.CheckboxField
label='Enable push notifications'
description='Receive real-time updates about your activity'
/>
)}
/>
<form.AppField
name='features.analytics'
children={(field) => (
<field.CheckboxField
label='Enable analytics tracking'
description='Help us improve by sharing usage data'
/>
)}
/>
<form.AppField
name='features.darkMode'
children={(field) => (
<field.CheckboxField label='Enable dark mode' description='Switch to a darker color scheme' />
)}
/>
<form.AppField
name='features.apiAccess'
children={(field) => (
<field.CheckboxField label='Enable API access' description='Get programmatic access to your data' />
)}
/>
</div>
</div>
<form.AppForm>
<form.SubmitButton>Save Preferences</form.SubmitButton>
</form.AppForm>
</form>
);
}
Props
Prop
Type