Appearance
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
useTogglefor 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
Related primitives
API reference (concise)
ts
export interface UseToggleReturn {
value: boolean
on: () => void
off: () => void
toggle: () => void
set: (value: boolean) => void
}
export function useToggle(initialValue?: boolean): UseToggleReturnFinal notes
- Prefer naming
const { value: isOpen } = useToggle(...)for readability. - Don’t use
useToggleto hide domain state changes; keep it as a UI primitive.