Interactive demo of checkbox field
/**
* Checkbox Field Demo - Interactive examples of checkbox fields
*/
import { Form, FormBuilder } from '@saastro/forms';
import { FormProvider } from '@/components/FormProvider';
import { TooltipProvider } from '@/components/ui/tooltip';
const config = FormBuilder.create('checkbox-demo')
.layout('manual')
.columns(12)
.addField('newsletter', (f) =>
f
.type('checkbox')
.label('Subscribe to newsletter')
.helperText('Get weekly updates, tips, and product announcements')
.optional()
.columns({ default: 12 }),
)
.addField('marketing', (f) =>
f
.type('checkbox')
.label('Receive marketing emails')
.helperText('Promotional content and special offers')
.optional()
.columns({ default: 12 }),
)
.addField('terms', (f) =>
f
.type('checkbox')
.label('I agree to the Terms of Service and Privacy Policy')
.helperText('Required to create an account')
.required()
.mustBeTrue('You must accept the terms to continue')
.columns({ default: 12 }),
)
.addField('age', (f) =>
f
.type('checkbox')
.label('I am 18 years or older')
.required()
.mustBeTrue('You must be 18 or older')
.columns({ default: 12 }),
)
.addStep('main', ['newsletter', 'marketing', 'terms', 'age'])
.build();
export default function CheckboxDemo() {
const handleSubmit = (data: Record<string, unknown>) => {
console.log('Form submitted:', data);
alert('Form submitted! Check console for data.');
};
return (
<TooltipProvider>
<FormProvider>
<Form config={config} onSubmit={handleSubmit} className="space-y-4" />
</FormProvider>
</TooltipProvider>
);
} Overview
The checkbox field is a boolean toggle for accepting terms, toggling options, or confirming choices. For multiple checkboxes that share a common label, see Checkbox Group.
Usage
Basic Checkbox
import { FormBuilder } from '@saastro/forms';
const config = FormBuilder.create('signup')
.addField('newsletter', (f) =>
f
.type('checkbox')
.label('Subscribe to newsletter')
.helperText('Get weekly updates and tips')
.optional(),
)
.addStep('main', ['newsletter'])
.build();
Terms Acceptance
Use mustBeTrue() to require the checkbox to be checked:
.addField('terms', (f) =>
f.type('checkbox')
.label('I agree to the Terms of Service and Privacy Policy')
.mustBeTrue('You must accept the terms to continue')
)
If you omit the custom message, the default error is "You must accept this".
With Description
.addField('marketing', (f) =>
f.type('checkbox')
.label('Marketing emails')
.helperText('Receive promotional content, product updates, and special offers. You can unsubscribe at any time.')
.optional()
)
JSON Configuration
{
"type": "checkbox",
"label": "I agree to the Terms of Service",
"schema": {
"mustBeTrue": true,
"mustBeTrueMessage": "You must accept the terms"
}
}
A shorter JSON form also works: a top-level "required": true with no "schema" key requires the checkbox to be checked, with the default error message "You must accept this":
{
"type": "checkbox",
"label": "I agree to the Terms of Service",
"required": true
}
Props
| Prop | Type | Default | Description |
|---|---|---|---|
type | 'checkbox' | - | Field type (required) |
label | string | - | Label shown next to checkbox |
helperText | string | - | Description text below the checkbox |
columns | Partial<Record<Breakpoint, number>> | - | Grid columns by breakpoint |
disabled | boolean | function | ConditionGroup | false | Disable the checkbox |
hidden | boolean | function | ConditionGroup | Responsive | false | Hide the field |
Validation
Optional
.addField('newsletter', (f) =>
f.type('checkbox')
.label('Subscribe')
.optional() // Initial value is false; both checked and unchecked are valid
)
Required to be True
.addField('terms', (f) =>
f.type('checkbox')
.label('Accept terms')
.mustBeTrue('You must accept the terms')
)
Note: with the builder, use mustBeTrue() — not required() — to force the box to be checked. required() on a checkbox only validates that the value is a boolean, so an unchecked box (false) still passes. Without a custom message, mustBeTrue() shows "You must accept this".
With Zod
import { z } from 'zod';
.addField('terms', (f) =>
f.type('checkbox')
.label('Accept terms')
.schema(z.literal(true, { message: 'You must accept the terms' }))
)
Conditional Logic
Show checkbox based on another field
.addField('premium', (f) =>
f.type('checkbox')
.label('Upgrade to Premium')
.hidden({
conditions: [{ field: 'plan', operator: 'equals', value: 'free' }],
operator: 'AND',
})
)
hidden() takes a ConditionGroup — an object with a conditions array and an 'AND' | 'OR' operator — not a bare condition.
Disable based on condition
.addField('confirm', (f) =>
f.type('checkbox')
.label('Confirm changes')
.disabled((values) => !values.hasChanges)
)
Styling
Custom Classes
.addField('terms', (f) =>
f.type('checkbox')
.label('Accept terms')
.mustBeTrue()
.classNames({
wrapper: 'bg-muted/30 p-3 rounded-md border',
input: 'border-primary data-[state=checked]:bg-primary',
label: 'font-medium',
error: 'text-destructive text-sm',
})
)
Responsive Layout
.addField('newsletter', (f) =>
f.type('checkbox')
.label('Subscribe to newsletter')
.optional()
.columns({ default: 12, md: 6 })
)
Per-field column spans apply only when the form uses manual layout mode (.layout('manual') with the builder, or "layout": { "mode": "manual" } in JSON) — in 'auto' mode they are ignored. See the Layout guide.
JSON Styling
{
"type": "checkbox",
"label": "Accept terms",
"schema": { "mustBeTrue": true },
"wrapper_className": "bg-muted/30 p-3 rounded-md",
"label_className": "font-medium",
"columns": { "default": 12 }
}
Related Fields
- Switch - Toggle switch (more prominent visual)
- Checkbox Group - Multiple related checkboxes
- Button Checkbox - Checkboxes styled as buttons
Accessibility
The checkbox control is rendered by the Checkbox component you inject (via the components prop or ComponentProvider), using the checked / onCheckedChange contract. Accessibility characteristics therefore come from your component library:
- With Radix-based components (e.g. shadcn/ui), you get keyboard activation (Space), focus-visible states, and screen reader announcements out of the box
- The
labelprop renders an associated label next to the control helperTextrenders description text, and validation errors render below the field