JSPM

  • Created
  • Published
  • Downloads 3634
  • Score
    100M100P100Q120986F
  • License Apache-2.0

A React component that provides a developer-friendly toolbar for interacting with LaunchDarkly during development

Package Exports

  • @launchdarkly/toolbar

Readme

LaunchDarkly Toolbar

A developer-friendly React component that provides real-time feature flag management and debugging capabilities during development. The toolbar integrates seamlessly with LaunchDarkly, allowing developers to inspect, override, and test feature flags without leaving their application.

Features

  • 🎯 Dual Mode Operation: Works with both LaunchDarkly Dev Server and SDK
  • 🔄 Real-time Flag Management: View and toggle feature flags instantly
  • 🎨 Shadow DOM Isolation: Zero CSS conflicts with your application
  • 📍 Flexible Positioning: Place toolbar in any corner of your screen
  • 🔌 Plugin System: Extend functionality with custom plugins
  • 📊 Event Monitoring: Track and inspect LaunchDarkly events
  • 🔍 Search & Filter: Quickly find flags in large projects
  • Hot Reload Support: Automatically reflects flag changes

Installation

npm install @launchdarkly/toolbar
# or
pnpm add @launchdarkly/toolbar
# or
yarn add @launchdarkly/toolbar

Usage

The Developer Toolbar is intended for use during local development. As such, you should ensure that the way you are instantiating it will keep it disabled in production environments.

The Developer Toolbar depends on your LaunchDarkly JS client having a reference to the same FlagOverridePlugin and EventInterceptionPlugin that you pass into the Developer Toolbar. As such, ensure that you instantiate the Developer Toolbar at the same time or immediately after the LaunchDarkly JS client is instantiated. Below are a few examples on how to instantiate the toolbar, one using the useLaunchDarklyToolbar react hook, and one using the CDN hosted toolbar script.

import { render } from 'react-dom';
import { asyncWithLDProvider } from 'launchdarkly-react-client-sdk';
import { useLaunchDarklyToolbar, FlagOverridePlugin, EventInterceptionPlugin } from '@launchdarkly/toolbar';

const flagOverridePlugin = new FlagOverridePlugin();
const eventInterceptionPlugin = new EventInterceptionPlugin();

(async () => {
  const LDProvider = await asyncWithLDProvider({
    clientSideID: 'client-side-id-123abc',
    context: {
      kind: 'user',
      key: 'user-key-123abc',
      name: 'Sandy Smith',
      email: 'sandy@example.com',
    },
    options: {
      plugins: [
        flagOverridePlugin,
        eventInterceptionPlugin,
        // other plugins...
      ],
      // other options...
    },
  });

  function App() {
    // Initialize toolbar with the same plugin instances
    useLaunchDarklyToolbar({
      flagOverridePlugin, // For flag overrides (SDK Mode only)
      eventInterceptionPlugin, // For event monitoring (works in both modes)

      // OR Dev Server Mode: Connect to LaunchDarkly dev server
      devServerUrl: 'http://localhost:8080',
      projectKey: 'my-project', // Optional: auto-detects if not provided

      // Common options
      position: 'bottom-right',
      enabled: process.env.NODE_ENV === 'development',
    });

    return <YourApp />;
  }

  render(
    <LDProvider>
      <App />
    </LDProvider>,
    document.getElementById('root'),
  );
})();

CDN Script Tag

Add this script to your index.html file.

<script src="https://unpkg.com/@launchdarkly/toolbar@latest/cdn/toolbar.min.js"></script>

In your corresponding code, wherever you instantiate your LaunchDarkly JS client, be sure to pass in the following plugins:

import * as LDClient from 'launchdarkly-js-client-sdk';
import { FlagOverridePlugin, EventInterceptionPlugin } from '@launchdarkly/toolbar';

const flagOverridePlugin = new FlagOverridePlugin();
const eventInterceptionPlugin = new EventInterceptionPlugin();

const context: LDClient.LDContext = {
  kind: 'user',
  key: 'context-key-123abc',
};

const client = LDClient.initialize('client-side-id-123abc', context, {
  plugins: [
    // any other plugins you might want
    flagOverridePlugin,
    eventInterceptionPlugin,
  ],
});

try {
  await client.waitForInitialization(5);
  // initialization succeeded, flag values are now available
  handleInitializedClient(client);
} catch (err) {
  // initialization failed or did not complete before timeout
}

if (process.env.NODE_ENV === 'development' && window.LaunchDarklyToolbar) {
  window.LaunchDarklyToolbar.init({
    flagOverridePlugin,
    eventInterceptionPlugin,
    position: 'bottom-right',
  });
}

Note: if you are using typescript and want type safety for the window.LaunchDarklyToolbar.init call, you can add a window.d.ts file to your application with the following:

import type { LaunchDarklyToolbar } from '@launchdarkly/toolbar';

declare global {
  interface Window {
    LaunchDarklyToolbar?: LaunchDarklyToolbar;
  }
}

Package Structure

@launchdarkly/toolbar/
├── src/
│   ├── core/              # Framework-agnostic toolbar implementation
│   │   ├── context/       # React context providers
│   │   ├── services/      # Business logic (DevServerClient, FlagStateManager)
│   │   ├── ui/            # UI components (Toolbar, Tabs, List, etc.)
│   │   ├── tests/         # Unit tests
│   │   └── index.ts       # Core entry point (for CDN builds)
│   ├── react/             # React-specific integrations and utilities
│   │   ├── useLaunchDarklyToolbar.ts  # Main React hook
│   │   └── lazyLoadToolbar.ts         # Dynamic CDN loading
│   ├── types/             # TypeScript type definitions
│   │   ├── config.ts      # Configuration types
│   │   ├── events.ts      # Event types
│   │   ├── plugins.ts     # Plugin interfaces
│   │   └── index.ts       # Type exports
│   └── index.ts           # Main entry point (NPM package)
├── dist/                  # NPM package output
│   ├── index.js           # ES module build
│   ├── index.cjs          # CommonJS build
│   └── index.d.ts         # TypeScript definitions
├── cdn/                   # CDN bundle output
│   └── toolbar.min.js     # IIFE bundle for script tags
├── .storybook/            # Storybook configuration
└── stories/               # Component documentation

Configuration Options

Common Options

interface ToolbarConfig {
  // LaunchDarkly configuration
  baseUrl?: string; // LaunchDarkly API base URL
  projectKey?: string; // Project key (auto-detected if not provided)

  // Dev Server Mode
  devServerUrl?: string; // URL to LaunchDarkly dev server

  // SDK Mode Plugins
  flagOverridePlugin?: IFlagOverridePlugin; // Enable flag overrides
  eventInterceptionPlugin?: IEventInterceptionPlugin; // Monitor events

  // UI Configuration
  position?: 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right';
  pollIntervalInMs?: number; // Polling interval (default: 1000ms)
}

React Hook Options

interface UseLaunchDarklyToolbarConfig extends ToolbarConfig {
  toolbarBundleUrl?: string; // Custom CDN URL for local development
  enabled?: boolean; // Toggle toolbar on/off (default: true)
}

Modes

Dev Server Mode

Connect directly to a LaunchDarkly dev server to manage server-side flags:

useLaunchDarklyToolbar({
  devServerUrl: 'http://localhost:8080',
  projectKey: 'my-project', // Optional
  position: 'bottom-right',
});

or if you are using the CDN script:

window.LaunchDarklyToolbar.init({
  devServerUrl: 'http://localhost:8080',
  projectKey: 'my-project', // Optional
  position: 'bottom-right',
});

Features:

  • View all flags from your LaunchDarkly project
  • Set flag overrides that persist in dev server
  • Changes visible to all connected clients
  • Ideal for backend/full-stack development

SDK Mode

Integrate with LaunchDarkly React SDK for client-side flag management:

import { useFlagOverridePlugin, useEventInterceptionPlugin } from './plugins';

useLaunchDarklyToolbar({
  flagOverridePlugin: useFlagOverridePlugin(),
  eventInterceptionPlugin: useEventInterceptionPlugin(),
  position: 'bottom-right',
});

or if you are using the CDN script:

window.LaunchDarklyToolbar.init({
  flagOverridePlugin: new FlagOverridePlugin(),
  eventInterceptionPlugin: new EventInterceptionPlugin(),
  position: 'bottom-right',
});

Features:

  • Local flag overrides (client-side only)
  • Event monitoring and inspection
  • No dev server required
  • Ideal for frontend development

Plugin System

Flag Override Plugin

interface IFlagOverridePlugin {
  getAllFlags(): Record<string, any>;
  setOverride(flagKey: string, value: any): void;
  clearOverride(flagKey: string): void;
  clearAllOverrides(): void;
  onFlagsChange(callback: (flags: Record<string, any>) => void): () => void;
}

Event Interception Plugin

interface IEventInterceptionPlugin {
  getEvents(): ProcessedEvent[];
  clearEvents(): void;
  onEvent(callback: (event: ProcessedEvent) => void): () => void;
}

Development

Building

# Build both NPM and CDN outputs
pnpm build

# Build only NPM package
pnpm build:lib

# Build only CDN bundle
pnpm build:cdn

# Watch mode for development
pnpm dev

Testing

# Run unit tests
pnpm test

# Run tests in watch mode
pnpm test:watch

Storybook

# Start Storybook dev server
pnpm storybook

# Build Storybook for deployment
pnpm build:storybook

Browser Support

  • Chrome 90+
  • Firefox 88+
  • Safari 14+
  • Edge 90+

License

Apache-2.0