Angular Toolbar

August 3, 2026 · View on GitHub

npm version Downloads License Angular Compatibility

A development toolbar for Angular applications that helps developers interact with the application more efficiently. Verified against Angular 19, 20, 21, and 22.

Toolbar Demo

Why ngx-dev-toolbar?

  • Toggle feature flags without backend changes
  • Simulate complete i18n environments (locale, timezone, currency, RTL)
  • Test product features and subscription tiers
  • Switch themes on the fly
  • Change user sessions effortlessly
  • Mock network requests in real-time
  • Test permission-based UI without backend changes

No more context switching or backend dependencies - everything you need is right in your browser!

Installation

npm install ngx-dev-toolbar

Compatibility

ngx-dev-toolbar is continuously verified against every supported Angular major. Each release is AOT-compiled against the following versions in CI (compat.yml):

AngularStatus
19✅ Verified
20✅ Verified
21✅ Verified
22✅ Verified

The published package declares an open peer range (@angular/* >=19.0.0), and the compatibility matrix above is what backs that claim. Standalone sandbox apps live in sandboxes/; run npm run verify:compat to reproduce locally.

Quick Start

Add the toolbar to your app.config.ts alongside your other providers:

// app.config.ts
import { ApplicationConfig, isDevMode } from '@angular/core';
import { provideRouter } from '@angular/router';
import { provideToolbar } from 'ngx-dev-toolbar';

export const appConfig: ApplicationConfig = {
  providers: [
    provideRouter(appRoutes),
    provideToolbar({
      enabled: isDevMode(),
    }),
  ],
};
// main.ts
import { bootstrapApplication } from '@angular/platform-browser';
import { AppComponent } from './app/app.component';
import { appConfig } from './app/app.config';

bootstrapApplication(AppComponent, appConfig);

That's it! No template changes needed. The toolbar automatically attaches to the DOM.

Available Tools

ToolDescription
Feature FlagsToggle feature flags on/off during development
PermissionsTest permission-based UI without backend changes
App FeaturesTest product features and subscription tiers
i18nAdvanced i18n simulation — locale, timezone, currency, pseudo-localization, RTL
PresetsSave and restore tool configurations
Network MockerMock HTTP requests in real-time

Feature Flags

import { ToolbarFeatureFlagService } from 'ngx-dev-toolbar';

@Component({...})
export class AppComponent {
  private featureFlagsService = inject(ToolbarFeatureFlagService);

  constructor() {
    this.featureFlagsService.setAvailableOptions([
      { id: 'darkMode', name: 'Dark Mode', isEnabled: false },
      { id: 'betaFeatures', name: 'Beta Features', isEnabled: true },
    ]);
  }
}

Subscribe to forced values:

this.featureFlagsService.getForcedValues().subscribe((flags) => {
  // Apply forced flag states
});

Permissions

import { ToolbarPermissionsService } from 'ngx-dev-toolbar';

@Component({...})
export class AppComponent {
  private permissionsService = inject(ToolbarPermissionsService);

  constructor() {
    this.permissionsService.setAvailableOptions([
      { id: 'can-edit', name: 'Can Edit', isGranted: false },
      { id: 'is-admin', name: 'Admin Access', isGranted: false },
    ]);
  }
}

App Features

Test product-level feature availability like license tiers and subscription features:

import { ToolbarAppFeaturesService } from 'ngx-dev-toolbar';

@Component({...})
export class AppComponent {
  private appFeaturesService = inject(ToolbarAppFeaturesService);

  constructor() {
    this.appFeaturesService.setAvailableOptions([
      { id: 'analytics', name: 'Advanced Analytics', isEnabled: false },
      { id: 'multi-user', name: 'Multi-User Support', isEnabled: true },
    ]);
  }
}

i18n

Simulate complete internationalization environments — locale, timezone, currency, unit system, pseudo-localization, and RTL:

import { ToolbarI18nService } from 'ngx-dev-toolbar';

@Component({...})
export class AppComponent {
  private i18nService = inject(ToolbarI18nService);

  constructor() {
    // Set available locales
    this.i18nService.setAvailableOptions([
      { code: 'en', name: 'English' },
      { code: 'es', name: 'Spanish' },
      { code: 'ar', name: 'Arabic' },
    ]);

    // React to locale changes
    this.i18nService.getForcedValues().subscribe((locales) => {
      // Apply forced locale
    });

    // React to timezone, currency, and other i18n settings
    this.i18nService.getForcedTimezone().subscribe((tz) => { /* e.g. 'Asia/Tokyo' */ });
    this.i18nService.getForcedCurrency().subscribe((cur) => { /* e.g. 'JPY' */ });
  }
}

The i18n tool also supports pseudo-localization for spotting hardcoded strings and RTL simulation for layout testing. See the full docs for the complete API reference.

Creating Custom Tools

Build your own toolbar tools using the exported UI components. Here's a complete Notes tool:

import { ChangeDetectionStrategy, Component, inject, signal } from '@angular/core';
import { Injectable } from '@angular/core';
import {
  ToolbarToolComponent,
  ToolbarWindowOptions,
  ToolbarButtonComponent,
  ToolbarInputComponent,
  ToolbarListComponent,
  ToolbarListItemComponent,
  ToolbarStepViewComponent,
  ToolbarStepDirective,
} from 'ngx-dev-toolbar';

// 1. Define your model
interface Note {
  id: string;
  title: string;
  content: string;
}

// 2. Create a service with signal-based state
@Injectable({ providedIn: 'root' })
class NotesService {
  private readonly _notes = signal<Note[]>([]);
  readonly notes = this._notes.asReadonly();

  add(title: string, content: string): void {
    this._notes.update(notes => [
      ...notes,
      { id: crypto.randomUUID(), title, content },
    ]);
  }

  remove(id: string): void {
    this._notes.update(notes => notes.filter(n => n.id !== id));
  }
}

// 3. Build the component
@Component({
  selector: 'app-notes-tool',
  standalone: true,
  imports: [
    ToolbarToolComponent,
    ToolbarButtonComponent,
    ToolbarInputComponent,
    ToolbarListComponent,
    ToolbarListItemComponent,
    ToolbarStepViewComponent,
    ToolbarStepDirective,
  ],
  template: `
    <ndt-toolbar-tool [options]="windowOptions" title="Notes" icon="edit">
      <ndt-step-view
        [currentStep]="viewMode()"
        defaultStep="list"
        (back)="viewMode.set('list')"
      >
        <!-- List view -->
        <ng-template ngtStep="list">
          <ndt-button (click)="viewMode.set('create')" icon="edit">
            Add Note
          </ndt-button>
          <ndt-list
            [hasItems]="notesService.notes().length > 0"
            emptyMessage="No notes yet"
            emptyHint="Click 'Add Note' to create one"
          >
            @for (note of notesService.notes(); track note.id) {
              <ndt-list-item [label]="note.title">
                <ndt-button
                  variant="icon"
                  icon="trash"
                  ariaLabel="Delete"
                  (click)="notesService.remove(note.id)"
                />
              </ndt-list-item>
            }
          </ndt-list>
        </ng-template>

        <!-- Create view -->
        <ng-template ngtStep="create" stepTitle="New Note">
          <ndt-input [(value)]="newTitle" placeholder="Title" ariaLabel="Note title" />
          <ndt-input [(value)]="newContent" placeholder="Content" ariaLabel="Note content" />
          <ndt-button (click)="onCreate()" label="Save" />
        </ng-template>
      </ndt-step-view>
    </ndt-toolbar-tool>
  `,
  changeDetection: ChangeDetectionStrategy.OnPush,
})
export class NotesToolComponent {
  protected readonly notesService = inject(NotesService);

  viewMode = signal<'list' | 'create'>('list');
  newTitle = signal('');
  newContent = signal('');

  readonly windowOptions: ToolbarWindowOptions = {
    id: 'notes',
    title: 'Notes',
    description: 'Quick development notes',
    size: 'medium',
  };

  onCreate(): void {
    if (this.newTitle()) {
      this.notesService.add(this.newTitle(), this.newContent());
      this.newTitle.set('');
      this.newContent.set('');
      this.viewMode.set('list');
    }
  }
}

Exported UI Components

ComponentSelectorPurpose
ToolbarToolComponentndt-toolbar-toolWindow wrapper with positioning and animations
ToolbarButtonComponentndt-buttonButtons with optional icon
ToolbarInputComponentndt-inputText inputs with two-way binding
ToolbarSelectComponentndt-selectDropdown selection
ToolbarListComponentndt-listList with empty/no-results states
ToolbarListItemComponentndt-list-itemList items with optional badge
ToolbarCardComponentndt-cardContent container
ToolbarClickableCardComponentndt-clickable-cardInteractive card with icon
ToolbarStepViewComponentndt-step-viewMulti-step view switcher
ToolbarIconComponentndt-icon30+ SVG icons
ToolbarLinkButtonComponentndt-link-buttonExternal link button

For a complete guide, see: Create a Custom Tool

Configuration

Configure which tools are visible:

provideToolbar({
  enabled: isDevMode(),
  showI18nTool: true,
  showFeatureFlagsTool: true,
  showAppFeaturesTool: true,
  showPermissionsTool: true,
  showPresetsTool: true,
})

Keyboard Shortcuts

  • Ctrl+Shift+D: Toggle toolbar visibility

Features

  • Zero Bundle Impact: Dynamic imports exclude toolbar from production builds
  • Persistent State: Settings persist across page reloads
  • No Template Changes: Toolbar attaches automatically to the DOM
  • Extensible: Create custom tools to fit your workflow

Documentation

For full documentation, visit: https://alfredoperez.github.io/ngx-dev-toolbar/

Contributing

We welcome contributions! Please see our contributing guidelines for details.

Support

License

This project is licensed under the MIT License - see the LICENSE file for details.


LinkedIn Bluesky GitHub Stars