Radio Group
beui/radioFreeComponent
Single-select choice control with a gliding layoutId indicator dot and spring press feedback.
Install it
npx shadcn@latest add https://beui.dev/r/radio.jsonSource
1"use client";2// beui.dev/components/motion/radio34import { motion, MotionConfig, useReducedMotion } from "motion/react";5import {6 createContext,7 useCallback,8 useContext,9 useId,10 useMemo,11 useState,12 type ReactNode,13} from "react";14import { SPRING_LAYOUT, SPRING_PRESS } from "@/lib/ease";15import { cn } from "@/lib/utils";1617type RadioCtx = {18 value: string;19 setValue: (value: string) => void;20 layoutId: string;21};2223const RadioCtx = createContext<RadioCtx | null>(null);2425function useRadioGroup() {26 const ctx = useContext(RadioCtx);27 if (!ctx) {28 throw new Error("RadioGroupItem must be used inside <RadioGroup>");29 }30 return ctx;31}3233export interface RadioGroupProps {34 value?: string;35 defaultValue?: string;36 onValueChange?: (value: string) => void;37 children: ReactNode;38 className?: string;39 orientation?: "vertical" | "horizontal";40}4142export function RadioGroup({43 value,44 defaultValue = "",45 onValueChange,46 children,47 className,48 orientation = "vertical",49}: RadioGroupProps) {50 const [internal, setInternal] = useState(defaultValue);51 const layoutId = useId();52 const reduce = useReducedMotion();53 const controlled = value !== undefined;54 const current = controlled ? value : internal;55 const setValue = useCallback(56 (next: string) => {57 if (!controlled) setInternal(next);58 onValueChange?.(next);59 },60 [controlled, onValueChange],61 );62 const contextValue = useMemo(63 () => ({ value: current, setValue, layoutId }),64 [current, layoutId, setValue],65 );6667 return (68 <MotionConfig transition={reduce ? { duration: 0 } : SPRING_LAYOUT}>69 <RadioCtx.Provider value={contextValue}>70 <div71 role="radiogroup"72 className={cn(73 "flex gap-3",74 orientation === "vertical" ? "flex-col" : "flex-row flex-wrap",75 className,76 )}77 >78 {children}79 </div>80 </RadioCtx.Provider>81 </MotionConfig>82 );83}8485export interface RadioGroupItemProps {86 value: string;87 label?: string;88 disabled?: boolean;89 className?: string;90 id?: string;91}9293export function RadioGroupItem({94 value,95 label,96 disabled,97 className,98 id: idProp,99}: RadioGroupItemProps) {100 const { value: groupValue, setValue, layoutId } = useRadioGroup();101 const autoId = useId();102 const id = idProp ?? autoId;103 const reduce = useReducedMotion();104 const selected = groupValue === value;105106 return (107 <label108 htmlFor={id}109 className={cn(110// … truncated
What it pulls in
npm packages
Files it writes
More from beui
All 104 items| Component | Registry | Kind | Access | Installs | Command |
|---|---|---|---|---|---|
| Action Swap Bluraction-swap-blur | beui | Components | Free | 3 deps · 4 files | |
| Action Swap Cascadeaction-swap-cascade | beui | Components | Free | 3 deps · 4 files | |
| Action Swap Rollaction-swap-roll | beui | Components | Free | 3 deps · 4 files | |
| Agent Activityagent-activity | beui | Components | Free | 4 deps · 9 files | |
| Agent Loading States Agent Progressagent-progress | beui | Components | Free | 3 deps · 3 files | |
| AI Sidebarai-sidebar | beui | Components | Free | 4 deps · 5 files | |
| Animated Badgeanimated-badge | beui | Components | Free | 4 deps · 3 files | |
| Number Animation Animated Numberanimated-number | beui | Components | Free | 3 deps · 3 files |
