UI/UI
Toggle
A two-state button that can be either on or off with smooth transitions and multiple variants.
<div className="flex gap-4 flex-wrap">
<Toggle>Default</Toggle>
<Toggle variant="outline">Outline</Toggle>
<Toggle variant="ghost">Ghost</Toggle>
<Toggle variant="secondary">Secondary</Toggle>
</div>
Installation
Install following dependencies:
npm install @radix-ui/react-toggle class-variance-authority
pnpm add @radix-ui/react-toggle class-variance-authority
yarn add @radix-ui/react-toggle class-variance-authority
bun add @radix-ui/react-toggle class-variance-authority
Copy and paste the following code into your project.
"use client";
import * as React from "react";
import * as TogglePrimitive from "@radix-ui/react-toggle";
import { cva, type VariantProps } from "class-variance-authority";
import { cn } from "@/lib/utils";
const toggleVariants = cva(
"inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-[var(--radius)] text-sm font-medium transition-all focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0",
{
variants: {
variant: {
default:
"border border-[hsl(var(--hu-border))] text-[hsl(var(--hu-foreground))] hover:bg-[hsl(var(--hu-accent))] hover:text-[hsl(var(--hu-accent-foreground))] data-[state=on]:bg-[hsl(var(--hu-primary))] data-[state=on]:text-[hsl(var(--hu-primary-foreground))] focus-visible:ring-[hsl(var(--hu-ring))]",
outline:
"border border-[hsl(var(--hu-border))] text-[hsl(var(--hu-foreground))] hover:bg-[hsl(var(--hu-accent))] hover:text-[hsl(var(--hu-accent-foreground))] data-[state=on]:bg-[hsl(var(--hu-accent))] data-[state=on]:text-[hsl(var(--hu-accent-foreground))] data-[state=on]:border-[hsl(var(--hu-primary))] focus-visible:ring-[hsl(var(--hu-ring))]",
ghost:
"text-[hsl(var(--hu-foreground))] hover:bg-[hsl(var(--hu-accent))] hover:text-[hsl(var(--hu-accent-foreground))] data-[state=on]:bg-[hsl(var(--hu-accent))] data-[state=on]:text-[hsl(var(--hu-accent-foreground))] focus-visible:ring-[hsl(var(--hu-ring))]",
secondary:
"bg-[hsl(var(--hu-secondary))] text-[hsl(var(--hu-secondary-foreground))] hover:bg-[hsl(var(--hu-secondary))]/80 data-[state=on]:bg-[hsl(var(--hu-primary))] data-[state=on]:text-[hsl(var(--hu-primary-foreground))] focus-visible:ring-[hsl(var(--hu-ring))]",
},
size: {
default: "h-9 px-4 py-2",
sm: "h-8 px-3 text-xs",
lg: "h-10 px-8",
xl: "h-12 px-10 text-base",
icon: "h-9 w-9",
},
},
defaultVariants: {
variant: "default",
size: "default",
},
},
);
export interface ToggleProps
extends React.ComponentPropsWithoutRef<typeof TogglePrimitive.Root>,
VariantProps<typeof toggleVariants> {
leftIcon?: React.ReactNode;
rightIcon?: React.ReactNode;
}
const Toggle = React.forwardRef<
React.ElementRef<typeof TogglePrimitive.Root>,
ToggleProps
>(
(
{ className, variant, size, leftIcon, rightIcon, children, ...props },
ref,
) => (
<TogglePrimitive.Root
ref={ref}
className={cn(toggleVariants({ variant, size, className }))}
{...props}
>
{leftIcon && leftIcon}
{children}
{rightIcon && rightIcon}
</TogglePrimitive.Root>
),
);
Toggle.displayName = TogglePrimitive.Root.displayName;
export { Toggle, toggleVariants };
npx hextaui@latest add toggle
pnpm dlx hextaui@latest add toggle
yarn dlx hextaui@latest add toggle
bun x hextaui@latest add toggle
Usage
import { Toggle } from "@/components/ui/Toggle";
<Toggle>Toggle me</Toggle>
Examples
Default
<Toggle>Toggle me</Toggle>
Variants
<Toggle variant="default">Default</Toggle>
<Toggle variant="outline">Outline</Toggle>
<Toggle variant="ghost">Ghost</Toggle>
<Toggle variant="secondary">Secondary</Toggle>
Sizes
<Toggle size="sm">Small</Toggle>
<Toggle size="default">Default</Toggle>
<Toggle size="lg">Large</Toggle>
<Toggle size="xl">Extra Large</Toggle>
With Icons
<Toggle leftIcon={<Bold />}>Bold</Toggle>
<Toggle rightIcon={<Italic />}>Italic</Toggle>
<Toggle variant="outline" leftIcon={<Underline />}>
Underline
</Toggle>
<Toggle variant="secondary" rightIcon={<Heart />}>
Like
</Toggle>
Icon Only
<Toggle variant="outline" size="icon">
<AlignLeft />
</Toggle>
<Toggle variant="ghost" size="icon">
<AlignCenter />
</Toggle>
<Toggle variant="secondary" size="icon">
<AlignRight />
</Toggle>
Text Formatting
Text Formatting
Toggle buttons for text formatting options
Text Alignment
Toggle buttons for text alignment with icons only
// Text Formatting
<div className="flex gap-2">
<Toggle leftIcon={<Bold />} aria-label="Toggle bold">
Bold
</Toggle>
<Toggle leftIcon={<Italic />} aria-label="Toggle italic">
Italic
</Toggle>
<Toggle leftIcon={<Underline />} aria-label="Toggle underline">
Underline
</Toggle>
</div>
// Text Alignment
<div className="flex gap-2">
<Toggle variant="outline" size="icon" aria-label="Align left">
<AlignLeft />
</Toggle>
<Toggle variant="outline" size="icon" aria-label="Align center">
<AlignCenter />
</Toggle>
<Toggle variant="outline" size="icon" aria-label="Align right">
<AlignRight />
</Toggle>
</div>
Interactive Toggles
Interactive Toggles
Toggle buttons with state management and different variants
const [isLiked, setIsLiked] = React.useState(false);
const [isStarred, setIsStarred] = React.useState(false);
<Toggle
variant="outline"
pressed={isLiked}
onPressedChange={setIsLiked}
leftIcon={<Heart className={isLiked ? "fill-current" : ""} />}
aria-label="Toggle like"
>
{isLiked ? "Liked" : "Like"}
</Toggle>
<Toggle
variant="ghost"
pressed={isStarred}
onPressedChange={setIsStarred}
leftIcon={<Star className={isStarred ? "fill-current" : ""} />}
aria-label="Toggle star"
>
{isStarred ? "Starred" : "Star"}
</Toggle>
Controlled State
<Toggle pressed={false}>Always Off</Toggle>
<Toggle defaultPressed={true}>Default On</Toggle>
Toggle Groups
Toggles can be grouped together to create more complex interactive components like toolbars and formatting controls.
Rich Text Toolbar
Text Formatting Group
Multiple toggles can be active at once
Active formats: none
Text Alignment Group
Only one alignment can be active at a time
Current alignment: left
List Type Group
Toggle between different list types or none
Current list type: none
Rich Text Toolbar
Complete text editor toolbar with grouped toggles
const [formatting, setFormatting] = React.useState<string[]>([]);
const [alignment, setAlignment] = React.useState("left");
const [listType, setListType] = React.useState("");
// Text Formatting Group (Multiple Selection)
<div className="flex gap-1 border rounded-lg p-1">
<Toggle
size="sm"
variant="ghost"
pressed={formatting.includes("bold")}
onPressedChange={(pressed) => {
setFormatting(prev =>
pressed
? [...prev, "bold"]
: prev.filter(f => f !== "bold")
);
}}
>
<Bold className="h-4 w-4" />
</Toggle>
<Toggle
size="sm"
variant="ghost"
pressed={formatting.includes("italic")}
onPressedChange={(pressed) => {
setFormatting(prev =>
pressed
? [...prev, "italic"]
: prev.filter(f => f !== "italic")
);
}}
>
<Italic className="h-4 w-4" />
</Toggle>
<Toggle
size="sm"
variant="ghost"
pressed={formatting.includes("underline")}
onPressedChange={(pressed) => {
setFormatting(prev =>
pressed
? [...prev, "underline"]
: prev.filter(f => f !== "underline")
);
}}
>
<Underline className="h-4 w-4" />
</Toggle>
</div>
// Text Alignment Group (Single Selection)
<div className="flex gap-1 border rounded-lg p-1">
<Toggle
size="sm"
variant="ghost"
pressed={alignment === "left"}
onPressedChange={() => setAlignment("left")}
>
<AlignLeft className="h-4 w-4" />
</Toggle>
<Toggle
size="sm"
variant="ghost"
pressed={alignment === "center"}
onPressedChange={() => setAlignment("center")}
>
<AlignCenter className="h-4 w-4" />
</Toggle>
<Toggle
size="sm"
variant="ghost"
pressed={alignment === "right"}
onPressedChange={() => setAlignment("right")}
>
<AlignRight className="h-4 w-4" />
</Toggle>
</div>
// List Type Group (Toggle Off)
<div className="flex gap-1 border rounded-lg p-1">
<Toggle
size="sm"
variant="ghost"
pressed={listType === "bulleted"}
onPressedChange={(pressed) => {
setListType(pressed ? "bulleted" : "");
}}
>
<List className="h-4 w-4" />
</Toggle>
<Toggle
size="sm"
variant="ghost"
pressed={listType === "numbered"}
onPressedChange={(pressed) => {
setListType(pressed ? "numbered" : "");
}}
>
<ListOrdered className="h-4 w-4" />
</Toggle>
</div>
Props
Prop | Type | Default |
---|---|---|
className? | string | undefined |
disabled? | boolean | false |
rightIcon? | React.ReactNode | undefined |
leftIcon? | React.ReactNode | undefined |
onPressedChange? | (pressed: boolean) => void | undefined |
defaultPressed? | boolean | false |
pressed? | boolean | undefined |
size? | "sm" | "default" | "lg" | "xl" | "icon" | "default" |
variant? | "default" | "outline" | "ghost" | "secondary" | "default" |
Edit on GitHub
Last updated on