RadioGroup | Dashforge-UI
DocsStarter Kits
v0.1.0-alpha

RadioGroup

A single-choice selection component built on MUI RadioGroup. Supports standalone usage, seamless DashForm integration with automatic field binding, validation error gating, and reactive visibility. Perfect for account type selection, shipping methods, plan tiers, and mutually exclusive choices.

Quick Start

Copy & Paste

import { RadioGroup } from '@dashforge/ui';

<RadioGroup
  name="accountType"
  label="Account Type"
  options={[
    { value: 'personal', label: 'Personal' },
    { value: 'business', label: 'Business' }
  ]}
/>

Examples

Common RadioGroup patterns and configurations

Basic

Single choice from mutually exclusive options

<RadioGroup
  name="accountType"
  label="Account Type"
  options={[
    { value: 'personal', label: 'Personal' },
    { value: 'business', label: 'Business' }
  ]}
/>
With Default Value

Pre-select an option at render time

<RadioGroup
  name="shipping"
  label="Shipping Method"
  defaultValue="standard"
  options={[
    { value: 'standard', label: 'Standard (5-7 days)' },
    { value: 'express', label: 'Express (2-3 days)' },
    { value: 'overnight', label: 'Overnight' }
  ]}
/>
Horizontal Layout

Display options side-by-side with row prop

<RadioGroup
  name="plan"
  label="Plan"
  row
  options={[
    { value: 'free', label: 'Free' },
    { value: 'pro', label: 'Pro' },
    { value: 'enterprise', label: 'Enterprise' }
  ]}
/>
With Disabled Option

Restrict specific choices from selection

<RadioGroup
  name="contact"
  label="Contact Method"
  options={[
    { value: 'email', label: 'Email' },
    { value: 'phone', label: 'Phone' },
    { value: 'sms', label: 'SMS', disabled: true }
  ]}
/>
Error State

Validation feedback for required selections

Please select an account type

<RadioGroup
  name="accountError"
  label="Account Type"
  options={[
    { value: 'personal', label: 'Personal' },
    { value: 'business', label: 'Business' }
  ]}
  error
  helperText="Please select an account type"
/>
Without Label

Render options without a group label

<RadioGroup
  name="standalone"
  options={[
    { value: 'option1', label: 'Option 1' },
    { value: 'option2', label: 'Option 2' }
  ]}
/>

Capabilities

Progressive adoption model from simple controlled component to reactive form integration

RadioGroup is designed for progressive adoption. Use it as a simple controlled component, integrate it with React Hook Form, or leverage Dashforge-native reactive capabilities. Choose the level that fits your team's workflow.

Controlled

Available Now

RadioGroup works as a standard React controlled component with familiar patterns. No proprietary lock-in required.

Standard value and onChange props

No proprietary lock-in

Easy incremental adoption

<RadioGroup
  value={accountType}
  onChange={(e) => setAccountType(e.target.value)}
  name="accountType"
  options={[
    { value: 'personal', label: 'Personal' },
    { value: 'business', label: 'Business' }
  ]}
/>

React Hook Form Ready

Integration-Friendly

Designed to integrate with React Hook Form workflows through DashForm. Compatible with existing form-library patterns.

Works through DashForm bridge

Validation and error handling supported

Fits existing RHF workflows

<DashForm>
  <RadioGroup 
    name="shippingMethod"
    label="Shipping Method"
    options={shippingOptions}
    rules={{ required: 'Please select a method' }}
  />
</DashForm>

Reactive Visibility

Available Now

RadioGroup can participate in engine-driven visibility rules through visibleWhen. Use it when single-choice depends on other form state.

Conditional rendering via visibleWhen

Engine evaluates the predicate

Useful for dependent choice fields

<RadioGroup
  name="businessType"
  label="Business Type"
  options={businessTypes}
  visibleWhen={(engine) => 
    engine.getNode('accountType')?.value === 'business'
  }
/>

Access Control (RBAC)

RadioGroup supports RBAC at two levels: group-level access controls the entire field, while option-level access controls individual choices. Group-level access has precedence.

Hide when unauthorized

<RadioGroup
  name="role"
  label="Role"
  access={{
    resource: 'user.role',
    action: 'read',
    onUnauthorized: 'hide'
  }}
  options={[
    { value: 'viewer', label: 'Viewer' },
    { value: 'editor', label: 'Editor' }
  ]}
/>

Disable when cannot edit

<RadioGroup
  name="role"
  label="Role"
  access={{
    resource: 'user.role',
    action: 'update',
    onUnauthorized: 'readonly'
  }}
  options={[
    { value: 'viewer', label: 'Viewer' },
    { value: 'editor', label: 'Editor' }
  ]}
/>

// Note: RadioGroup becomes disabled when readonly

Restrict specific options

<RadioGroup
  name="role"
  label="Role"
  options={[
    { value: 'viewer', label: 'Viewer' },
    { value: 'editor', label: 'Editor' },
    {
      value: 'admin',
      label: 'Admin',
      access: {
        resource: 'user.role.admin',
        action: 'assign',
        onUnauthorized: 'disable'
      }
    }
  ]}
/>

Combined with visibleWhen

<RadioGroup
  name="businessType"
  label="Business Type"
  visibleWhen={(e) => 
    e.getValue('accountType') === 'business'
  }
  access={{
    resource: 'account.businessType',
    action: 'edit',
    onUnauthorized: 'readonly'
  }}
  options={[
    { value: 'llc', label: 'LLC' },
    { value: 'corp', label: 'Corporation' }
  ]}
/>

Note: When combining visibleWhen with RBAC, both conditions must be satisfied. RadioGroup becomes disabled when readonly (no native readonly support). If a hidden option is selected, it remains visible but disabled.


Form Integration

Real-world scenarios showing RadioGroup integrated with DashForm for validation, error handling, and reactive behavior.

RadioGroup works in real form contexts, not just isolated demos. Try these live scenarios to experience DashForm integration and reactive visibility—both fully implemented and production-ready.

Scenario 1

React Hook Form Integration

Try it: Select options and submit the form

RadioGroup integrates seamlessly with React Hook Form through DashForm. Components self-register, errors display automatically after interaction, and validation follows familiar RHF patterns. Try submitting without selecting options to see validation in action.

Full Example
import { DashForm } from '@dashforge/forms';
import { RadioGroup } from '@dashforge/ui';

function CheckoutForm() {
  const handleSubmit = (data: FormData) => {
    console.log('Submitted:', data);
  };

  return (
    <DashForm
      defaultValues={{ 
        shippingMethod: '', 
        contactPreference: '' 
      }}
      onSubmit={handleSubmit}
      mode="onBlur"
    >
      <RadioGroup
        name="shippingMethod"
        label="Shipping Method"
        options={[
          { value: 'standard', label: 'Standard (5-7 days) - Free' },
          { value: 'express', label: 'Express (2-3 days) - $9.99' },
          { value: 'overnight', label: 'Overnight - $24.99' }
        ]}
        rules={{
          required: 'Please select a shipping method'
        }}
      />
      
      <RadioGroup
        name="contactPreference"
        label="Contact Preference"
        options={[
          { value: 'email', label: 'Email' },
          { value: 'phone', label: 'Phone' }
        ]}
        rules={{
          required: 'Please select a contact method'
        }}
      />
      
      <button type="submit">Complete Order</button>
    </DashForm>
  );
}

// RadioGroup automatically:
// - Registers with React Hook Form
// - Syncs selected value from form state
// - Displays validation errors when touched
// - Tracks touched state on blur

Why This Matters

Gradual adoption: Drop RadioGroup into existing form architectures without rewriting validation logic or state management. Perfect for checkout flows and preference selection.

Scenario 2

Reactive Conditional Visibility

Try it: Select "Business" to see conditional radio group appear

RadioGroup supports conditional rendering through the visibleWhen prop. Fields render based on engine state—components query field values and make rendering decisions. Select "Business" account type to see the business type radio group appear instantly without manual state orchestration. This is part of Dashforge Reactive V2 architecture.

Full Example
import { DashForm } from '@dashforge/forms';
import { RadioGroup, Select } from '@dashforge/ui';

function AccountSetupForm() {
  return (
    <DashForm 
      defaultValues={{ 
        accountType: '', 
        businessType: '',
        contactMethod: ''
      }}
    >
      <Select
        name="accountType"
        label="Account Type"
        options={[
          { value: 'personal', label: 'Personal' },
          { value: 'business', label: 'Business' }
        ]}
      />

      <RadioGroup
        name="contactMethod"
        label="Preferred Contact Method"
        options={[
          { value: 'email', label: 'Email' },
          { value: 'phone', label: 'Phone' },
          { value: 'mail', label: 'Mail' }
        ]}
      />

      {/* Business type: renders only for business accounts */}
      <RadioGroup
        name="businessType"
        label="Business Type"
        options={[
          { value: 'sole-proprietor', label: 'Sole Proprietor' },
          { value: 'llc', label: 'LLC' },
          { value: 'corporation', label: 'Corporation' }
        ]}
        visibleWhen={(engine) => {
          const accountType = engine.getNode('accountType')?.value;
          return accountType === 'business';
        }}
        rules={{
          required: 'Please select a business type'
        }}
      />
    </DashForm>
  );
}

// The Engine API provides:
// - getNode(name): Access any field's state
// - Component re-renders on dependency changes
// - Component makes rendering decision (engine provides state)

Why This Matters

Build adaptive forms where radio group visibility responds to user input. The component handles conditional rendering—you define the predicate. Perfect for multi-step flows that branch based on user choices.


API Reference

Complete RadioGroup props and type definitions

Explicit vs Auto-Bound Props: When inside DashForm, RadioGroup receives value, error, helperText, onChange, and onBlur automatically through field binding. Explicit props always take precedence over form-provided values.

PropTypeDefaultDescription
namestring-Field name for form integration (required)
optionsRadioGroupOption[]-Array of radio options. Each option has: { value: string, label: React.ReactNode, disabled?: boolean }. Required prop that defines available choices.
labelReact.ReactNode-Label text displayed above the radio group. When provided, wraps the group in FormLabel.
valuestring-Controlled selected value of the radio group
onChange(event, value) => void-Callback fired when the selected value changes. Receives both the event and the new value.
rowbooleanfalseIf true, displays radio buttons horizontally. Default is vertical layout.
errorbooleanfalseIf true, displays error state. Explicit error prop overrides form-provided error state. When inside DashForm without explicit prop, error is gated (shows only when touched OR submitted).
helperTextstring-Helper text displayed below radio group. Explicit helperText prop overrides form-provided validation error message. When inside DashForm, validation errors display as helperText (gated by touched/submitted state).
rulesValidationRules-Validation rules for DashForm integration. Format follows React Hook Form rules contract. Only used when inside DashForm—ignored in standalone mode. Common rule: { required: "message" } for mandatory selection.
visibleWhen(engine: Engine) => boolean-Component-level conditional rendering predicate. Receives engine instance with access to all field state via getNode(name). When false, component renders null. Re-evaluates on dependency changes. Only works inside DashForm (requires engine).

Under the hood

How RadioGroup works internally

Form integration

Automatically binds to form state inside DashForm. No Controller, no manual wiring. Works as a standard MUI RadioGroup when used standalone.

Behavior model

Options array prop for cleaner, declarative code. Errors appear only after blur or submit. Enforces single-choice selection semantics—only one option can be selected at a time.

Architecture

Built on MUI RadioGroup with FormControlLabel wrappers. Fully typed with TypeScript. Purpose-built for account types, shipping methods, plan tiers, and any mutually exclusive choice.


On This Page