RHFCheckbox
Checkbox component integrated with React Hook Form that preserves value types
You are reading the v1 docs. v2 renamed several props and replaced GeckoUIPortal with GeckoUIProvider. Read this page in v2.
Live examples are only rendered in the v2 docs.
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