Skip to content

Toggle

Turn an option on or off with a button that stays pressed.

import { Toggle } from '@youj/react'

Installation & usage

npm i @youj/react

For an app already set up with npx @youj/cli init, which installs the peers (@youj/core, @youj/motion, motion and Base UI) and wires the provider. New app? Start at Installation.

Use a toggle for a view or formatting option that applies at once and is read from the button itself: pressed means on. For a setting in a form or a settings list, use a Switch. For a set of related options, use a Toggle Group, and for a one-off action, a Button.

import { useState } from 'react'
import { Toggle } from '@youj/react'
import { CornerDownLeftIcon } from '@youj/icons'

export function WrapLines() {
  const [wrap, setWrap] = useState(false)
  return (
    <Toggle variant="outline" pressed={wrap} onPressedChange={setWrap}>
      <CornerDownLeftIcon />
      Wrap
    </Toggle>
  )
}

Use pressed and onPressedChange to control it, or defaultPressed to let it hold its own state. Pressed reads as a filled neutral key with full-strength text. The accent stays reserved for actions.

Reference

Propssound, variant, size (3)

Props

Toggle props
PropType
soundboolean

With sound on (YoujProvider’s sound), pressing plays toggleOn and releasing toggleOff. false keeps this one silent.

default true

true
variant'default' | 'outline'

default 'default'

'default'
size'default' | 'sm' | 'lg'

default 'default'

'default'
Other props pass through: extends TogglePrimitive.Props, VariantProps<typeof toggleVariants>.

The state comes from Base UI's Toggle, and these are the props you'll reach for:

PropTypeDescription
pressedbooleanControlled state.
defaultPressedbooleanInitial state when uncontrolled. Default false.
onPressedChange(pressed: boolean, details) => voidCalled when the user toggles it.
disabledbooleanIgnores presses.
valuestringIdentifies it inside a Toggle Group.
MotionPress, release, hover and pressed fill · reduced motion

Motion

Motion tokens
MomentTokens
Pressscales to 0.97 while held
duration.instantease.standard

80ms · cubic-bezier(0.2, 0, 0, 1)

80mscubic-bezier(0.2, 0, 0, 1)
Release, hover and pressed fillscale back, background and text colors
duration.fastease.standard

140ms · cubic-bezier(0.2, 0, 0, 1)

140mscubic-bezier(0.2, 0, 0, 1)

Reduced motion

No press scale. The fill and text colors still change on the fast token, since nothing moves.

Reduced motion

The press doesn't scale. Colors still change, so the pressed state reads the same, and a state icon switches to its second state instantly instead of morphing.

AccessibilityKeyboard, ARIA, announcements

Accessibility

  • It's a <button> with aria-pressed. Space and Enter toggle it.
  • Keep the name constant and let aria-pressed carry the state: a toggle named "Pin project" is announced as pressed or not pressed. Don't swap the label between "Pin" and "Unpin".
  • Icon-only toggles need an aria-label. A Youj icon inside is decorative unless you label it.
  • For a group of related toggles, use a Toggle Group, which gives them one tab stop and arrow-key navigation.