npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2025 – Pkg Stats / Ryan Hefner

react-visibility-manager

v3.0.0

Published

A simple React component to manage visibility state logic.

Readme

React Visibility Manager

A simple, headless, and unopinionated React utility to manage visibility state for components like dropdowns, modals, tooltips, and accordions using React's Context API.

npm version License: MIT

Features

  • 💡 Decoupled Logic: Keep your UI components clean by separating visibility logic.
  • 💪 Flexible: Works with any component library or your custom components.
  • ⚙️ Headless: Provides the logic, not the UI. You have 100% control over the rendering and styling.
  • 🚀 Easy to Use: Get started in minutes with an intuitive component-based or hook-based API.
  • 🔵 Typed: Written in TypeScript for a great developer experience.
  • 🎯 Single/Multiple Mode: Choose between accordion-style single visibility or multiple simultaneous visibility.
  • Optimized: Memoized components and hooks for better performance.

Installation

npm install react-visibility-manager

or with Yarn:

yarn add react-visibility-manager

Quick Start

Wrap your application (or just a part of it — that's usually recommended to avoid unnecessary re-renders) with VisibilityProvider. Then, use VisibilityTrigger to control a VisibilityTarget. The triggerKey and targetKey props link them together. New: triggers can pass an optional meta payload. Targets receive that meta under a prop (default meta) — you can override the prop name with metaPropName.

import {
  VisibilityProvider,
  VisibilityTrigger,
  VisibilityTarget,
} from 'react-visibility-manager';

// Your custom components
const Button = (props) => <button {...props} />;
const DropdownMenu = ({ isOpen, ...props }) =>
  isOpen ? <div {...props} /> : null;

function App() {
  return (
    <VisibilityProvider>
      <div>
        <VisibilityTrigger triggerKey="profile-dropdown">
          <Button>My Profile</Button>
        </VisibilityTrigger>

        <VisibilityTarget targetKey="profile-dropdown">
          <DropdownMenu>
            <ul>
              <li>Settings</li>
              <li>Log Out</li>
            </ul>
          </DropdownMenu>
        </VisibilityTarget>
      </div>
    </VisibilityProvider>
  );
}

API Reference

<VisibilityProvider>

This is the context provider that holds the global visibility state. It should wrap any components that use VisibilityTrigger, VisibilityTarget, or the hooks.

| Prop | Type | Default | Description | | -------------- | ------------------------ | ------------ | -------------------------------------------------------- | | children | ReactNode | | Your React application or a part of it. | | initialState | Record<string, boolean> | {} | An initial state for your visibility keys, e.g., { 'my-modal': true }. | | mode | 'single' \| 'multiple' | 'multiple' | Controls whether only one item can be visible at a time ('single') or multiple items can be visible simultaneously ('multiple'). |

<VisibilityTrigger>

A wrapper component that makes its child element a toggle for a specific visibility key.

| Prop | Type | Default | Description | | ------------ | ------------- | --------- | --------------------------------------------------------------------------- | | children | ReactElement| | A single valid React element that will receive the toggle handler. | | triggerKey | string | | A unique key to identify which state to toggle. | | propName | string | 'onClick' | The name of the prop to which the toggle function will be passed. Use 'onMouseEnter' for hover effects. |

<VisibilityTarget>

A wrapper component that injects the visibility state into its child element.

| Prop | Type | Default | Description | | ---------------- | ------------------------------------------ | --------- | ------------------------------------------------------------------------ | | children | ReactElement | | A single valid React element that will receive the visibility state. | | targetKey | string | | A unique key that links this target to a trigger. | | isOpenPropName | string | 'isOpen'| The name of the prop that will be passed to the child with the boolean visibility state. | | shouldRender | boolean | true | If false, the component will unmount (return null) when not visible. | | wrapper | (node, isOpen) => ReactElement | | An optional render prop function to wrap the child, useful for animations. |

useVisibilityTarget(targetKey)

A hook that returns the state and controls for a specific visibility key. This is a great alternative to the <VisibilityTarget> component for more complex logic.

It returns an object with the following properties:

  • isOpen: boolean - The current visibility state.
  • toggle: () => void - A function to toggle the state.
  • set: (value: boolean) => void - A function to explicitly set the state to true or false.

useVisibilityState()

A hook that returns the entire visibility state object. Useful for reading multiple visibility states at once or for debugging purposes.

Returns Record<string, boolean> - the complete visibility state.

useVisibilityActions()

A hook that returns all the action functions for managing visibility state.

Returns an object with:

  • toggle: (key: string) => void - Toggle visibility for any key
  • set: (key: string, value: boolean) => void - Set visibility for any key
  • reset: (initial: VisibilityState) => void - Reset to initial state

Visibility Modes

The library supports two visibility modes that can be set on the VisibilityProvider:

Multiple Mode (Default)

In multiple mode, any number of visibility targets can be open simultaneously. This is the default behavior and works great for independent dropdowns, tooltips, or modals.

<VisibilityProvider mode="multiple">
  {/* Multiple items can be visible at the same time */}
</VisibilityProvider>

Single Mode

In single mode, only one visibility target can be open at a time. When you open a new target, all others automatically close. This is perfect for accordion-style components, tab panels, or exclusive dropdowns.

<VisibilityProvider mode="single">
  {/* Only one item can be visible at a time */}
</VisibilityProvider>

Examples

Using the useVisibilityTarget Hook

The hook is perfect when you need more control over rendering, such as for creating a modal component.

import {
  VisibilityProvider,
  VisibilityTrigger,
  useVisibilityTarget,
} from 'react-visibility-manager';

const Modal = ({ title, children, modalKey }) => {
  const { isOpen, set } = useVisibilityTarget(modalKey);

  if (!isOpen) {
    return null;
  }

  return (
    <div className="modal-backdrop">
      <div className="modal-content">
        <h2>{title}</h2>
        {children}
        <button onClick={() => set(false)}>Close</button>
      </div>
    </div>
  );
};

function App() {
  return (
    <VisibilityProvider>
      <VisibilityTrigger triggerKey="terms-modal">
        <button>Open Terms of Service</button>
      </VisibilityTrigger>

      <Modal modalKey="terms-modal" title="Terms of Service">
        <p>Here are the terms and conditions...</p>
      </Modal>
    </VisibilityProvider>
  );
}

Using Low-Level Hooks for Complex Logic

For advanced use cases, you can use the low-level hooks directly:

import {
  VisibilityProvider,
  useVisibilityState,
  useVisibilityActions,
} from 'react-visibility-manager';

const ComplexComponent = () => {
  const state = useVisibilityState();
  const { toggle, set, reset } = useVisibilityActions();

  // Check if multiple items are open
  const openItems = Object.entries(state).filter(([_, isOpen]) => isOpen);
  const hasMultipleOpen = openItems.length > 1;

  return (
    <div>
      <p>Currently open: {openItems.map(([key]) => key).join(', ')}</p>
      {hasMultipleOpen && (
        <button onClick={() => reset({})}>Close All</button>
      )}
      
      <button onClick={() => toggle('item-1')}>Toggle Item 1</button>
      <button onClick={() => toggle('item-2')}>Toggle Item 2</button>
      <button onClick={() => set('item-3', true)}>Open Item 3</button>
    </div>
  );
};

Creating an Accordion with Single Mode

Perfect use case for single mode - only one accordion item should be open at a time.

import {
  VisibilityProvider,
  VisibilityTrigger,
  VisibilityTarget,
} from 'react-visibility-manager';

const AccordionItem = ({ title, isOpen, children, ...props }) => (
  <div className="accordion-item" {...props}>
    <h3 className={`accordion-header ${isOpen ? 'active' : ''}`}>{title}</h3>
    {isOpen && <div className="accordion-content">{children}</div>}
  </div>
);

function Accordion() {
  return (
    <VisibilityProvider mode="single">
      <div className="accordion">
        <VisibilityTrigger triggerKey="section-1">
          <button>Section 1</button>
        </VisibilityTrigger>
        <VisibilityTarget targetKey="section-1">
          <AccordionItem title="Section 1">
            Content for section 1...
          </AccordionItem>
        </VisibilityTarget>

        <VisibilityTrigger triggerKey="section-2">
          <button>Section 2</button>
        </VisibilityTrigger>
        <VisibilityTarget targetKey="section-2">
          <AccordionItem title="Section 2">
            Content for section 2...
          </AccordionItem>
        </VisibilityTarget>

        <VisibilityTrigger triggerKey="section-3">
          <button>Section 3</button>
        </VisibilityTrigger>
        <VisibilityTarget targetKey="section-3">
          <AccordionItem title="Section 3">
            Content for section 3...
          </AccordionItem>
        </VisibilityTarget>
      </div>
    </VisibilityProvider>
  );
}

Managing Multiple Independent Components

Using multiple mode (default) for independent dropdowns that can be open simultaneously.

import {
  VisibilityProvider,
  VisibilityTrigger,
  VisibilityTarget,
} from 'react-visibility-manager';

const Dropdown = ({ isOpen, children, ...props }) => (
  <div className={`dropdown ${isOpen ? 'open' : ''}`} {...props}>
    {children}
  </div>
);

function NavigationBar() {
  return (
    <VisibilityProvider mode="multiple">
      <nav className="navbar">
        <VisibilityTrigger triggerKey="user-menu">
          <button>User Menu</button>
        </VisibilityTrigger>
        <VisibilityTarget targetKey="user-menu">
          <Dropdown>
            <ul>
              <li>Profile</li>
              <li>Settings</li>
              <li>Logout</li>
            </ul>
          </Dropdown>
        </VisibilityTarget>

        <VisibilityTrigger triggerKey="notifications">
          <button>Notifications</button>
        </VisibilityTrigger>
        <VisibilityTarget targetKey="notifications">
          <Dropdown>
            <ul>
              <li>New message from John</li>
              <li>System update available</li>
            </ul>
          </Dropdown>
        </VisibilityTarget>
      </nav>
    </VisibilityProvider>
  );
}

FAQ Component with Single Mode

import {
  VisibilityProvider,
  VisibilityTrigger,
  VisibilityTarget,
} from 'react-visibility-manager';

const FaqItem = ({ question, isOpen, children }) => (
  <div className="faq-item">
    <h3 className={`faq-question ${isOpen ? 'open' : ''}`}>
      {question}
      <span className="toggle-icon">{isOpen ? '−' : '+'}</span>
    </h3>
    {isOpen && <div className="faq-answer">{children}</div>}
  </div>
);

function Faq() {
  return (
    <VisibilityProvider mode="single">
      <div className="faq-container">
        <h2>Frequently Asked Questions</h2>

        <VisibilityTrigger triggerKey="faq-1">
          <button className="faq-trigger">What is this library for?</button>
        </VisibilityTrigger>
        <VisibilityTarget targetKey="faq-1">
          <FaqItem question="What is this library for?">
            It helps manage visibility state in React applications with a clean, 
            headless approach that works with any UI library or custom components.
          </FaqItem>
        </VisibilityTarget>

        <VisibilityTrigger triggerKey="faq-2">
          <button className="faq-trigger">Is it hard to use?</button>
        </VisibilityTrigger>
        <VisibilityTarget targetKey="faq-2">
          <FaqItem question="Is it hard to use?">
            No, it's designed to be simple and intuitive! You can get started 
            in just a few minutes with either components or hooks.
          </FaqItem>
        </VisibilityTarget>

        <VisibilityTrigger triggerKey="faq-3">
          <button className="faq-trigger">Does it support TypeScript?</button>
        </VisibilityTrigger>
        <VisibilityTarget targetKey="faq-3">
          <FaqItem question="Does it support TypeScript?">
            Yes! The library is written in TypeScript and provides full type safety 
            and great developer experience with IntelliSense support.
          </FaqItem>
        </VisibilityTarget>
      </div>
    </VisibilityProvider>
  );
}

Performance Optimizations

The library is optimized for performance with several built-in optimizations:

  • Memoized Components: All components (VisibilityProvider, VisibilityTarget, VisibilityTrigger) are memoized to prevent unnecessary re-renders.
  • Stable Callbacks: Hook callbacks are memoized and only recreate when dependencies change.
  • Split Context: State and actions are provided through separate contexts to minimize re-renders.
  • Efficient Updates: The reducer pattern ensures state updates are predictable and efficient.

Best Practices

  1. Scope Your Providers: Don't wrap your entire app unless necessary. Use multiple smaller providers for different sections.
// ✅ Good - scoped provider
function Dashboard() {
  return (
    <VisibilityProvider>
      {/* Dashboard-specific visibility logic */}
    </VisibilityProvider>
  );
}

// ❌ Avoid - unnecessarily broad scope
function App() {
  return (
    <VisibilityProvider>
      <Router>
        <Routes>
          {/* Entire app */}
        </Routes>
      </Router>
    </VisibilityProvider>
  );
}
  1. Use Meaningful Keys: Choose descriptive keys for better debugging and maintainability.
// ✅ Good
<VisibilityTrigger triggerKey="user-profile-dropdown">

// ❌ Avoid
<VisibilityTrigger triggerKey="dropdown1">

TypeScript Support

The library is fully typed and provides excellent TypeScript support:

import type { 
  VisibilityState, 
  VisibilityMode, 
  UseVisibilityTargetResult,
  VisibilityProviderProps,
  VisibilityTargetProps,
  VisibilityTriggerProps 
} from 'react-visibility-manager';

// Custom hook with full type safety
const useCustomVisibility = (key: string): UseVisibilityTargetResult => {
  return useVisibilityTarget(key);
};

// Typed component props
interface CustomModalProps {
  modalKey: string;
  title: string;
  children: React.ReactNode;
}

const CustomModal: React.FC<CustomModalProps> = ({ modalKey, title, children }) => {
  const { isOpen, set } = useVisibilityTarget(modalKey);
  
  // TypeScript knows isOpen is boolean, set accepts boolean
  return isOpen ? (
    <div>
      <h2>{title}</h2>
      {children}
      <button onClick={() => set(false)}>Close</button>
    </div>
  ) : null;
};

Contributing

If you encounter any bugs or have suggestions for improvements, please feel free to open an issue or submit a pull request on GitHub. We welcome contributions from the community!

License

MIT