Toggle Field

Checkbox

A boolean checkbox field for accepting terms, toggling options, or confirming choices.

stable
checkbox boolean toggle terms

Interactive demo of checkbox field

Get weekly updates, tips, and product announcements

Promotional content and special offers

Required to create an account

/**
 * 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

PropTypeDefaultDescription
type'checkbox'-Field type (required)
labelstring-Label shown next to checkbox
helperTextstring-Description text below the checkbox
columnsPartial<Record<Breakpoint, number>>-Grid columns by breakpoint
disabledboolean | function | ConditionGroupfalseDisable the checkbox
hiddenboolean | function | ConditionGroup | ResponsivefalseHide 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 }
}

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 label prop renders an associated label next to the control
  • helperText renders description text, and validation errors render below the field