Angular 106 🅰️ Angular DevTools
Angular DevTools is a browser extension that provides debugging and profiling capabilities specifically for Angular applications. It integrates directly into Chrome and Firefox developer tools, adding an “Angular” tab that exposes the framework’s internal state in a way that generic browser DevTools cannot. The extension is maintained by the Angular team and is available for free on the Chrome Web Store and Firefox Add-ons .
The previous chapters covered testing and performance optimization. This chapter covers the tool that makes those disciplines practical. A test tells you whether the application works. The profiler tells you why it is slow. The component inspector tells you what state the component is in. Angular DevTools is the window into the running application — the tool you reach for when a component is not rendering, when change detection is running too often, or when a service is being resolved from the wrong injector.
Key point: Angular DevTools works exclusively with development builds. When Angular compiles for production, it removes the debug hooks that DevTools uses to communicate with the application. If you open the extension and see “We detected an application built with production configuration,” the application was compiled with optimizations enabled. Run ng serve or disable optimization in the build configuration to use the tool .
Why Angular DevTools exists
Generic browser DevTools show you the DOM, the network, and the JavaScript call stack. They do not show you which component owns a piece of DOM, what the component’s input values are, which injector resolved a service, or how long Angular spent running change detection. Angular DevTools fills that gap.
The component tree problem. A DOM element is not a component. A <div> might be the host element of a component, a directive, or neither. The browser DevTools show the DOM tree. Angular DevTools shows the component tree — the hierarchy of components and directives that Angular actually instantiated. The two trees are related but not identical. The component tree is the one that matters for debugging Angular behavior .
The state problem. A component’s properties are not visible in the DOM unless they are rendered. A boolean flag that controls a conditional, a list that drives an @for, a signal that feeds a computed — these live in the component instance, not in the DOM. Angular DevTools shows the properties of the selected component and lets you edit them in real time. Change a boolean, and the template updates .
The change detection problem. Angular runs change detection on every event, every timer, every HTTP response. The profiler records each cycle, shows how long it took, which components were checked, and what triggered it. Without the profiler, performance problems are guesswork. With it, the slow component is visible in the flame graph .
The injector problem. Angular’s dependency injection resolves services from a hierarchy of injectors. When a service is resolved from the wrong injector, the behavior is wrong, but the reason is invisible. The Injector Tree tab shows the environment hierarchy and the element hierarchy, and highlights the resolution path for the selected service .
The trade-off. Angular DevTools is a development tool. It does not work in production. It requires a development build. It adds a browser extension to the debugging environment. The alternative is instrumenting the application with console logs and manual timing. The extension is simpler, more precise, and less invasive.
a. The Components Tab
The Components tab is the primary interface for inspecting the application’s structure. It displays a hierarchical tree of the components and directives that Angular instantiated, mirroring the component hierarchy of the running application .
The tree can be navigated with the mouse or the keyboard. The up and down arrows select the previous and next nodes. The left and right arrows collapse and expand a node. A search box above the tree finds components by name. Pressing Enter jumps to the next match; Shift+Enter jumps to the previous match .
When a component is selected, its properties and metadata appear in the panel on the right. The properties include the inputs, the outputs, and the internal state of the component. The metadata includes the component’s selector, its change detection strategy, and other configuration. Right-clicking a property value opens an editor. Type a new value and press Enter to apply it. The template updates immediately, which is useful for testing conditional branches or edge cases without modifying the source .
Double-clicking a component in the tree navigates to its host DOM node in the browser’s Elements tab. This is the fastest way to see the relationship between the component and the DOM it renders. For components, an icon at the top-right of the properties view navigates to the component definition in the Sources tab .
The console integration is one of the most practical features. Angular DevTools exposes the most recently selected component instances as global variables. $ng0 is the currently selected component. $ng1 is the previously selected one. $ng2 is the one before that. These references let you interact with the component from the console — call methods, inspect nested properties, or trigger behavior manually .
The Inspect element icon in the top-left corner of the Angular tab lets you select a component by clicking its DOM element. Hover over an element, and the extension identifies the associated directives and components. Click to select the corresponding node in the component tree .
b. The Profiler Tab
The Profiler tab records and visualizes Angular’s change detection. It answers the question “Why is the application slow?” by showing exactly where the time is spent .
To start a recording, hover over the circle in the top-left corner and click Start recording. Interact with the application to trigger change detection. Click Stop recording to finish. The recorded profile appears as a sequence of bars. Each bar represents one change detection cycle. The height of the bar represents the time Angular spent in that cycle. Selecting a bar shows a bar chart of the components and directives that were checked during that cycle, sorted by the time they consumed. The panel also shows the estimated frame rate, which tells you whether the cycle would have caused a dropped frame at 60fps .
The flame graph view provides a different perspective. Each tile in the graph represents an element at a specific position in the render tree. The x-axis is the total time for the cycle. The y-axis is the element hierarchy. The color intensity indicates how much time Angular spent in that tile relative to the slowest tile. Double-clicking a tile zooms into its children. This view makes it easy to see which branch of the component tree is responsible for the bulk of the work .
The “Change detection” checkbox above the flame graph filters the view. When checked, it highlights only the components that actually went through change detection and grays out those that were skipped. This is particularly useful for debugging OnPush components. An OnPush component that did not re-render appears gray because Angular skipped it. If you expected it to update but it did not, the gray tile tells you that the component was not marked dirty .
The profiler also shows the source that triggered the change detection. This tells you whether the cycle was caused by a user event, a timer, an HTTP response, or something else. Knowing the trigger is often the first step in reducing unnecessary cycles .
Recordings can be exported as JSON files with the Save Profile button and imported later. This is useful for sharing a profile with a colleague or comparing a profile before and after a fix .
c. The Injector Tree
The Injector Tree tab visualizes Angular’s dependency injection hierarchy. It is available for applications built with Angular 17 or later .
The tab shows two trees. The environment hierarchy represents the injectors configured at the module or application level. The element hierarchy represents the injectors configured on elements and components. When you select an injector, the path that Angular’s dependency injection algorithm traverses from that injector to the root is highlighted. For element injectors, the path includes the environment injectors that the algorithm jumps to when a dependency cannot be resolved in the element hierarchy .
Clicking an injector that has configured providers displays those providers on the right. The list shows the token and its type. This is useful for debugging a service that is resolved from the wrong level of the hierarchy. If a component is resolving a service from the root injector when it should resolve a component-level provider, the Injector Tree shows the path and the provider list makes the mistake visible .
The Injector Tree is also useful for understanding why a service instance is shared or not shared. A service provided at the root is a singleton. A service provided at the component level is a new instance per component. The tree shows which injector owns the provider, and the resolution path shows where the service comes from .
d. The Chrome DevTools Integration
Angular and the Chrome team have collaborated to bring Angular-specific data directly into the Chrome DevTools performance panel. This integration creates a custom “Angular” track alongside Chrome’s standard performance tracks, correlating Angular’s framework-level events with the browser’s lower-level function calls .
The integration is enabled by running ng.enableProfiling() in the browser console, or by calling enableProfiling() from @angular/core during application bootstrap. The profiling works exclusively in development mode. Calling enableProfiling() before bootstrapApplication captures all startup events .
The Angular track uses color coding to distinguish task types. Blue represents TypeScript code written by the application developer — service methods, component constructors, lifecycle hooks. Purple represents templates that the Angular compiler has transformed. Green represents entry points and dependency injection services instantiated during bootstrap. The color coding makes it immediately clear whether a given block of time was spent in application code, template rendering, or framework initialization .
The integration also shows the number of synchronization passes for a change detection cycle. A single pass is normal. More than one pass suggests that state is being updated during change detection. This is a performance anti-pattern because it slows down page updates and can, in the worst case, cause infinite loops. The Angular track makes this visible in the profile .
Complete Example Session
This session demonstrates the Angular DevTools workflow: inspecting a component, profiling change detection, and resolving an injector.
// ============================================
// PART 1: THE APPLICATION
// ============================================
// A small Angular application with a slow component.
@Component({
selector: 'app-slow-list',
template: `
<ul>
@for (item of items(); track item.id) {
<li>{{ expensiveComputation(item) }}</li>
}
</ul>
`,
})
export class SlowListComponent {
items = signal<Item[]>(generateItems(1000));
expensiveComputation(item: Item): string {
// A deliberately slow computation
let result = '';
for (let i = 0; i < 10000; i++) {
result += item.name.charAt(i % item.name.length);
}
return result;
}
}
// ============================================
// PART 2: INSTALL AND OPEN DEVTOOLS
// ============================================
// 1. Install Angular DevTools from the Chrome Web Store.
// 2. Run the application: ng serve
// 3. Open the browser DevTools: F12 or Cmd+Option+I
// 4. Click the "Angular" tab.
// The Components tab shows the component tree.
// The Profiler tab shows the change detection timeline.
// ============================================
// PART 3: INSPECT THE COMPONENT
// ============================================
// In the Components tab:
// 1. Search for "SlowList".
// 2. Click the component in the tree.
// 3. The properties panel shows the items signal.
// In the console:
$ng0.items()
// Output: the array of items
$ng0.expensiveComputation($ng0.items()[0])
// Output: the computed string
// ============================================
// PART 4: EDIT A PROPERTY
// ============================================
// In the properties panel:
// 1. Right-click the items signal value.
// 2. Edit the array to contain fewer items.
// 3. Press Enter.
// The template updates immediately.
// The list renders fewer items.
// This verifies the @for tracking without changing the source.
// ============================================
// PART 5: PROFILE CHANGE DETECTION
// ============================================
// 1. Click the Profiler tab.
// 2. Click Start recording.
// 3. Interact with the application (click a button, scroll).
// 4. Click Stop recording.
// The timeline shows a sequence of bars.
// The tallest bar is the slow cycle.
// ============================================
// PART 6: ANALYZE THE SLOW CYCLE
// ============================================
// Click the tallest bar.
// The bar chart shows SlowListComponent at the top.
// The details show the time spent in the template.
// The expensiveComputation method is called 1000 times per cycle.
// ============================================
// PART 7: THE FLAME GRAPH
// ============================================
// Switch to the flame graph view.
// The SlowListComponent tile is the darkest.
// Double-click to zoom in.
// The @for loop renders 1000 <li> elements.
// Each element calls expensiveComputation.
// ============================================
// PART 8: THE FIX
// ============================================
// The fix is to precompute the values.
@Component({
selector: 'app-fast-list',
template: `
<ul>
@for (item of computedItems(); track item.id) {
<li>{{ item.displayName }}</li>
}
</ul>
`,
})
export class FastListComponent {
private items = signal<Item[]>(generateItems(1000));
computedItems = computed(() =>
this.items().map(item => ({
id: item.id,
displayName: this.expensiveComputation(item),
}))
);
}
// ============================================
// PART 9: PROFILE THE FIX
// ============================================
// Record another profile.
// The slow cycle is gone.
// The computedItems signal caches the result.
// The template reads the cached displayName.
// ============================================
// PART 10: THE INJECTOR TREE
// ============================================
// In the Components tab, select a component.
// Click the Injector Tree tab.
// The element hierarchy shows the component's injector.
// The environment hierarchy shows the root injector.
// Select a service to see the resolution path.
// ============================================
// PART 11: THE CHROME INTEGRATION
// ============================================
// In the browser console:
ng.enableProfiling()
// Record a performance profile in the Chrome Performance panel.
// The "Angular" track appears alongside the standard tracks.
// Blue bars: your TypeScript code.
// Purple bars: templates.
// Green bars: DI services and entry points.
// ============================================
// PART 12: THE DEBUG WORKFLOW
// ============================================
// 1. Open the Angular tab.
// 2. Use the Components tab to inspect state.
// 3. Use the Profiler tab to find slow cycles.
// 4. Use the flame graph to find slow components.
// 5. Use the Injector Tree to debug DI.
// 6. Use the Chrome integration for deep analysis.
// 7. Fix the issue.
// 8. Profile again to verify the fix.
The twelve parts cover the application, installing and opening DevTools, inspecting the component, editing a property, profiling change detection, analyzing the slow cycle, the flame graph, the fix, profiling the fix, the Injector Tree, the Chrome integration, and the debug workflow.
Quick Reference
The Angular DevTools Tabs
| Tab | Purpose |
|---|---|
| Components | Inspect component tree and state |
| Profiler | Record and visualize change detection |
| Injector Tree | Visualize dependency injection hierarchy |
The Console Shortcuts
| Shortcut | Purpose |
|---|---|
$ng0 | Currently selected component |
$ng1 | Previously selected component |
$ng2 | Component selected before that |
The Profiler Views
| View | Purpose |
|---|---|
| Timeline | Change detection cycles as bars |
| Bar chart | Components sorted by time |
| Flame graph | Hierarchical time distribution |
| Change detection filter | Highlight checked components |
The Chrome Integration
| Feature | Purpose |
|---|---|
ng.enableProfiling() | Enable Angular track in Chrome |
| Blue bars | Application TypeScript code |
| Purple bars | Compiled templates |
| Green bars | DI services and entry points |
The Error Messages
| Message | Meaning |
|---|---|
| “Angular application not detected” | No Angular app on the page |
| “Production configuration” | Compiled with optimizations |
Best Practices
✅ Do This:
// Use $ng0 to access the selected component in the console
$ng0.items()
// Use the Profiler to find slow change detection cycles
// Record, interact, stop, analyze
// Use the flame graph to find slow components
// Double-click to zoom into the hierarchy
// Use the Injector Tree to debug service resolution
// Select the injector, view the resolution path
❌ Don’t Do This:
// Don't expect DevTools to work in production
// Run ng serve or disable optimization
// Don't edit properties in production
// The changes are for debugging only
// Don't ignore the "Change detection" filter
// Use it to debug OnPush components
Common Pitfalls
| Pitfall | Why It Happens | Fix |
|---|---|---|
| “Angular application not detected” | Wrong page or no Angular app | Check the page |
| “Production configuration” | Optimizations enabled | Run ng serve |
| Profiler shows no data | No change detection triggered | Interact with the app |
| Flame graph confusing | Too many components | Use the filter |
| Injector Tree missing | Angular < 17 | Upgrade Angular |
Real-World Examples
1. Open DevTools
# F12 or Cmd+Option+I
2. Select a Component
# Click in the component tree
3. Access in Console
$ng0
4. Edit a Property
# Right-click, edit, Enter
5. Start Profiling
# Click Start recording
6. Analyze a Cycle
# Click the tallest bar
7. View the Flame Graph
# Switch to flame graph view
8. Debug OnPush
# Check the "Change detection" filter
9. Injector Tree
# Click the Injector Tree tab
10. Chrome Integration
ng.enableProfiling()
Visual
The Angular DevTools Interface
┌──────────────────────────────────────────────┐
│ ANGULAR TAB │
│ │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────┐ │
│ │ Components │ │ Profiler │ │ Injector│ │
│ │ │ │ │ │ Tree │ │
│ └─────────────┘ └─────────────┘ └─────────┘ │
│ │
│ Components: component tree + properties │
│ Profiler: change detection timeline │
│ Injector Tree: DI hierarchy │
│ │
└──────────────────────────────────────────────┘
The Profiler Timeline
┌──────────────────────────────────────────────┐
│ PROFILER TIMELINE │
│ │
│ █ │
│ █ █ │
│ █ █ █ █ █ │
│ █ █ █ █ █ █ █ █ █ │
│ │
│ Each bar = one change detection cycle │
│ Taller bar = more time spent │
│ Click a bar to see the component breakdown │
│ │
└──────────────────────────────────────────────┘
The Flame Graph
┌──────────────────────────────────────────────┐
│ FLAME GRAPH │
│ │
│ AppComponent │
│ ├─ HeaderComponent │
│ ├─ MainComponent │
│ │ ├─ FastComponent │
│ │ └─ SlowComponent ← dark tile │
│ │ └─ @for loop ← dark tile │
│ └─ FooterComponent │
│ │
│ Darker tiles = more time │
│ Double-click to zoom │
│ │
└──────────────────────────────────────────────┘
The Injector Tree
┌──────────────────────────────────────────────┐
│ INJECTOR TREE │
│ │
│ Environment Hierarchy: │
│ RootInjector │
│ └─ AppModuleInjector │
│ │
│ Element Hierarchy: │
│ AppComponent │
│ └─ ChildComponent │
│ └─ ServiceProvider │
│ │
│ Resolution path highlighted │
│ Providers listed on the right │
│ │
└──────────────────────────────────────────────┘
Summary
| Item | Value |
|---|---|
| Angular DevTools | Browser extension for Angular debugging |
| Platforms | Chrome, Firefox |
| Tabs | Components, Profiler, Injector Tree |
| Console shortcuts | $ng0, $ng1, $ng2 |
| Profiler views | Timeline, bar chart, flame graph |
| Injector Tree | Angular 17+ |
| Chrome integration | ng.enableProfiling() |
| Development only | Yes |
Key takeaways:
- Angular DevTools is a browser extension that provides Angular-specific debugging and profiling. It adds an “Angular” tab to Chrome and Firefox DevTools. The extension is maintained by the Angular team and is available for free .
- The Components tab shows the component tree and lets you inspect and edit component state. The properties panel shows the inputs, outputs, and internal state of the selected component. Right-clicking a property opens an editor for real-time modification. The console shortcuts
$ng0,$ng1, and$ng2expose recently selected component instances . - The Profiler tab records and visualizes change detection cycles. Each bar in the timeline represents a cycle, and its height represents the time spent. The bar chart shows the components sorted by time. The flame graph shows the hierarchical time distribution. The “Change detection” filter highlights components that were checked and grays out those that were skipped .
- The Injector Tree visualizes the dependency injection hierarchy. It shows the environment hierarchy and the element hierarchy. Selecting an injector highlights the resolution path. Clicking an injector shows its providers. The feature is available for Angular 17 and later .
- The Chrome DevTools integration brings Angular data into the performance panel. Running
ng.enableProfiling()enables the Angular track. The track uses color coding: blue for application TypeScript code, purple for templates, green for DI services and entry points. The integration shows synchronization passes and helps identify state updates during change detection . - DevTools works exclusively in development mode. Production builds remove the debug hooks. Running
ng serveor disabling optimization is required. The error message “We detected an application built with production configuration” indicates the application was compiled with optimizations . - The tool is the window into the running application. It shows the component tree, the state, the change detection timing, and the injector hierarchy. It is the first tool to reach for when a component is not rendering, when change detection is slow, or when a service is resolved from the wrong injector.
Remember: Angular DevTools is the debugging and profiling extension for Angular applications. The Components tab shows the component tree and the state. The Profiler tab records change detection and shows where the time goes. The Injector Tree shows the dependency injection hierarchy. The console shortcuts expose component instances. The Chrome integration brings Angular data into the performance panel. The tool works only in development mode. It is the window into the running application, and it is the first tool to reach for when something is not working as expected.
Stop using slow, ad-bloated tool sites! 🤮
🔎 Search “KandZ Tools” on Google to use many professional utilities for free.
KandZ.me is the ultimate minimalist hub for:
✅ Finance (Mortgage, Interest, Inflation)
✅ Tech (Base64, JSON, Dev Suite, IP)
✅ Health (BMI, BMR, TDEE)
✅ Productivity (Timer, Workspace, QR)
⚡️ Fast & Private
🔒 No data leaves your device
💎 100% Free
🔗 Use it now: https://tools.kandz.me
🔖 Bookmark it—you’ll need it later!