Gecko UIGecko UI

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:

PropTypeDefaultDescription
namestring-Field name (required)
controlControlAuto-injectedOptional: Pass explicitly for nested forms or custom form context
rulesRegisterOptions-Inline validation rules
labelReactNode | Function-Label displayed next to checkbox
labelClassNamestring-CSS class for the label
valueunknown-Value to set when checked
singlebooleanfalseAct as single selection (stores value, not array)
uncheckedValueunknownnullValue when unchecked (only with single)
partialboolean | FunctionfalseShow indeterminate state
onChange(value: unknown) => void-Change callback
...restCheckboxProps-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 value prop
  • 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 name create 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