A development toolbar for Angular 19+ applications that helps developers interact with the application more efficiently.
- Toggle feature flags without backend changes
- Switch languages instantly
- 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!
npm install ngx-dev-toolbarAdd 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.
| Tool | Description |
|---|---|
| Feature Flags | Toggle feature flags on/off during development |
| Permissions | Test permission-based UI without backend changes |
| App Features | Test product features and subscription tiers |
| Language Switcher | Switch between available languages |
| Presets | Save and restore tool configurations |
| Network Mocker | Mock HTTP requests in real-time |
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
});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 },
]);
}
}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 },
]);
}
}import { ToolbarLanguageService } from 'ngx-dev-toolbar';
@Component({...})
export class AppComponent {
private languageService = inject(ToolbarLanguageService);
constructor() {
this.languageService.setAvailableOptions([
{ code: 'en', name: 'English' },
{ code: 'es', name: 'Spanish' },
]);
}
}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');
}
}
}| Component | Selector | Purpose |
|---|---|---|
ToolbarToolComponent |
ndt-toolbar-tool |
Window wrapper with positioning and animations |
ToolbarButtonComponent |
ndt-button |
Buttons with optional icon |
ToolbarInputComponent |
ndt-input |
Text inputs with two-way binding |
ToolbarSelectComponent |
ndt-select |
Dropdown selection |
ToolbarListComponent |
ndt-list |
List with empty/no-results states |
ToolbarListItemComponent |
ndt-list-item |
List items with optional badge |
ToolbarCardComponent |
ndt-card |
Content container |
ToolbarClickableCardComponent |
ndt-clickable-card |
Interactive card with icon |
ToolbarStepViewComponent |
ndt-step-view |
Multi-step view switcher |
ToolbarIconComponent |
ndt-icon |
30+ SVG icons |
ToolbarLinkButtonComponent |
ndt-link-button |
External link button |
For a complete guide, see: Create a Custom Tool
Configure which tools are visible:
provideToolbar({
enabled: isDevMode(),
showLanguageTool: true,
showFeatureFlagsTool: true,
showAppFeaturesTool: true,
showPermissionsTool: true,
showPresetsTool: true,
})- Ctrl+Shift+D: Toggle toolbar visibility
- 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
For full documentation, visit: https://alfredoperez.github.io/ngx-dev-toolbar/
We welcome contributions! Please see our contributing guidelines for details.
This project is licensed under the MIT License - see the LICENSE file for details.
