ToggleButton
freeToggleButton composes Button while swapping active and inactive content from one boolean state.
Preview
Dark
import { useState } from 'react';
import ToggleButton from '@/components/composites/ToggleButton';
export default function UsageDemo() {
const [visible, setVisible] = useState(false);
return (
<ToggleButton
active={visible}
inactiveContent={{ children: 'Show details' }}
activeContent={{ children: 'Hide details' }}
type="button"
aria-pressed={visible}
onClick={() => setVisible((value) => !value)}
/>
);
}Installation
Add this component with the NateUI CLI.
npx nateui@latest add ToggleButtonExamples
Icon Content
Preview
Dark
import { useState } from 'react';
import { PiSidebar, PiSidebarSimple } from 'react-icons/pi'
import ToggleButton from '@/components/composites/ToggleButton';
export default function IconContentDemo() {
const [open, setOpen] = useState(false);
return (
<ToggleButton
active={open}
inactiveContent={{ icon: <PiSidebarSimple /> }}
activeContent={{ icon: <PiSidebar /> }}
type="button"
variant="ghost"
shape="circle"
aria-label={open ? 'Close panel' : 'Open panel'}
aria-pressed={open}
onClick={() => setOpen((value) => !value)}
/>
);
}Mixed Content
Preview
Dark
import { useState } from 'react';
import { PiEye, PiEyeSlash } from 'react-icons/pi'
import ToggleButton from '@/components/composites/ToggleButton';
export default function MixedContentDemo() {
const [previewing, setPreviewing] = useState(false);
return (
<ToggleButton
active={previewing}
inactiveContent={{
icon: <PiEye />,
children: 'Show preview',
}}
activeContent={{
icon: <PiEyeSlash />,
children: 'Hide preview',
}}
type="button"
variant="subtle"
aria-pressed={previewing}
onClick={() => setPreviewing((value) => !value)}
/>
);
}Disabled
Preview
Dark
import { useState } from 'react';
import ToggleButton from '@/components/composites/ToggleButton';
import { PiChecks, PiCopy } from 'react-icons/pi'
export default function DisabledDemo() {
const [copy, setCopy] = useState(false);
return (
<ToggleButton
disabled
active={copy}
inactiveContent={{
icon: <PiCopy />,
children: 'Copy',
}}
activeContent={{
icon: <PiChecks/>,
children: 'Copied',
}}
type="button"
variant="subtle"
aria-label={copy ? 'Copied' : 'Copy'}
aria-pressed={copy}
onClick={() => setCopy((value) => !value)}
/>
);
}Disabled Active Style
Preview
Dark
import { useState } from 'react';
import ToggleButton from '@/components/composites/ToggleButton';
import { PiHeart } from 'react-icons/pi'
export default function DisabledDemo() {
const [like, setLike] = useState(true);
return (
<ToggleButton
disabledActiveStyle
active={like}
inactiveContent={{
icon: <PiHeart />,
children: 'Like',
}}
activeContent={{
icon: <PiHeart className="fill-current" />,
children: 'Liked',
}}
type="button"
variant="ghost"
aria-label={like ? 'Liked' : 'Like'}
aria-pressed={like}
onClick={() => setLike((value) => !value)}
/>
);
}API
ToggleButton accepts the content slots below and forwards the remaining Button props and native button props to Button.
| Prop | Description | Type | Default |
|---|---|---|---|
active | Selects activeContent and applies Button's active visual state when true. | boolean | false |
disabledActiveStyle | Keeps the active content while preventing active from being passed to Button. | boolean | false |
activeContent | Content rendered while the toggle is active. | ToggleButtonContent | — |
inactiveContent | Content rendered while the toggle is inactive. | ToggleButtonContent | — |
asElement | Root element or component rendered by Button. | ElementType | 'button' |
block | Expands the control to the full width of its container. | boolean | false |
className | Additional classes or a state-aware class callback. | ButtonClassName | — |
clickFeedback | Enables Button's press-down feedback. | boolean | true |
destructive | Applies Button's destructive intent palette. | boolean | false |
disabled | Prevents interaction and applies the disabled state. | boolean | false |
iconAlignment | Places an icon before or after the active/inactive label. | ButtonIconAlignment | 'start' |
loading | Shows Button's loading spinner and prevents repeat activation. | boolean | false |
onClick | Click handler forwarded to Button. | ButtonClickHandler | — |
ref | Ref forwarded to Button's underlying element. | Ref<HTMLButtonElement> | — |
shape | Border radius treatment. | ButtonShape | 'round' |
size | Button control size. | ControlSize | ConfigProvider default |
variant | Button visual style. | ButtonVariant | 'default' |
...nativeProps | Native props forwarded to the rendered element, such as type, aria-pressed, and aria-label. | NativeButtonProps<E> | — |
Types
type ToggleButtonContent = {
children?: ReactNode;
icon?: string | ReactNode;
};
type ButtonIconAlignment = 'start' | 'end';
type ButtonShape = 'round' | 'circle' | 'none';
type ControlSize = 'sm' | 'md' | 'lg';
type ButtonVariant = 'solid' | 'subtle' | 'default' | 'ghost' | 'link';ToggleButton does not infer an accessibility relationship from active; pass aria-pressed, aria-expanded, and related native props according to what the control operates.