Toggle
Pressed-state button and toggle group built on the Button surface.
Composition
The preferred shape for composing the installed primitive from its exported parts.
Parts
Exported compound parts from the installed source.
Toggle └── ToggleGroup
First install and activate one skin. Core deliberately contains no visual token defaults.
The Control UI source installs this primitive from src/registry/sources/control-ui/ui/toggle.tsx. Install it on its own with the command above, or inspect the source below.
npx shadcn@latest add https://control-ui.dev/r/toggle.jsonKnobs
Typed custom properties the recipe paints with. Set one on the root — style, a utility class, or a skin — and every slot inherits it.
--cui-button-* · 17 knobsHow the cascade resolves--cui-button-radius<length-percentage>var(--radius-control)--cui-button-gap<length-percentage>0.375rem--cui-button-icon<length>1rem--cui-button-bg<color>transparent--cui-button-bg-image*none--cui-button-foreground<color>var(--muted-foreground)--cui-button-hover-bg<color>oklch(from var(--foreground) l c h / 0.06)--cui-button-hover-foreground<color>var(--foreground)--cui-button-press-bg<color>var(--cui-button-hover-bg)--cui-button-press-scale<number>0.98--cui-button-active-bg<color>oklch(from var(--foreground) l c h / 0.08)--cui-button-active-foreground<color>var(--foreground)--cui-button-active-hover-bg<color>var(--cui-button-active-bg)--cui-button-shadow*0 0 transparent--cui-button-hover-shadow*var(--cui-button-shadow)--cui-button-press-shadow*var(--cui-button-hover-shadow)--cui-button-active-shadow*var(--cui-button-shadow)Library dependencies
Public registry items keep their source on their own documentation page instead of duplicating it here.
Raw code
This primitive's source and the support files it installs with
"use client";import { Toggle as TogglePrimitive } from "@base-ui/react/toggle";import { ToggleGroup as ToggleGroupPrimitive } from "@base-ui/react/toggle-group";import type { ComponentProps } from "react";import type { ControlledMultiChoice } from "@/components/control-ui/control-props";import { cn } from "@/components/control-ui/lib/cn";import type { ButtonProps } from "@/components/control-ui/ui/button";import { buttonContentClasses, buttonRecipeClasses } from "@/components/control-ui/ui/button";export type ToggleProps = Omit<ButtonProps, "render" | "nativeButton" | "value"> & { pressed?: boolean; defaultPressed?: boolean; onPressedChange?: (pressed: boolean) => void; value?: string; showCheck?: boolean;};export type ToggleGroupProps<TValue extends string = string> = Omit<ComponentProps<"div">, "defaultValue" | "onChange"> & ControlledMultiChoice<TValue> & { multiple?: boolean; disabled?: boolean; orientation?: "horizontal" | "vertical"; };// keeps its own anatomy while sharing Button's visual recipeexport function Toggle({ variant = "surface", size = "sm", tone = "neutral", active, showCheck = false, className, pressed, defaultPressed, onPressedChange, value, disabled, children, ...props}: ToggleProps) { return ( <TogglePrimitive pressed={pressed} defaultPressed={defaultPressed} onPressedChange={onPressedChange} value={value} disabled={disabled} render={(renderProps, state) => { const isActive = active ?? state.pressed; return ( <button type="button" {...renderProps} data-control-ui="toggle" data-control-family="button" data-slot="root" data-control="true" data-active={isActive ? "true" : undefined} data-variant={variant} data-tone={tone} data-size={size} className={cn(buttonRecipeClasses(size), renderProps.className, className)} > <span data-control-ui="toggle" data-control-family="button" data-slot="content" className={buttonContentClasses}> {showCheck ? ( <span data-control-ui="toggle" data-slot="check" className="flex size-3.5 shrink-0 items-center justify-center"> {isActive ? ( <svg viewBox="0 0 12 12" className="size-3" aria-hidden="true" fill="none"> <path d="M2.5 6.5 5 9l4.5-5" stroke="currentColor" strokeWidth="1.4" strokeLinecap="round" strokeLinejoin="round" /> </svg> ) : null} </span> ) : null} {renderProps.children} </span> </button> ); }} {...props} > {children} </TogglePrimitive> );}export function ToggleGroup<TValue extends string = string>({ className, orientation = "horizontal", ...props }: ToggleGroupProps<TValue>) { return ( <ToggleGroupPrimitive data-control-ui="toggle" data-slot="group" data-orientation={orientation} orientation={orientation} className={cn("inline-flex items-center gap-1", orientation === "vertical" && "flex-col", className)} {...props} /> );}