COMPONENTS/INPUTS

Radio group.

One choice from a considered set.

In practice

Provide a name and aria-label on RadioGroup. Give each RadioGroupItem a unique value and visible label.

Installation

Install Coal once. Components and TypeScript declarations are included. React 19 and React DOM 19 are peer dependencies.

Terminal · local package
npm install http://localhost:3100/downloads/chlohal-coal-ui-0.5.0.tgz

This installs the local package. It is not yet published on npm. See installation for stylesheet setup and integration.

Usage

example.tsx
"use client";
import * as React from "react";
import "@chlohal/coal-ui/styles.css";
import { RadioGroup, RadioGroupItem } from "@chlohal/coal-ui";
export default function Example() {
    return (<RadioGroup defaultValue="personal" aria-label="Workspace plan">
          {["personal", "studio", "team"].map((s) => (<label key={s}>
              <RadioGroupItem value={s}/>
              {s}
            </label>))}
        </RadioGroup>);
}

Source

The original Coal implementation. Use the package export to integrate it.

packages/react/src/radio-group.tsx
"use client";
import * as React from "react";
import { cn, useValue, useRequired } from "./internal.js";
const Context = React.createContext<
  | {
      value: string;
      set: (value: string) => void;
      name: string;
      disabled?: boolean;
      required?: boolean;
    }
  | undefined
>(undefined);
export type RadioGroupProps = Omit<
  React.ComponentPropsWithRef<"div">,
  "defaultValue" | "onChange"
> & {
  value?: string;
  defaultValue?: string;
  onValueChange?: (value: string) => void;
  name?: string;
  disabled?: boolean;
  required?: boolean;
};
export function RadioGroup({
  value,
  defaultValue = "",
  onValueChange,
  name,
  disabled,
  required,
  children,
  className,
  ...props
}: RadioGroupProps) {
  const id = React.useId();
  const [v, set] = useValue(value, defaultValue, onValueChange);
  return (
    <Context.Provider
      value={{ value: v, set, name: name ?? id, disabled, required }}
    >
      <div
        role="radiogroup"
        className={cn("coal-radio-group", className)}
        {...props}
      >
        {children}
      </div>
    </Context.Provider>
  );
}
export function RadioGroupItem({
  value,
  disabled,
  onChange,
  className,
  ...props
}: Omit<React.ComponentPropsWithRef<"input">, "type" | "value"> & {
  value: string;
}) {
  const c = useRequired(Context, "RadioGroupItem");
  return (
    <input
      {...props}
      type="radio"
      className={cn("coal-radio", className)}
      name={c.name}
      value={value}
      required={c.required}
      disabled={disabled || c.disabled}
      checked={c.value === value}
      onChange={(e) => {
        onChange?.(e);
        if (!e.defaultPrevented) c.set(value);
      }}
    />
  );
}

Accessibility checklist

  • Provide an accessible name for controls, including icon-only buttons.
  • Check keyboard navigation, visible focus and disabled states in your application.
  • Do not rely on color alone to communicate status.