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)
npx shadcn@latest add @nurav-ui/use-toggleAlternatively, install via direct URL:
npx shadcn@latest add https://nurav-ui.vercel.app/r/use-toggle.jsonManual Setup
Create the hook file
Copy the source into 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
Return 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.