Angular Components & Templates

Components are the building blocks of every Angular application: a TypeScript class holding state and logic, a template defining its view, and styles scoped to it. Modern Angular components are standalone (no NgModules required), use built-in control flow (@if, @for, @switch) instead of structural directives, lazy-load parts of the view with @defer, and increasingly rely on signals for inputs and state.

These changes, arriving across Angular 14–19 and now the defaults, make Angular components more concise, faster to render, and closer in feel to other modern frameworks, while keeping Angular's strengths: strong typing, dependency injection, and a batteries-included toolchain.

TL;DR

Quick Example

Using it, with a list and deferred reviews:

Core Concepts

Standalone Components

Since Angular 19, components are standalone by default: each lists the components, directives, and pipes it uses in imports. Applications bootstrap with bootstrapApplication(AppComponent, { providers: [...] }), and routes lazy-load components directly with loadComponent. NgModules still work for existing code, but new code doesn't need them.

Template Binding Syntax

Templates are type-checked in strict mode, which catches typos and wrong input types at build time.

Built-In Control Flow

The built-in syntax replaced *ngIf, *ngFor, and ngSwitch: it's faster, needs no imports, and narrows types better. A schematic migrates existing templates.

Deferrable Views

@defer splits part of a template, and its dependencies, into a separate lazy chunk:

It's one of the easiest wins for initial load performance. See web performance.

Content Projection

<ng-content /> renders content passed between a component's tags, and select creates named slots (<ng-content select="[card-footer]" />). For more dynamic cases, ng-template with ngTemplateOutlet passes templates as inputs.

Lifecycle Hooks

ngOnInit (after the first input values), ngOnChanges (input changes, less needed with signal inputs), ngAfterViewInit (view children available), ngOnDestroy (cleanup), and the newer afterNextRender / afterRender for DOM work that must run after rendering. For cleanup, DestroyRef and takeUntilDestroyed() avoid manual unsubscribe logic.

Change Detection

With ChangeDetectionStrategy.OnPush, a component is checked only when its inputs change by reference, an event fires within it, a signal it reads changes, or it's explicitly marked. Combined with signals and immutable data, OnPush (or zoneless mode) dramatically reduces unnecessary work.

Component Design Patterns

For shared UI libraries, Angular CDK and Angular Material provide accessible primitives (overlays, drag and drop, virtual scrolling, a11y utilities). See design systems.

Best Practices

Keep Components Small and Focused

One responsibility per component, with presentational components free of injected data services. Big "page" components should compose smaller ones.

Always Use track Wisely

Track by a stable unique ID, not by object identity or $index when items can be reordered or replaced. Good tracking prevents unnecessary DOM re-creation and lost component state.

Default to OnPush and Signals

New components should use signal inputs, computed for derived values, and OnPush or zoneless change detection. It's faster, and it forces clearer data flow.

Use NgOptimizedImage and @defer

NgOptimizedImage enforces image dimensions, lazy loading, and priority hints for LCP images, and @defer removes non-critical UI from the initial bundle. Both are low-effort, high-impact performance improvements.

Common Mistakes

Calling Expensive Methods in Templates

Use a computed signal or a pure pipe, which is memoized and recomputed only when inputs change.

Mutating Inputs Under OnPush

Pushing into an array input doesn't change its reference, so OnPush children don't update. Pass new arrays and objects, or use signals.

Forgetting to Import Dependencies in Standalone Components

Using a pipe or child component without adding it to imports causes template errors ("is not a known element"). The Angular language service and compiler point to the missing import.

FAQ

Do I still need NgModules in Angular?

No, not for new code. Standalone components, directives, and pipes declare their own dependencies, and applications bootstrap and lazy-load without modules. NgModules remain supported for existing apps and some libraries.

Why is track required in @for?

It tells Angular how to identify each item across renders, so it can move, add, or remove only the changed DOM nodes. Making it required prevents a common performance pitfall of the old *ngFor (missing trackBy), which re-created DOM unnecessarily.

What does @defer do?

It lazy-loads a section of a template, including its components, directives, and pipes, as a separate JavaScript chunk, rendering it when a trigger fires (in viewport, on interaction, idle, or timer). Placeholder and loading blocks handle the interim UI, which reduces initial bundle size and improves load performance.

When should I use OnPush change detection?

For essentially all new components. With signals and immutable data, OnPush avoids checking components whose inputs haven't changed. It's the natural companion to zoneless Angular.

Related Topics

References