Skip to content

useToggle

What this solves

Boolean UI state is common:

  • Open/close modals and drawers
  • Expand/collapse panels
  • Show/hide help overlays

Ad-hoc useState<boolean> is fine, but small inconsistencies add up:

  • Different naming (open, setOpen, isOpen) across the codebase
  • Repeated tiny handlers (open, close, toggle)

useToggle standardizes these patterns with a minimal API.


When to use it

Use useToggle for simple boolean UI state:

  • Modals/drawers
  • Feature flags in UI (show/hide)
  • Temporary UI toggles (help, filters panel)

When NOT to use it

Do not use useToggle when:

  • The state isn’t truly boolean (tri-state, enums, derived state)
  • The value must be persisted remotely or synchronized globally
  • You need to model transitions with more nuance (e.g., opening/closing animations with intermediate states)

Basic usage

tsx
import { useToggle } from '@/shared/hooks'

export function Example() {
  const { value: isOpen, on: open, off: close, toggle, set } = useToggle(false)

  return (
    <div>
      <button type="button" onClick={open}>Open</button>
      <button type="button" onClick={close}>Close</button>
      <button type="button" onClick={toggle}>Toggle</button>
      <button type="button" onClick={() => set(true)}>Force open</button>

      {isOpen && <div role="dialog">Dialog content</div>}
    </div>
  )
}

Common patterns

Pattern 1 — Modal open/close wiring

tsx
import { useToggle } from '@/shared/hooks'

export function Page() {
  const modal = useToggle(false)

  return (
    <>
      <button type="button" onClick={modal.on}>Open modal</button>

      {modal.value && (
        <div role="dialog" aria-modal="true">
          <button type="button" onClick={modal.off}>Close</button>
        </div>
      )}
    </>
  )
}

Why this is recommended:

  • Consistent naming (on/off/toggle) reduces cognitive overhead

Pattern 2 — Drive conditional UI and reset predictably

tsx
import { useToggle } from '@/shared/hooks'

export function FiltersPanel() {
  const filters = useToggle(false)

  const clearAndClose = () => {
    // clear filters here
    filters.off()
  }

  return (
    <div>
      <button type="button" onClick={filters.toggle}>
        {filters.value ? 'Hide filters' : 'Show filters'}
      </button>

      {filters.value && (
        <div>
          <button type="button" onClick={clearAndClose}>Clear</button>
        </div>
      )}
    </div>
  )
}

Things to avoid

  • Using useToggle for state that should be derived (compute it instead)
  • Duplicating the same boolean in multiple components (lift state up instead)
  • Over-abstracting: if you only need const [open, setOpen] = useState(false) once, keep it simple


API reference (concise)

ts
export interface UseToggleReturn {
  value: boolean
  on: () => void
  off: () => void
  toggle: () => void
  set: (value: boolean) => void
}

export function useToggle(initialValue?: boolean): UseToggleReturn

Final notes

  • Prefer naming const { value: isOpen } = useToggle(...) for readability.
  • Don’t use useToggle to hide domain state changes; keep it as a UI primitive.