Radio

Native radio input resolved through the shared styling contract. Group value is owned by createRadioGroupController in the DOM engine; React / Vue / CE expose radio items that you wire with a shared name and parent-controlled checked (or native HTML grouping). There is no React/Vue RadioGroup component in this beta.

Preview
Plan

Usage

tsx
import { useState } from "react";
import { Radio } from "@sometic/react/selection";

export function Example() {
    const [value, setValue] = useState("pro");
    return (
        <div role="radiogroup" aria-label="Plan">
            <Radio
                name="plan"
                value="free"
                checked={value === "free"}
                onValueChange={setValue}
                aria-label="Free"
            />
            <Radio
                name="plan"
                value="pro"
                checked={value === "pro"}
                onValueChange={setValue}
                aria-label="Pro"
            />
        </div>
    );
}
tsx
import { useState } from "react";
import { Radio } from "@sometic/react/selection";

export function Example(): JSX.Element {
    const [value, setValue] = useState("pro");
    return (
        <div role="radiogroup" aria-label="Plan">
            <Radio
                name="plan"
                value="free"
                checked={value === "free"}
                onValueChange={setValue}
                aria-label="Free"
            />
            <Radio
                name="plan"
                value="pro"
                checked={value === "pro"}
                onValueChange={setValue}
                aria-label="Pro"
            />
        </div>
    );
}
html
<script type="module">
    import { registerSelectionElements } from "@sometic/elements/selection";
    registerSelectionElements();
</script>

<div role="radiogroup" aria-label="Plan">
    <sometic-radio name="plan" value="free" aria-label="Free"></sometic-radio>
    <sometic-radio name="plan" value="pro" checked aria-label="Pro"></sometic-radio>
</div>

Vue

vue
<script setup>
import { ref } from "vue";
import { Radio } from "@sometic/vue/selection";

const value = ref("pro");
</script>

<template>
    <div role="radiogroup" aria-label="Plan">
        <Radio
            name="plan"
            value="free"
            :checked="value === 'free'"
            aria-label="Free"
            @value-change="value = $event"
        />
        <Radio
            name="plan"
            value="pro"
            :checked="value === 'pro'"
            aria-label="Pro"
            @value-change="value = $event"
        />
    </div>
</template>

Beta note: @sometic/react/selection and @sometic/vue/selection export Radio (with Checkbox / Switch / Select). Compose group state yourself, or use createRadioGroupController from @sometic/dom/radio for vanilla.

How it works

  1. Engine: resolveRadio requires value: string and builds native radio attrs. createRadioGroupController holds group string | null and resolveItem.
  2. Adapters: React/Vue Radio is a single item: required value, optional checked, onValueChange(value) / valueChange when selected.
  3. Custom element: sometic-radio observes checked, disabled, name, value, shadow.

Anatomy

Partdata-slotRole
Rootroot<input type="radio">

State: data-checked, data-disabled, data-invalid, optional size/variant.

Props / attributes

React RadioProps

PropTypeDefaultDescription
valuestringrequiredItem value
checkedbooleanfalseSelected
onValueChange(value: string) => voidFires with this item's value when chosen
disabled / required / invalidbooleanfalseFlags
namestringShared group name
size / variantstringTheme
stylingStyleableProps<"root">Unstyled
Native attrsremaining input attrsForwarded; ref OK

Vue

Props: value (required), checked, disabled, name. Emits: valueChange.

DOM group controller

createRadioGroupController({ value?, defaultValue?, name?, onValueChange? })resolveItem(itemValue), setValue.

Custom element (sometic-radio)

Observed: checked, disabled, name, value, shadow.

Events / callbacks

SurfaceEventPayload
ReactonValueChangeitem value string
VuevalueChangeitem value string
CEchange-style checked events
Group controlleronValueChangestring | null

Controlled vs uncontrolled

Items are typically controlled by parent group state. Native HTML grouping via shared name also works uncontrolled without React/Vue state (browser manages exclusivity).

Form participation

Native radios with shared name submit the selected value. Works with Form / sometic-form.

Accessibility

ConcernGuidance
Group nameWrap with role="radiogroup" + aria-label / aria-labelledby
Item labelsVisible labels per item or aria-label
KeyboardNative arrow-key group behavior when name is shared
Not listboxThis is radio, not Combobox / Menu
RequiredPrefer form-level validation that the group has a value

Styling

[data-checked], [data-disabled], [data-invalid], [data-slot="root"], optional size/variant.

Edge cases

  • Missing shared name: items will not native-group; manage checked yourself.
  • null group value: no item checked (controller path).
  • SSR: resolve is pure; register CE in the browser.
  • Inventing RadioGroup: do not assume a React RadioGroup export; compose state.
  • Select vs Radio: Select for longer / compact lists; Radio for visible concurrent options.
  • Multi-instance: independent items; exclusivity comes from name or parent state.

Performance notes

Per-item resolve is cheap. Prefer one parent state update over remounting the group. Import @sometic/react/selection subpaths for tree-shaking.

When to use / When not

Use for mutually exclusive choices in a short list.

Do not use for:

FAQ

Where is React RadioGroup? Not shipped. Compose with state + Radio items or DOM createRadioGroupController.

Why onValueChange passes the item value? So parents can set group state without reading the DOM.

Required on one item? Prefer form-level validation that the group has a value.

Select vs Radio? Select for longer option lists / compact UI; Radio for visible concurrent options.

CE grouping? Shared name attribute across sometic-radio nodes.

Does React forward refs? Yes, to the underlying <input>.

Arrow keys without shared name? Native grouping needs shared name; otherwise manage focus yourself.

Invalid? Sets aria-invalid / data-invalid; pair with Field for messages.

Bundle tip? Import @sometic/react/selection (or Vue / elements / dom matching subpaths).