πŸš€ We’re actively developing new and unique custom hooks for React! Contribute on GitHub

useToggle

Simple React hook for managing boolean state with convenient toggle functions for common operations.

The useToggle hook provides an easy way to manage boolean state with additional utility functions for common operations like toggling, setting true/false explicitly. It's perfect for modals, feature flags, loading states, and any boolean-based UI interactions.

Basic Usage

import { useToggle } from 'light-hooks';

function Modal() {
  const { value: isOpen, toggle, setTrue: open, setFalse: close } = useToggle(false);

  return (
    <div>
      <button onClick={open}>Open Modal</button>
      
      {isOpen && (
        <div className="modal">
          <div className="modal-content">
            <h2>Modal Content</h2>
            <p>This is a modal dialog.</p>
            <button onClick={close}>Close</button>
            <button onClick={toggle}>Toggle</button>
          </div>
        </div>
      )}
    </div>
  );
}

API Reference

Parameters

ParameterTypeDescription
initialValuebooleanThe initial boolean value (optional, defaults to false)

Return Value

interface UseToggleReturn {
  value: boolean;              // Current boolean value
  toggle: () => void;          // Function to toggle the value
  setTrue: () => void;         // Function to set the value to true
  setFalse: () => void;        // Function to set the value to false
  setValue: (value: boolean) => void;  // Function to set a specific value
}

Examples

Feature Toggles and Settings

function Settings() {
  const { value: darkMode, toggle: toggleDarkMode } = useToggle(false);
  const { value: notifications, setValue: setNotifications } = useToggle(true);
  const { value: autoSave, setTrue: enableAutoSave, setFalse: disableAutoSave } = useToggle(true);

  return (
    <div className="settings">
      <h3>Application Settings</h3>
      
      <div className="setting-item">
        <label>
          <input
            type="checkbox"
            checked={darkMode}
            onChange={toggleDarkMode}
          />
          Dark Mode
        </label>
      </div>
      
      <div className="setting-item">
        <label>
          <input
            type="checkbox"
            checked={notifications}
            onChange={(e) => setNotifications(e.target.checked)}
          />
          Enable Notifications
        </label>
      </div>
      
      <div className="setting-item">
        <label>Auto Save</label>
        <div>
          <button 
            onClick={enableAutoSave}
            className={autoSave ? 'active' : ''}
          >
            On
          </button>
          <button 
            onClick={disableAutoSave}
            className={!autoSave ? 'active' : ''}
          >
            Off
          </button>
        </div>
      </div>
    </div>
  );
}

Loading States

function DataComponent() {
  const [data, setData] = useState(null);
  const { value: loading, setTrue: startLoading, setFalse: stopLoading } = useToggle(false);
  const { value: error, setTrue: showError, setFalse: hideError } = useToggle(false);

  const fetchData = async () => {
    startLoading();
    hideError();
    
    try {
      const response = await fetch('/api/data');
      if (!response.ok) throw new Error('Failed to fetch');
      
      const result = await response.json();
      setData(result);
    } catch (err) {
      showError();
      console.error('Fetch error:', err);
    } finally {
      stopLoading();
    }
  };

  return (
    <div>
      <button onClick={fetchData} disabled={loading}>
        {loading ? 'Loading...' : 'Fetch Data'}
      </button>
      
      {error && (
        <div className="error">
          <p>Failed to load data</p>
          <button onClick={hideError}>Dismiss</button>
        </div>
      )}
      
      {data && (
        <div className="data">
          <pre>{JSON.stringify(data, null, 2)}</pre>
        </div>
      )}
    </div>
  );
}
function Layout({ children }: { children: React.ReactNode }) {
  const { value: sidebarOpen, toggle: toggleSidebar, setFalse: closeSidebar } = useToggle(false);
  const { value: mobileMenuOpen, toggle: toggleMobileMenu } = useToggle(false);

  return (
    <div className="layout">
      <header>
        <button onClick={toggleSidebar} className="sidebar-toggle">
          ☰ Menu
        </button>
        <button onClick={toggleMobileMenu} className="mobile-menu-toggle">
          πŸ“± Mobile Menu
        </button>
      </header>
      
      <div className="layout-content">
        {sidebarOpen && (
          <aside className="sidebar">
            <nav>
              <a href="/" onClick={closeSidebar}>Home</a>
              <a href="/about" onClick={closeSidebar}>About</a>
              <a href="/contact" onClick={closeSidebar}>Contact</a>
            </nav>
            <button onClick={closeSidebar}>Close Sidebar</button>
          </aside>
        )}
        
        <main className="main-content">
          {children}
        </main>
      </div>
      
      {mobileMenuOpen && (
        <div className="mobile-menu-overlay">
          <div className="mobile-menu">
            <button onClick={toggleMobileMenu}>βœ• Close</button>
            <nav>
              <a href="/">Home</a>
              <a href="/about">About</a>
              <a href="/contact">Contact</a>
            </nav>
          </div>
        </div>
      )}
    </div>
  );
}

Form Validation and States

function ContactForm() {
  const [formData, setFormData] = useState({ name: '', email: '', message: '' });
  const { value: isSubmitting, setTrue: startSubmitting, setFalse: stopSubmitting } = useToggle(false);
  const { value: showSuccess, setTrue: showSuccessMessage, setFalse: hideSuccess } = useToggle(false);
  const { value: showValidation, setTrue: enableValidation } = useToggle(false);

  const handleSubmit = async (e: React.FormEvent) => {
    e.preventDefault();
    enableValidation();
    
    if (!formData.name || !formData.email) {
      return; // Show validation errors
    }
    
    startSubmitting();
    
    try {
      await submitForm(formData);
      showSuccessMessage();
      setFormData({ name: '', email: '', message: '' });
      
      // Hide success message after 3 seconds
      setTimeout(hideSuccess, 3000);
    } catch (error) {
      console.error('Submit error:', error);
    } finally {
      stopSubmitting();
    }
  };

  return (
    <form onSubmit={handleSubmit}>
      <div>
        <input
          type="text"
          placeholder="Name"
          value={formData.name}
          onChange={(e) => setFormData(prev => ({ ...prev, name: e.target.value }))}
        />
        {showValidation && !formData.name && (
          <span className="error">Name is required</span>
        )}
      </div>
      
      <div>
        <input
          type="email"
          placeholder="Email"
          value={formData.email}
          onChange={(e) => setFormData(prev => ({ ...prev, email: e.target.value }))}
        />
        {showValidation && !formData.email && (
          <span className="error">Email is required</span>
        )}
      </div>
      
      <textarea
        placeholder="Message"
        value={formData.message}
        onChange={(e) => setFormData(prev => ({ ...prev, message: e.target.value }))}
      />
      
      <button type="submit" disabled={isSubmitting}>
        {isSubmitting ? 'Sending...' : 'Send Message'}
      </button>
      
      {showSuccess && (
        <div className="success">
          Message sent successfully!
        </div>
      )}
    </form>
  );
}

Advanced Examples

Accordion Component

function AccordionItem({ title, children }: { title: string; children: React.ReactNode }) {
  const { value: isExpanded, toggle } = useToggle(false);

  return (
    <div className="accordion-item">
      <button
        className="accordion-header"
        onClick={toggle}
        aria-expanded={isExpanded}
      >
        {title}
        <span className={`arrow ${isExpanded ? 'expanded' : ''}`}>β–Ό</span>
      </button>
      
      {isExpanded && (
        <div className="accordion-content">
          {children}
        </div>
      )}
    </div>
  );
}

function FAQ() {
  return (
    <div className="accordion">
      <AccordionItem title="How do I get started?">
        <p>You can get started by installing our package and reading the documentation.</p>
      </AccordionItem>
      
      <AccordionItem title="Is it free to use?">
        <p>Yes, our library is completely free and open source.</p>
      </AccordionItem>
      
      <AccordionItem title="Can I contribute?">
        <p>Absolutely! We welcome contributions from the community.</p>
      </AccordionItem>
    </div>
  );
}

Multi-Step Form

function MultiStepForm() {
  const { value: step1Complete, setTrue: completeStep1 } = useToggle(false);
  const { value: step2Complete, setTrue: completeStep2 } = useToggle(false);
  const { value: showReview, setTrue: showReviewStep } = useToggle(false);

  const [formData, setFormData] = useState({
    personal: { name: '', email: '' },
    preferences: { newsletter: false, notifications: true },
    review: { terms: false }
  });

  const handleStep1Submit = (data: any) => {
    setFormData(prev => ({ ...prev, personal: data }));
    completeStep1();
  };

  const handleStep2Submit = (data: any) => {
    setFormData(prev => ({ ...prev, preferences: data }));
    completeStep2();
    showReviewStep();
  };

  return (
    <div className="multi-step-form">
      <div className="progress">
        <div className={`step ${step1Complete ? 'complete' : 'active'}`}>
          1. Personal Info
        </div>
        <div className={`step ${step2Complete ? 'complete' : step1Complete ? 'active' : ''}`}>
          2. Preferences
        </div>
        <div className={`step ${showReview ? 'active' : ''}`}>
          3. Review
        </div>
      </div>

      {!step1Complete && (
        <PersonalInfoStep onSubmit={handleStep1Submit} />
      )}
      
      {step1Complete && !step2Complete && (
        <PreferencesStep onSubmit={handleStep2Submit} />
      )}
      
      {showReview && (
        <ReviewStep data={formData} />
      )}
    </div>
  );
}

Feature Flags System

function FeatureFlagsProvider({ children }: { children: React.ReactNode }) {
  const { value: betaFeatures, toggle: toggleBetaFeatures } = useToggle(false);
  const { value: experimentalUI, toggle: toggleExperimentalUI } = useToggle(false);
  const { value: advancedMode, setValue: setAdvancedMode } = useToggle(false);

  const features = {
    betaFeatures,
    experimentalUI,
    advancedMode,
    toggleBetaFeatures,
    toggleExperimentalUI,
    setAdvancedMode
  };

  return (
    <FeatureFlagsContext.Provider value={features}>
      {children}
    </FeatureFlagsContext.Provider>
  );
}

function FeatureFlagsPanel() {
  const { 
    betaFeatures, 
    experimentalUI, 
    advancedMode,
    toggleBetaFeatures, 
    toggleExperimentalUI, 
    setAdvancedMode 
  } = useContext(FeatureFlagsContext);

  return (
    <div className="feature-flags-panel">
      <h3>Feature Flags</h3>
      
      <label>
        <input
          type="checkbox"
          checked={betaFeatures}
          onChange={toggleBetaFeatures}
        />
        Enable Beta Features
      </label>
      
      <label>
        <input
          type="checkbox"
          checked={experimentalUI}
          onChange={toggleExperimentalUI}
        />
        Experimental UI
      </label>
      
      <div>
        <span>Advanced Mode:</span>
        <button onClick={() => setAdvancedMode(true)}>Enable</button>
        <button onClick={() => setAdvancedMode(false)}>Disable</button>
      </div>
    </div>
  );
}

Theme Switcher

function ThemeSwitcher() {
  const { value: isDark, toggle: toggleTheme, setValue: setTheme } = useToggle(false);
  const { value: isSystemTheme, toggle: toggleSystemTheme } = useToggle(false);

  useEffect(() => {
    if (isSystemTheme) {
      const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)');
      setTheme(mediaQuery.matches);
      
      const handler = (e: MediaQueryListEvent) => setTheme(e.matches);
      mediaQuery.addEventListener('change', handler);
      
      return () => mediaQuery.removeEventListener('change', handler);
    }
  }, [isSystemTheme, setTheme]);

  useEffect(() => {
    document.documentElement.setAttribute('data-theme', isDark ? 'dark' : 'light');
  }, [isDark]);

  return (
    <div className="theme-switcher">
      <h3>Theme Settings</h3>
      
      <label>
        <input
          type="checkbox"
          checked={isSystemTheme}
          onChange={toggleSystemTheme}
        />
        Use System Theme
      </label>
      
      {!isSystemTheme && (
        <div className="manual-theme-controls">
          <button
            onClick={() => setTheme(false)}
            className={!isDark ? 'active' : ''}
          >
            β˜€οΈ Light
          </button>
          <button
            onClick={() => setTheme(true)}
            className={isDark ? 'active' : ''}
          >
            πŸŒ™ Dark
          </button>
          <button onClick={toggleTheme}>
            πŸ”„ Toggle
          </button>
        </div>
      )}
      
      <p>Current theme: {isDark ? 'Dark' : 'Light'}</p>
    </div>
  );
}

Comparison with useState

While you could achieve similar functionality with useState, useToggle provides convenience methods:

// Using useState (more verbose)
const [isOpen, setIsOpen] = useState(false);
const toggle = () => setIsOpen(prev => !prev);
const open = () => setIsOpen(true);
const close = () => setIsOpen(false);

// Using useToggle (concise)
const { value: isOpen, toggle, setTrue: open, setFalse: close } = useToggle(false);

TypeScript Support

The hook is fully typed with comprehensive TypeScript interfaces:

const {
  value,      // boolean
  toggle,     // () => void
  setTrue,    // () => void
  setFalse,   // () => void
  setValue    // (value: boolean) => void
}: UseToggleReturn = useToggle(false);

Performance Tips

  1. Destructure with meaningful names: Use descriptive aliases for better code readability
  2. Memoize callbacks: If passing toggle functions as props, consider using useCallback
  3. Combine related toggles: Group related boolean states in a single component

Common Use Cases

  • 🎭 UI States: Modals, dropdowns, sidebars, menus
  • βš™οΈ Settings: Feature toggles, preferences, configurations
  • πŸ“ Forms: Validation states, submission states, field visibility
  • 🎨 Themes: Dark/light mode, UI variants
  • πŸ“± Responsive: Mobile menu states, collapsible content
  • πŸ”„ Loading: Request states, progress indicators
  • 🎯 Features: A/B testing, feature flags, experimental features

Best Practices

  1. Use descriptive aliases: const { value: isVisible, setTrue: show, setFalse: hide }
  2. Group related toggles: Keep related boolean states in the same component
  3. Initialize with meaningful defaults: Consider the most common initial state
  4. Combine with other hooks: Works well with useEffect, useLocalStorage, etc.
  5. Handle cleanup: Remember to clean up listeners or timers in effects

Perfect for any boolean state management in your React applications!