Checkbox Field

Checkbox component integrated with TanStack Form via React context for boolean field management.

npx shadcn@latest add https://shuip.plvo.dev/r/tsf-checkbox-field.json
pnpm dlx shadcn@latest add https://shuip.plvo.dev/r/tsf-checkbox-field.json
bun x shadcn@latest add https://shuip.plvo.dev/r/tsf-checkbox-field.json
'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>
);
}
Loading...

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

Loading...
'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

Loading...
'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

Loading...
'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

On this page