Forms
Radio Group.
A set of options where exactly one can be picked, navigable with the arrow keys.
import { Label } from "@/components/ui/label";
import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group";
export default function RadioGroupDemo() {
return (
<RadioGroup defaultValue="daily" aria-label="Backup schedule">
<div className="flex items-center gap-3">
<RadioGroupItem value="hourly" id="rg-hourly" />
<Label htmlFor="rg-hourly">Back up hourly</Label>
</div>
<div className="flex items-center gap-3">
<RadioGroupItem value="daily" id="rg-daily" />
<Label htmlFor="rg-daily">Back up daily</Label>
</div>
<div className="flex items-center gap-3">
<RadioGroupItem value="never" id="rg-never" />
<Label htmlFor="rg-never">Never</Label>
</div>
</RadioGroup>
);
}Installation#
With the CLI
$npx lanterncn add radio-groupThis also installs the Lantern theme and any Lantern UI components it depends on. The shadcn CLI works directly too: npx shadcn@latest add httptim/lantern-ui/radio-group. See Installation if your project is not set up yet.
Manually
Install the dependencies
npm install radix-uiCopy the source into your project
components/ui/radio-group.tsx"use client"; import * as React from "react"; import { RadioGroup as RadioGroupPrimitive } from "radix-ui"; import { cn } from "@/lib/utils"; function RadioGroup({ className, ...props }: React.ComponentProps<typeof RadioGroupPrimitive.Root>) { return <RadioGroupPrimitive.Root data-slot="radio-group" className={cn("grid gap-3", className)} {...props} />; } function RadioGroupItem({ className, ...props }: React.ComponentProps<typeof RadioGroupPrimitive.Item>) { return ( <RadioGroupPrimitive.Item data-slot="radio-group-item" className={cn( "peer relative aspect-square size-[18px] shrink-0 cursor-pointer rounded-full border border-input bg-background/40 transition-[border-color,box-shadow] outline-none", "after:absolute after:-inset-2 hover:border-muted-foreground", "focus-visible:border-primary focus-visible:ring-2 focus-visible:ring-ring/25 data-[state=checked]:border-primary", "aria-invalid:border-destructive aria-invalid:ring-destructive/20 disabled:cursor-not-allowed disabled:opacity-50", className, )} {...props} > <RadioGroupPrimitive.Indicator data-slot="radio-group-indicator" className="flex items-center justify-center"> <span className="size-2 rounded-full bg-primary" /> </RadioGroupPrimitive.Indicator> </RadioGroupPrimitive.Item> ); } export { RadioGroup, RadioGroupItem };Update the import paths to match your project
The source imports
cnfrom@/lib/utils.
Usage#
import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group";<RadioGroup defaultValue="daily">
<div className="flex items-center gap-3">
<RadioGroupItem value="daily" id="daily" />
<Label htmlFor="daily">Back up daily</Label>
</div>
</RadioGroup>For choice cards, wrap each item in a Label and style the checked card with has-data-[state=checked]. The Field component does this for you with FieldLabel.
Examples#
Choice cards
import { CloudIcon, MonitorIcon } from "lucide-react";
import { Label } from "@/components/ui/label";
import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group";
const modes = [
{
value: "in-game",
title: "In-game host",
description: "Your computer serves the site. It goes offline when the chunk unloads.",
icon: MonitorIcon,
},
{
value: "hub",
title: "Lantern Hub",
description: "We keep a copy online around the clock and sync it when your computer boots.",
icon: CloudIcon,
},
];
export default function RadioGroupCards() {
return (
<RadioGroup defaultValue="hub" aria-label="Hosting mode" className="w-full max-w-md">
{modes.map((mode) => (
<Label
key={mode.value}
htmlFor={`host-${mode.value}`}
className="cursor-pointer items-start gap-3 rounded-md border border-input p-4 transition-[border-color,background-color,box-shadow] hover:border-muted-foreground/60 has-data-[state=checked]:border-primary has-data-[state=checked]:bg-primary/8 has-data-[state=checked]:shadow-[4px_5px_0_var(--block-shadow)]"
>
<mode.icon className="mt-px size-4 shrink-0 text-muted-foreground" aria-hidden="true" />
<span className="grid flex-1 gap-1.5">
<span className="text-sm font-medium">{mode.title}</span>
<span className="text-[13px] leading-normal font-normal text-muted-foreground">{mode.description}</span>
</span>
<RadioGroupItem value={mode.value} id={`host-${mode.value}`} />
</Label>
))}
</RadioGroup>
);
}