logo

Nurav UI

Community
Hooks

useToggle

A clean, minimal hook for managing boolean state — perfect for modals, accordions, and switches.Copy Markdown

Overview

useToggle is a small but powerful abstraction over useState. It eliminates the boilerplate of writing const [isOpen, setIsOpen] = useState(false) followed by a manual setIsOpen(!isOpen) function.

Commonly used for:

  • Opening and closing Modals or Drawers
  • Expanding and collapsing FAQ Accordions
  • Toggling password visibility (hide/show)
  • Dark mode/Light mode quick toggles

Installation

Automatic (CLI)

Terminal
npx shadcn@latest add @nurav-ui/use-toggle

Alternatively, install via direct URL:

Terminal
npx shadcn@latest add https://nurav-ui.vercel.app/r/use-toggle.json

Manual Setup

Create the hook file

Copy the source into hooks/use-toggle.ts:

hooks/use-toggle.ts
import { useState, useCallback } from "react";

export function useToggle(initialState: boolean = false): [boolean, () => void] {
  const [state, setState] = useState<boolean>(initialState);

  const toggle = useCallback(() => {
    setState((prev) => !prev);
  }, []);

  return [state, toggle];
}

Usage

Simple Modal Toggle

A clean way to handle modal open/close states.

import { useToggle } from '@/hooks/use-toggle';

export default function ModalDemo() {
  const [isOpen, toggleModal] = useToggle(false);

  return (
    <div>
      <button onClick={toggleModal} className="border p-2">
        {isOpen ? 'Close' : 'Open'} Modal
      </button>

      {isOpen && (
        <div className="fixed inset-0 bg-black/50 flex items-center justify-center">
          <div className="bg-white p-6 rounded-lg shadow-xl">
            <h2 className="text-xl font-bold">I am a Modal!</h2>
            <button onClick={toggleModal} className="mt-4 text-red-500 underline">
              Dismiss
            </button>
          </div>
        </div>
      )}
    </div>
  );
}

Password Visibility Example

Using useToggle to switch between type="password" and type="text".

import { useToggle } from '@/hooks/use-toggle';

export default function PasswordInput() {
  const [isVisible, toggleVisibility] = useToggle(false);

  return (
    <div className="flex flex-col gap-2 max-w-xs">
      <label className="text-sm font-medium">Password</label>
      <div className="relative">
        <input
          type={isVisible ? 'text' : 'password'}
          placeholder="Enter password..."
          className="border w-full p-2 pr-10 rounded shadow-sm focus:ring-2 focus:ring-primary"
        />
        <button
          onClick={toggleVisibility}
          className="absolute right-2 top-2 text-xs text-muted-foreground hover:text-foreground"
        >
          {isVisible ? 'Hide' : 'Show'}
        </button>
      </div>
    </div>
  );
}

Advanced: Custom Toggler List

Using useToggle for multiple independent items.

import { useToggle } from '@/hooks/use-toggle';

function FAQItem({ question, answer }: { question: string, answer: string }) {
  const [isExpanded, toggle] = useToggle(false);

  return (
    <div className="border-b py-4">
      <button 
        onClick={toggle} 
        className="flex w-full justify-between items-center font-semibold text-lg"
      >
        <span>{question}</span>
        <span className={`transform transition-transform ${isExpanded ? 'rotate-180' : ''}`}>

        </span>
      </button>
      {isExpanded && <p className="mt-2 text-muted-foreground">{answer}</p>}
    </div>
  );
}

API Reference

Parameters

PropTypeDefaultDescription
initialState
booleanfalseThe initial boolean value of the state.

Return Value

PropTypeDefaultDescription
value
booleanThe current boolean state.
toggle
() => voidA memoized function that negates the current value.

Why useCallback? The toggle function is internally wrapped in useCallback with no dependencies. This means the function reference stays stable for the entire lifecycle of the component, making it perfect for passing down to memoized children.

Built by Varun

Last Updated:April 10, 2026

On this page