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 🙏

© 2026 – Pkg Stats / Ryan Hefner

@unleash/toolbar

v1.2.0

Published

Client-side override toolbar for Unleash JavaScript SDKs

Readme

Unleash Toolbar

A client-side debugging toolbar for Unleash feature flags. Override flag values and context in real-time without server changes.

Unleash Toolbar Screenshot

Features

  • Flag Overrides: Force boolean flags ON/OFF or override variant values
  • Context Overrides: Modify userId, sessionId, environment, and custom properties
  • Persistence: Save overrides in memory, sessionStorage, or localStorage
  • React Integration: Seamlessly wraps the official @unleash/proxy-client-react SDK
  • Next.js SSR Support: Server-side rendering with cookie-based state sync using @unleash/nextjs
  • Customizable UI: Theming support and positioning options
  • Draggable & Dismissible: Drag the floating icon to any window edge, minimize to the icon, or hide it entirely until the next page refresh
  • Keyboard First: Cmd/Ctrl+Shift+F to summon, Esc to minimize, focus tethered back to wherever you opened it from, and full screen-reader semantics
  • Custom Banner: Optional message in the toolbar to clarify its scope for your team
  • SDK Compatible: Works with Unleash JavaScript SDK

Bundle Size

The toolbar is optimized for minimal impact on your application:

  • Core: ~12 KB gzipped (~2.4 KB entry + ~9.6 KB panel UI, loaded asynchronously)
  • React: ~0.6 KB gzipped (thin wrapper)
  • Next.js: ~0.7 KB gzipped (server utilities)
  • CSS: ~3 KB gzipped

Installation

npm install @unleash/toolbar

# For React integration
npm install @unleash/toolbar @unleash/proxy-client-react unleash-proxy-client

# For Next.js SSR integration
npm install @unleash/toolbar @unleash/nextjs

Quick Start

Vanilla JavaScript

import { initUnleashToolbar } from '@unleash/toolbar';
import { UnleashClient } from 'unleash-proxy-client';
import '@unleash/toolbar/toolbar.css';

// Initialize toolbar with new Unleash client - returns wrapped client
const client = initUnleashToolbar(new UnleashClient({
  url: 'https://your-unleash-instance.com/api/frontend',
  clientKey: 'your-client-key',
  appName: 'my-app'
}), {
  storageMode: 'local',
  position: 'bottom-right',
  initiallyVisible: true
});

// Start the client
await client.start();

// Use the client for all flag evaluations
const isEnabled = client.isEnabled('my-feature');
const variant = client.getVariant('my-experiment');

// Listen for changes (from toolbar or SDK updates)
client.on('update', () => {
  // Re-evaluate flags when overrides change
  const newValue = client.isEnabled('my-feature');
  updateUI(newValue);
});

React

The toolbar integrates seamlessly with the official @unleash/proxy-client-react SDK. Just pass your configuration - the toolbar handles everything automatically!

npm install @unleash/toolbar @unleash/proxy-client-react unleash-proxy-client
import { useFlag, useVariant } from '@unleash/proxy-client-react';
import { UnleashToolbarProvider } from '@unleash/toolbar/react';
import '@unleash/toolbar/toolbar.css';

// Same config format as the official React SDK
const config = {
  url: 'https://your-unleash-instance.com/api/frontend',
  clientKey: 'your-client-key',
  appName: 'my-app',
  refreshInterval: 15
};

// That's it! No need to import FlagProvider or manage the SDK client
function App() {
  return (
    <UnleashToolbarProvider 
      config={config}
      toolbarOptions={{
        storageMode: 'local',
        position: 'bottom-right'
      }}
    >
      <MyComponent />
    </UnleashToolbarProvider>
  );
}

// Use hooks from the official React SDK - they work with toolbar overrides!
function MyComponent() {
  const isEnabled = useFlag('my-feature');
  const variant = useVariant('my-experiment');
  
  return (
    <div>
      {isEnabled && <NewFeature />}
      {variant.name === 'variant-a' && <VariantA />}
    </div>
  );
}

Key Points:

  • Pass config directly to UnleashToolbarProvider - no other imports needed!
  • The toolbar automatically uses FlagProvider from the React SDK
  • Import hooks from @unleash/proxy-client-react (the official SDK)
  • All official React SDK hooks work seamlessly with toolbar overrides

Advanced: Custom FlagProvider or pre-instantiated client

import { FlagProvider } from '@unleash/proxy-client-react';
import { UnleashClient } from 'unleash-proxy-client';

// Option 1: Custom FlagProvider
<UnleashToolbarProvider 
  FlagProvider={FlagProvider}  // Optional - use if you need customization
  config={config}
>
  <MyApp />
</UnleashToolbarProvider>

// Option 2: Pre-instantiated client
const client = new UnleashClient({ /* config */ });

<UnleashToolbarProvider 
  client={client}  // Pass client instead of config
  toolbarOptions={{ /* ... */ }}
>
  <MyApp />
</UnleashToolbarProvider>

Next.js App Router (Client & Server Components)

The toolbar provides full Next.js App Router support with server-side rendering and cookie-based state synchronization.

Client Components

// app/layout.tsx
import { UnleashToolbarProvider } from '@unleash/toolbar/next';
import '@unleash/toolbar/toolbar.css';

export default function RootLayout({ children }) {
  return (
    <html>
      <body>
        <UnleashToolbarProvider
          config={{
            url: process.env.NEXT_PUBLIC_UNLEASH_URL!,
            clientKey: process.env.NEXT_PUBLIC_UNLEASH_CLIENT_KEY!,
            appName: 'my-next-app',
          }}
        >
          {children}
        </UnleashToolbarProvider>
      </body>
    </html>
  );
}
// app/page.tsx
'use client';

import { useFlag, useVariant } from '@unleash/toolbar/next';

export default function HomePage() {
  const isEnabled = useFlag('new-checkout');
  const variant = useVariant('payment-provider');

  return (
    <div>
      {isEnabled && <NewCheckout />}
      <PaymentForm provider={variant.name} />
    </div>
  );
}

Server Components with SSR

// app/server-page/page.tsx
import { cookies } from 'next/headers';
import { getDefinitions, evaluateFlags, flagsClient } from '@unleash/nextjs';
import { applyToolbarOverrides } from '@unleash/toolbar/next/server';

export default async function ServerPage() {
  // Fetch definitions from Unleash API (uses env config)
  const definitions = await getDefinitions({
    fetchOptions: { next: { revalidate: 15 } },
  });

  // Apply toolbar overrides from cookies
  const cookieStore = await cookies();
  const modifiedDefinitions = applyToolbarOverrides(definitions, cookieStore);

  // Evaluate flags with context
  const { toggles } = evaluateFlags(modifiedDefinitions, {
    sessionId: 'session-id',
    userId: 'user-id',
  });

  // Create offline client
  const flags = flagsClient(toggles);

  // Check flags server-side
  const isEnabled = flags.isEnabled('new-feature');

  return <div>{isEnabled ? 'Feature ON' : 'Feature OFF'}</div>;
}

Environment Variables for Next.js:

# Used by @unleash/nextjs SDK
UNLEASH_SERVER_API_URL=https://your-unleash-instance.com/api
UNLEASH_SERVER_API_TOKEN=your-server-token
UNLEASH_APP_NAME=my-app

# Used by client-side toolbar
NEXT_PUBLIC_UNLEASH_URL=https://your-unleash-instance.com/api/frontend
NEXT_PUBLIC_UNLEASH_CLIENT_KEY=your-client-key
NEXT_PUBLIC_UNLEASH_APP_NAME=my-app

How it works:

  1. Client-side toolbar automatically syncs state to cookies
  2. Server components read toolbar state from cookies
  3. applyToolbarOverrides() modifies flag definitions before evaluation
  4. Flags evaluate server-side with toolbar overrides applied
  5. Changes in toolbar immediately affect both client and server rendering

Vue 3 (Composition API)

<script setup lang="ts">
import { ref, watch } from 'vue'
import { useUnleash } from './composables/useUnleash'

// Initialize Unleash with the composable
const { unleashClient, isReady, updateTrigger } = useUnleash()

// Reactive flag states
const isEnabled = ref(false)

// Evaluate flags
const evaluateFlags = () => {
  if (!unleashClient.value) return
  isEnabled.value = unleashClient.value.isEnabled('my-feature')
}

// Evaluate when ready
watch(isReady, (ready) => {
  if (ready) evaluateFlags()
})
</script>

Configuration Options

initUnleashToolbar(client, options)

interface InitToolbarOptions {
  // Persistence mode (default: 'local')
  // - 'local': Persists across tabs and browser restarts (RECOMMENDED for development)
  // - 'session': Persists only in current tab, cleared when tab closes
  // - 'memory': No persistence, cleared on page reload
  storageMode?: 'memory' | 'session' | 'local';
  
  // Storage key for persistence (default: 'unleash-toolbar-state')
  storageKey?: string;
  
  // UI position (default: 'bottom-right')
  // Corner positions: 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right'
  // Side positions (vertically centered): 'left' | 'right'
  // Used as the starting position; overridden once the user drags the toolbar.
  position?: 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right' | 'left' | 'right';
  
  // Allow dragging the floating icon to reposition the toolbar along any
  // window edge. The chosen position is persisted. (default: true)
  draggable?: boolean;
  
  // Initial visibility (default: false, but respects persisted state if available)
  initiallyVisible?: boolean;
  
  // Optional informational message shown as a banner below the header.
  // Useful for clarifying the toolbar's scope to your team, e.g.
  // "Only client-side flags are overridable here". Empty by default.
  banner?: string;
  
  // Optional URL shown as a link next to the banner message (opens in a new
  // tab). Only rendered when `banner` is also set.
  bannerLink?: string;
  
  // Text for the banner link (default: "Read more"). Only used with bannerLink.
  bannerLinkText?: string;
  
  // Sort flags alphabetically instead of by evaluation order (default: false)
  sortAlphabetically?: boolean;
  
  // Theme preset (default: 'light')
  themePreset?: 'light' | 'dark';
  
  // Theme customization (overrides preset)
  theme?: {
    primaryColor?: string;
    backgroundColor?: string;
    textColor?: string;
    borderColor?: string;
    fontFamily?: string;
  };
  
  // Custom container element (default: document.body)
  container?: HTMLElement | null;

  // Enable cookie sync for SSR (default: false)
  // Set to true for Next.js or other SSR frameworks
  enableCookieSync?: boolean;

  // Render the floating toggle icon when collapsed (default: true)
  // Set to false for a shortcut-only setup where the toolbar renders nothing
  // until it is opened.
  showToggleButton?: boolean;

  // Keyboard shortcut that opens the panel, focuses it when already open, and
  // minimizes it when pressed from inside (default: 'mod+shift+f')
  // 'mod' is Cmd on macOS and Ctrl elsewhere. Set to false to register no
  // global key listener.
  shortcut?: string | false;

  // Minimize the panel when a pointer press lands outside it (default: false)
  closeOnOutsideClick?: boolean;

  // Where to place focus when the panel opens (default: 'panel')
  focusOnOpen?: 'panel' | 'search' | 'context';
}

Next.js Server Utilities

import { applyToolbarOverrides, applyToolbarOverridesToToggles, getToolbarStateFromCookies } from '@unleash/toolbar/next/server';
import type { ClientFeaturesResponse, IToggle } from '@unleash/nextjs';

// Apply overrides before evaluation (recommended)
const modifiedDefinitions = applyToolbarOverrides(
  definitions: ClientFeaturesResponse,
  cookieStore: CookieStore
): ClientFeaturesResponse;

// Apply overrides after evaluation (alternative)
const modifiedToggles = applyToolbarOverridesToToggles(
  toggles: IToggle[],
  cookieStore: CookieStore
): IToggle[];

// Read toolbar state from cookies
const state = getToolbarStateFromCookies(
  cookieStore: CookieStore
): ToolbarState | null;

Storage Modes Explained

local (default): Best for development workflows

  • ✅ Persists across all browser tabs
  • ✅ Survives page reloads and browser restarts
  • ✅ Set overrides once, test everywhere
  • Use case: Daily feature development and debugging

session: Useful for isolated testing

  • ✅ Persists within current tab only
  • ✅ Survives page reloads in the same tab
  • ❌ Lost when tab is closed
  • Use case: Testing different configurations in multiple tabs simultaneously

memory: Temporary testing only

  • ❌ Lost on every page reload
  • ❌ No persistence whatsoever
  • Use case: Quick one-off tests or strict security requirements

API Reference

Toolbar Instance

const toolbar = window.unleashToolbar;

// Show/hide the toolbar
toolbar.show();
toolbar.hide();
// Opens, or focuses an already-open panel, or minimizes it if focus is inside
toolbar.toggle();

// Open with focus on a specific control
toolbar.show({ focus: 'search' });   // flag search box
toolbar.show({ focus: 'context' });  // first Context field (switches tab)
toolbar.show({ focus: 'panel' });    // the panel itself (default)

// Get current state
const state = toolbar.getState();

// Set flag overrides
toolbar.setFlagOverride('my-feature', { type: 'flag', value: true });
toolbar.setFlagOverride('my-variant', { type: 'variant', variantKey: 'variant-b' });
toolbar.setFlagOverride('my-feature', null); // Clear override

// Set context overrides
toolbar.setContextOverride({
  userId: 'test-user-123',
  properties: { tier: 'premium' }
});

// Reset overrides
toolbar.resetOverrides();
toolbar.resetContextOverrides();

// Cleanup
toolbar.destroy();

Listening to Changes

The toolbar automatically triggers the Unleash SDK's 'update' event when overrides change. Use the standard SDK pattern to listen for changes:

// Listen to flag/context changes from the toolbar
client.on('update', () => {
  console.log('Flags updated - re-evaluate your flags');
  // Re-render your UI, re-check flags, etc.
});

This works for both toolbar changes (flag overrides, context overrides) and SDK changes (new config from server).

React Hooks

Use hooks from the official @unleash/proxy-client-react SDK - they automatically work with toolbar overrides!

import {
  useFlag,
  useVariant,
  useUnleashClient,
  useUnleashContext,
  useFlagsStatus
} from '@unleash/proxy-client-react';

// Check flag status - updates automatically when toolbar overrides change
const isEnabled = useFlag('my-feature');

// Get variant - updates automatically when toolbar overrides change
const variant = useVariant('my-experiment');

// Access client (the wrapped client from the toolbar)
const client = useUnleashClient();

// Update context dynamically
const updateContext = useUnleashContext();
await updateContext({ userId: 'new-user-id' });

// Check loading/ready state
const { flagsReady, flagsError } = useFlagsStatus();

Next.js Hooks

For Next.js, import hooks from @unleash/toolbar/next, they're re-exported for convenience:

import { useFlag, useVariant, useUnleashClient } from '@unleash/toolbar/next';

const isEnabled = useFlag('my-feature');
const variant = useVariant('my-experiment');

UI Features

Flag List Tab

  • Override Controls: Dropdown to set boolean or variant overrides
  • Flag Info: See default vs. effective values for each flag
  • Quick Actions: Reset individual flag overrides

Context Tab

  • Standard Fields: userId, sessionId, remoteAddress, environment, appName
  • Custom Properties: Edit or reset property values
  • Live Updates: Changes apply immediately to all evaluations

Header Actions

  • Reset Flags: Clear all flag overrides
  • Reset Context: Clear all context overrides
  • Close: Hide the toolbar

Keyboard & Accessibility

The toolbar is fully operable from the keyboard.

Shortcuts

| Key | Action | | --- | --- | | Cmd/Ctrl + Shift + F | Open the panel, focus it when it is already open, or minimize it when pressed from inside | | Esc | Minimize, handing focus back to wherever you opened the panel from | | Tab / Shift + Tab | Move between controls; off either end, focus returns to the page where you left it | | | Switch tabs (while a tab has focus) | | | Change a flag's override between OFF / — / ON |

Rebind or disable the shortcut with the shortcut option:

initUnleashToolbar(client, {
  shortcut: 'mod+shift+u', // 'mod' is Cmd on macOS, Ctrl elsewhere
});

initUnleashToolbar(client, {
  shortcut: false, // register no global key listener
});

Shortcut-driven setup

To render nothing until the toolbar is summoned, and land directly in the flag search box:

initUnleashToolbar(client, {
  showToggleButton: false,
  focusOnOpen: 'search',
});

The toolbar stays mounted and listening while "closed" — it is hidden with display: none, so there is no re-initialization cost on reopen, and it is out of both the tab order and the accessibility tree while hidden.

With showToggleButton: false and shortcut: false there is no way for a user to reopen the toolbar; only the programmatic API can. Keep at least one of them enabled.

Focus behaviour

The panel is not modal. The page underneath stays interactive, is never marked inert, and focus is never trapped — watching your app react to a flag change is the whole point of the tool, and a keyboard user has to be able to get back out there.

Opened from the keyboard, focus lands on the panel itself rather than on a control, so screen readers announce the region and its name — and the panel draws a focus ring so sighted keyboard users can see that focus has left the page. The ring stays off when the panel is opened by clicking the floating icon.

From there, focus is tethered to wherever the panel was summoned from:

  • Tab off the last control (or Shift+Tab off the first) returns focus to the element you opened the panel from, leaving the panel open. The next Tab carries on through your page from that point.
  • The shortcut is the way back in. Pressed from the page with the panel open it pulls focus into the panel rather than closing it; pressed from inside the panel it minimizes.
  • Esc inside the panel minimizes and hands focus back to the same origin — or to the floating icon when the icon is what opened it.

Without an origin to return to (the panel was opened by clicking the icon, or the origin has since been unmounted), Tab is left to the browser's own ordering.

Semantics

  • The panel is a labelled region landmark, so assistive tech can jump straight to it regardless of where the toolbar sits in the tab order
  • Tabs use the WAI-ARIA tabs pattern, with arrow-key navigation and a roving tab stop
  • Each flag's OFF / — / ON control is a radiogroup labelled with the flag name, so assistive tech reports which state is in effect. Its roving tab stop also means a flag costs one Tab press to pass, not three
  • Icon-only controls carry visually hidden text rather than relying on title
  • Filtering the flag list announces its result count through a status region, debounced so a burst of typing produces one announcement rather than one per keystroke
  • Animations are suppressed under prefers-reduced-motion: reduce

Theme Customization

Override CSS variables or use the theme option:

const toolbar = initUnleashSessionToolbar({
  theme: {
    primaryColor: '#ff6b6b',
    backgroundColor: '#ffffff',
    textColor: '#2d3436',
    borderColor: '#dfe6e9',
    fontFamily: 'Inter, sans-serif',
    focusColor: '#ff6b6b' // keyboard focus ring; optional
  }
});

Focus ring colour

The keyboard focus ring follows themePreset automatically — a mid purple on light, a lighter purple on dark, both above the 3:1 contrast ratio WCAG requires of non-text indicators. Controls on the coloured header use a white ring instead, since primaryColor already has to be dark enough to carry the header's white text.

Set theme.focusColor only when a custom backgroundColor would leave the default ring hard to see — e.g. a dark backgroundColor while staying on the light preset:

initUnleashToolbar(client, {
  theme: { backgroundColor: '#101010', textColor: '#f5f5f5', focusColor: '#A9A6F5' },
});

Or override CSS variables globally:

:root {
  --unleash-toolbar-primary: #your-color;
  --unleash-toolbar-bg: #your-bg;
  --unleash-toolbar-text: #your-text;
  --unleash-toolbar-border: #your-border;
  --unleash-toolbar-font: your-font;
}

Development

# Install dependencies
npm install

# Build the library
npm run build

# Run tests
npm test

# Run tests in watch mode
npm run test:watch

# Run tests with coverage
npm run test:coverage

# Run type checking
npm run type-check

# Run linter
npm run lint

# Auto-fix linting issues
npm run lint:fix

Example Applications

The repository includes example applications demonstrating integration with different frameworks:

  • Vanilla JS - Basic HTML/JavaScript integration

    npm run serve:example:vanilla
  • React - Hooks and provider pattern with official React SDK

    npm run serve:example:react
  • Next.js App Router - Server-side rendering with client and server components

    npm run serve:example:nextjs
  • Angular - Service-based integration

    npm run serve:example:angular
  • Vue 3 - Composition API with composables

    npm run serve:example:vue

All examples include:

  • Environment configuration setup
  • Multiple feature flags for testing
  • Variant flag demonstrations
  • Toolbar integration best practices
  • Server-side rendering examples (Next.js)

Requirements

  • Browser: Modern browsers with ES2020 support (Chrome 90+, Firefox 88+, Safari 14+)
  • Unleash SDKs:
    • unleash-proxy-client ^3.0.0 (required)
    • @unleash/proxy-client-react ^5.0.0 (optional, for React)
    • @unleash/nextjs ^1.0.0 (optional, for Next.js SSR)

Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

License

Apache-2.0

Links

Support