# Toggle

A button that stays pressed or unpressed, for turning a view option on and off.

```tsx
import { PinIcon } from "lucide-react"
import { Toggle } from "@zenoxai/ui/components/toggle"

function ToggleDemo() {
  return (
    <Toggle aria-label="Pin alert">
      <PinIcon />
    </Toggle>
  )
}
```

## Usage

```tsx
import { PinIcon } from "lucide-react"

import { Toggle } from "@zenoxai/ui/components/toggle"

export function PinToggle() {
  return (
    <Toggle aria-label="Pin alert">
      <PinIcon />
    </Toggle>
  )
}
```

Use a toggle for an option that changes how the current view looks or behaves: wrap long
lines, hide resolved alerts, pin an item. It reads as a button with a pressed state, so it
belongs in toolbars and table headers.

Use something else when:

- The option is a setting that persists, such as enabling notifications: use a
  [Switch](/components/switch).
- The user picks one or more options from a set: use a [Toggle Group](/components/toggle-group).
- Clicking runs an action instead of changing a state: use a [Button](/components/button).

## Examples

### Outline

`outline` adds a border, so the toggle stands out next to text or on a busy surface. The
`default` variant has no border and suits icon toolbars. A third variant, `segmented`, is meant
for items inside a [Toggle Group](/components/toggle-group).

```tsx
import { EyeOffIcon, WrapTextIcon } from "lucide-react"
import { Toggle } from "@zenoxai/ui/components/toggle"

function ToggleOutline() {
  return (
    <>
      <Toggle variant="outline" aria-label="Wrap long lines" defaultPressed>
        <WrapTextIcon />
      </Toggle>
      <Toggle variant="outline">
        <EyeOffIcon data-icon="inline-start" />
        Hide resolved
      </Toggle>
    </>
  )
}
```

### Sizes

`sm` (28px), `default` (32px) and `lg` (36px), matching the [Button](/components/button) heights.
Mark an icon next to text with `data-icon="inline-start"` or `data-icon="inline-end"` to
tighten the padding on that side.

```tsx
import { PinIcon } from "lucide-react"
import { Toggle } from "@zenoxai/ui/components/toggle"

function ToggleSizes() {
  return (
    <>
      <Toggle size="sm" variant="outline" aria-label="Pin alert">
        <PinIcon />
      </Toggle>
      <Toggle variant="outline" aria-label="Pin alert">
        <PinIcon />
      </Toggle>
      <Toggle size="lg" variant="outline" aria-label="Pin alert">
        <PinIcon />
      </Toggle>
    </>
  )
}
```

### Controlled

Pass `pressed` and `onPressedChange` when something else on the page depends on the state.
Use `defaultPressed` for an uncontrolled toggle that starts pressed.

```tsx
import * as React from "react"
import { RadioIcon } from "lucide-react"
import { Toggle } from "@zenoxai/ui/components/toggle"

function ToggleControlled() {
  const [live, setLive] = React.useState(false)

  return (
    <div className="flex items-center gap-3">
      <Toggle variant="outline" pressed={live} onPressedChange={setLive}>
        <RadioIcon data-icon="inline-start" />
        Live updates
      </Toggle>
      <span className="text-sm text-text-secondary">
        {live
          ? "New alerts stream in as they arrive."
          : "Refresh to see new alerts."}
      </span>
    </div>
  )
}
```

### Disabled

A disabled toggle still shows whether it is pressed, so the user can read the state while the
option is unavailable.

```tsx
import { EyeOffIcon, PinIcon } from "lucide-react"
import { Toggle } from "@zenoxai/ui/components/toggle"

function ToggleDisabled() {
  return (
    <>
      <Toggle variant="outline" disabled>
        <EyeOffIcon data-icon="inline-start" />
        Hide resolved
      </Toggle>
      <Toggle variant="outline" disabled defaultPressed aria-label="Pin alert">
        <PinIcon />
      </Toggle>
    </>
  )
}
```

## Accessibility

- The toggle is a native `<button>` with `aria-pressed`, so screen readers announce it as a
  toggle button and its state. Space and Enter change it.
- Keep the label the same in both states ("Live updates", not "Start" and "Stop"): the pressed
  state already tells the user whether it is on.
- An icon-only toggle needs an `aria-label`.

## API reference

`import { ... } from "@zenoxai/ui/components/toggle"`

### Toggle

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| defaultPressed | `boolean` | `false` | Whether the toggle button is currently pressed. This is the uncontrolled counterpart of `pressed`. |
| disabled | `boolean` | `false` | Whether the component should ignore user interaction. |
| nativeButton | `boolean` | `true` | Whether the component renders a native `<button>` element when replacing it via the `render` prop. Set to `false` if the rendered element is not a button (for example, `<div>`). |
| onPressedChange | `((pressed: boolean, eventDetails: { reason: "none"; event: Event; cancel: () => void; allowPropagation: () => void; isCanceled: boolean; isPropagationAllowed: boolean; trigger: Element; }) => void)` |  | Callback fired when the pressed state is changed. |
| pressed | `boolean` |  | Whether the toggle button is currently pressed. This is the controlled counterpart of `defaultPressed`. |
| render | `ReactElement<unknown, string \| JSXElementConstructor<any>> \| ComponentRenderFn<HTMLProps, ToggleState>` |  | Allows you to replace the component's HTML element with a different tag, or compose it with another component.  Accepts a `ReactElement` or a function that returns the element to render. |
| size | `"default" \| "sm" \| "lg" \| null` | `default` | Height: `sm` 28px, `default` 32px, `lg` 36px. |
| value | `string` |  | A unique string that identifies the toggle when used inside a toggle group. |
| variant | `"default" \| "outline" \| "segmented" \| null` | `default` | `default` is borderless; `outline` adds the border of an outline Button; `segmented` is an item of a segmented ToggleGroup. Every variant marks the pressed state the same way, with the selection colors (primary-soft fill and text) that FilterChip and the sidebar use, so pressed never looks like hover. |

