Checkbox Field

Checkbox component integrated with TanStack Form for type-safe 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
import type {
DeepKeys,
DeepValue,
FieldAsyncValidateOrFn,
FieldOptions,
FieldValidateOrFn,
FormAsyncValidateOrFn,
FormValidateOrFn,
ReactFormApi,
} from '@tanstack/react-form';
import { Checkbox } from '@/components/ui/checkbox';
import { Field, FieldDescription, FieldError, FieldLabel } from '@/components/ui/field';
export interface CheckboxFieldProps<
TFormData,
TName extends DeepKeys<TFormData>,
TData extends DeepValue<TFormData, TName> = DeepValue<TFormData, TName>,
> {
form: ReactFormApi<
TFormData,
undefined | FormValidateOrFn<TFormData>,
undefined | FormValidateOrFn<TFormData>,
undefined | FormAsyncValidateOrFn<TFormData>,
undefined | FormValidateOrFn<TFormData>,
undefined | FormAsyncValidateOrFn<TFormData>,
undefined | FormValidateOrFn<TFormData>,
undefined | FormAsyncValidateOrFn<TFormData>,
undefined | FormValidateOrFn<TFormData>,
undefined | FormAsyncValidateOrFn<TFormData>,
undefined | FormAsyncValidateOrFn<TFormData>,
any
>;
name: TName;
label: string;
description?: string;
formProps?: Partial<
FieldOptions<
TFormData,
TName,
TData,
undefined | FieldValidateOrFn<TFormData, TName, TData>,
undefined | FieldValidateOrFn<TFormData, TName, TData>,
undefined | FieldAsyncValidateOrFn<TFormData, TName, TData>,
undefined | FieldValidateOrFn<TFormData, TName, TData>,
undefined | FieldAsyncValidateOrFn<TFormData, TName, TData>,
undefined | FieldValidateOrFn<TFormData, TName, TData>,
undefined | FieldAsyncValidateOrFn<TFormData, TName, TData>,
undefined | FieldValidateOrFn<TFormData, TName, TData>,
undefined | FieldAsyncValidateOrFn<TFormData, TName, TData>
>
>;
fieldProps?: React.ComponentProps<typeof Field>;
props?: React.ComponentProps<typeof Checkbox>;
}
export function CheckboxField<
TFormData,
TName extends DeepKeys<TFormData>,
TData extends DeepValue<TFormData, TName> = DeepValue<TFormData, TName>,
>({ form, name, label, description, formProps, fieldProps, props }: CheckboxFieldProps<TFormData, TName, TData>) {
return (
<form.Field name={name} {...formProps}>
{(field) => {
const { isValid, errors } = field.state.meta;
return (
<Field className='gap-2' data-invalid={!isValid} {...fieldProps}>
<div className='flex items-center gap-2'>
<Checkbox
name={field.name}
checked={field.state.value as boolean}
onCheckedChange={(checked) => field.handleChange(checked as TData)}
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: error }))} />
)}
{description && <FieldDescription className='text-xs'>{description}</FieldDescription>}
</Field>
);
}}
</form.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.

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

  • Clickable inline label positioned next to checkbox
  • Boolean field type for true/false state
  • Required validation for terms acceptance
  • Nested paths for grouped checkboxes

Required checkbox

<CheckboxField
    form={form}
    name='terms'
    label='I accept the terms and conditions'
    formProps={{
      validators: {
        onChange: ({ value }) =>
          !value ? 'You must accept the terms' : undefined
      }
    }}
/>

Examples

Dependent Fields

Loading...
'use client';
import { useForm, useStore } from '@tanstack/react-form';
import { CheckboxField } from '#/registry/ui/tsf-checkbox-field';
import { InputField } from '@/components/ui/shuip/tanstack-form/input-field';
import { SubmitButton } from '@/components/ui/shuip/tanstack-form/submit-button';
export default function TsfCheckboxFieldDependentFieldsExample() {
const form = useForm({
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'
>
<InputField
form={form}
name='billingAddress'
label='Billing Address'
formProps={{
validators: {
onChange: ({ value }) => (!value ? 'Billing address is required' : undefined),
},
}}
/>
<CheckboxField
form={form}
name='customShipping'
label='Use a different shipping address'
formProps={{
listeners: {
onChange: ({ value }) => {
if (!value) {
form.setFieldValue('shippingAddress', '');
form.setFieldValue('sameAsBilling', false);
}
},
},
}}
/>
{customShipping && (
<div className='space-y-4 pl-6 border-l-2'>
<CheckboxField
form={form}
name='sameAsBilling'
label='Same as billing address'
formProps={{
listeners: {
onChange: ({ value }) => {
if (!value) {
form.setFieldValue('shippingAddress', '');
} else {
form.setFieldValue('shippingAddress', form.getFieldValue('billingAddress'));
}
},
},
}}
/>
<InputField
form={form}
name='shippingAddress'
label='Shipping Address'
props={{ disabled: sameAsBilling }}
formProps={{
validators: {
onChange: ({ value }) => {
if (!value && customShipping) return 'Shipping address is required';
return undefined;
},
},
}}
/>
</div>
)}
<SubmitButton form={form}>Submit Order</SubmitButton>
</form>
);
}

Default

Loading...
'use client';
import { useForm } from '@tanstack/react-form';
import { CheckboxField } from '@/components/ui/shuip/tanstack-form/checkbox-field';
import { SubmitButton } from '@/components/ui/shuip/tanstack-form/submit-button';
export default function TsfCheckboxFieldExample() {
const form = useForm({
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'
>
<CheckboxField
form={form}
name='termsExample'
label='I accept the terms and conditions'
formProps={{
validators: {
onChange: ({ value }) => (!value ? 'You must accept the terms and conditions' : undefined),
},
}}
/>
<CheckboxField
form={form}
name='marketingExample'
label='Send me promotional emails'
description='Optional: Receive updates about new features'
/>
<SubmitButton form={form} />
</form>
);
}

Group

Loading...
'use client';
import { useForm } from '@tanstack/react-form';
import { CheckboxField } from '#/registry/ui/tsf-checkbox-field';
import { SubmitButton } from '@/components/ui/shuip/tanstack-form/submit-button';
export default function TsfCheckboxFieldGroupExample() {
const form = useForm({
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'>
<CheckboxField
form={form}
name='features.notifications'
label='Enable push notifications'
description='Receive real-time updates about your activity'
/>
<CheckboxField
form={form}
name='features.analytics'
label='Enable analytics tracking'
description='Help us improve by sharing usage data'
/>
<CheckboxField
form={form}
name='features.darkMode'
label='Enable dark mode'
description='Switch to a darker color scheme'
/>
<CheckboxField
form={form}
name='features.apiAccess'
label='Enable API access'
description='Get programmatic access to your data'
/>
</div>
</div>
<SubmitButton form={form}>Save Preferences</SubmitButton>
</form>
);
}

Props

Prop

Type

On this page