RHFCheckbox
Checkbox component integrated with React Hook Form that preserves value types
RHFCheckbox
A controlled checkbox component for React Hook Form that preserves value types. Unlike native checkboxes that convert values to strings, this component maintains the original data type. Supports both single-select (like a radio) and multi-select modes with optional indeterminate state.
Installation
import { RHFCheckbox } from '@geckoui/geckoui';Basic Usage
Single Checkbox Mode
import { useForm, FormProvider } from 'react-hook-form';
import { RHFCheckbox } from '@geckoui/geckoui';
function Example() {
const methods = useForm({
defaultValues: {
agreement: null
}
});
return (
<FormProvider {...methods}>
<RHFCheckbox
name="agreement"
value="agreed"
label="I agree to the terms and conditions"
single
/>
</FormProvider>
);
}Multi-Select Mode
import { useForm, FormProvider } from 'react-hook-form';
import { RHFCheckbox } from '@geckoui/geckoui';
function Example() {
const methods = useForm({
defaultValues: {
options: []
}
});
return (
<FormProvider {...methods}>
<div className="flex flex-col gap-3">
<RHFCheckbox name="options" value="option1" label="Option 1" />
<RHFCheckbox name="options" value="option2" label="Option 2" />
<RHFCheckbox name="options" value="option3" label="Option 3" />
</div>
</FormProvider>
);
}Props API
Extends all props from Checkbox component plus:
| Prop | Type | Default | Description |
|---|---|---|---|
name | string | - | Field name (required) |
control | Control | Auto-injected | Optional: Pass explicitly for nested forms or custom form context |
rules | RegisterOptions | - | Inline validation rules |
label | ReactNode | Function | - | Label displayed next to checkbox |
labelClassName | string | - | CSS class for the label |
value | unknown | - | Value to set when checked |
single | boolean | false | Act as single selection (stores value, not array) |
uncheckedValue | unknown | null | Value when unchecked (only with single) |
partial | boolean | Function | false | Show indeterminate state |
onChange | (value: unknown) => void | - | Change callback |
| ...rest | CheckboxProps | - | All Checkbox component props |
Examples
Single with Custom Unchecked Value
<RHFCheckbox
name="enabled"
value="yes"
uncheckedValue="no"
label="Enable notifications"
single
/>With Validation (Zod)
import { useForm, FormProvider } from 'react-hook-form';
import { zodResolver } from '@hookform/resolvers/zod';
import { z } from 'zod';
import { RHFCheckbox, RHFInputGroup } from '@geckoui/geckoui';
const schema = z.object({
terms: z.literal('agreed', { message: 'You must agree to the terms' })
});
function Example() {
const methods = useForm({
resolver: zodResolver(schema),
mode: 'onBlur',
defaultValues: { terms: null }
});
return (
<FormProvider {...methods}>
<RHFInputGroup required>
<RHFCheckbox
name="terms"
value="agreed"
label="I agree to the terms and conditions"
single
/>
</RHFInputGroup>
</FormProvider>
);
}With Partial/Indeterminate State
<div className="flex flex-col gap-3">
<RHFCheckbox
name="languages"
value="js"
partial={({ field }) => {
const selected = field.value || [];
return selected.length > 0 && selected.length < 2;
}}
label="JavaScript"
/>
<RHFCheckbox
name="languages"
value="ts"
partial={({ field }) => {
const selected = field.value || [];
return selected.length > 0 && selected.length < 2;
}}
label="TypeScript"
/>
</div>With Object Values
<RHFCheckbox
name="settings"
value={{ feature: 'advanced', enabled: true }}
label="Enable advanced features"
single
/>Disabled State
<div className="flex gap-4">
<RHFCheckbox name="readonly" value="checked" label="Checked & Disabled" disabled />
<RHFCheckbox name="disabled" value="unchecked" label="Unchecked & Disabled" disabled />
</div>Complete Form Example
import { useForm, FormProvider } from 'react-hook-form';
import { zodResolver } from '@hookform/resolvers/zod';
import { z } from 'zod';
import { RHFCheckbox, RHFInputGroup, Button } from '@geckoui/geckoui';
const schema = z.object({
terms: z.literal('agreed', { message: 'You must agree to the terms' }),
newsletter: z.string().nullable().optional(),
interests: z.array(z.string()).min(1, 'Select at least one interest')
});
function FormExample() {
const methods = useForm({
resolver: zodResolver(schema),
mode: 'onBlur',
defaultValues: {
terms: null,
newsletter: null,
interests: []
}
});
return (
<FormProvider {...methods}>
<form onSubmit={methods.handleSubmit(console.log)}>
<RHFInputGroup required>
<RHFCheckbox
name="terms"
value="agreed"
label="I agree to the terms and conditions"
single
/>
</RHFInputGroup>
<RHFInputGroup>
<RHFCheckbox
name="newsletter"
value="subscribed"
label="Subscribe to newsletter"
single
/>
</RHFInputGroup>
<RHFInputGroup label="Interests" required>
<div className="flex flex-col gap-2">
<RHFCheckbox name="interests" value="tech" label="Technology" />
<RHFCheckbox name="interests" value="design" label="Design" />
<RHFCheckbox name="interests" value="business" label="Business" />
</div>
</RHFInputGroup>
<Button type="submit">Submit</Button>
</form>
</FormProvider>
);
}Inline Rules Validation
For simple validation, use the rules prop:
<RHFCheckbox
name="terms"
value="agreed"
label="I agree to the terms"
single
rules={{ required: 'You must accept the terms' }}
/>For complex forms, we recommend using a schema resolver (Zod, Yup) instead.
Single vs Multi Mode
Single Mode (single={true})
- Acts like a radio button
- Stores one value or
uncheckedValue - When checked: stores the
valueprop - When unchecked: stores
uncheckedValue(default:null)
// Result: "agreed" or null
<RHFCheckbox name="terms" value="agreed" single />
// Result: "yes" or "no"
<RHFCheckbox name="enabled" value="yes" uncheckedValue="no" single />Multi Mode (default)
- Stores an array of selected values
- Multiple checkboxes with same
namecreate a group - Each checkbox adds/removes its value from the array
// Result: ["option1", "option3"]
<RHFCheckbox name="options" value="option1" label="Option 1" />
<RHFCheckbox name="options" value="option2" label="Option 2" />
<RHFCheckbox name="options" value="option3" label="Option 3" />Value Type Preservation
Unlike native HTML checkboxes, RHFCheckbox preserves the original value type:
// String values
<RHFCheckbox name="plan" value="pro" single />
// Boolean values
<RHFCheckbox name="enabled" value={true} single />
// Number values
<RHFCheckbox name="age" value={18} single />
// Object values
<RHFCheckbox name="settings" value={{ feature: "advanced" }} single />
// Array values
<RHFCheckbox name="data" value={[1, 2, 3]} single />Indeterminate State
Use the partial prop to show an indeterminate state (useful for "select all" scenarios):
// Boolean partial
<RHFCheckbox
name="selectAll"
value="all"
partial={someSelected && !allSelected}
single
/>
// Dynamic partial based on field state
<RHFCheckbox
name="items"
value="item1"
partial={({ field }) => field.value.length > 0 && field.value.length < total}
/>Error States
RHFCheckbox doesn't apply error styling directly. Use RHFInputGroup to display errors:
<RHFInputGroup required>
<RHFCheckbox name="terms" value="agreed" label="I agree" single />
</RHFInputGroup>Tip: Use mode: 'onBlur' in useForm to validate on blur:
const methods = useForm({
resolver: zodResolver(schema),
mode: 'onBlur'
});Accessibility
- Inherits all accessibility features from Checkbox component
- Proper ARIA attributes
- Keyboard navigable (Space/Enter to toggle)
- Screen reader compatible
- Disabled state properly communicated