Angular Components — Building Blocks Guide
Components
Components are one of the basic building blocks of any Angular application. In Angular 21, all new components are standalone by default - they do not require an NgModule. A component contains HTML to display a view, CSS to style it, and TypeScript to control it. A component is created using the @Component() decorator, which includes a standalone: true property by default and holds the following properties:-
moduleId: [ ]
If it set, the templateUrl and styleUrl are resolved relative to the angular component.
viewProviders: [ ]
List of dependency injection providers scoped to this or current component's view.
template: [ ]
It contains the inline template of the component's view.
templateUrl: [ ]
It contains the external template URL of the component's view.
styles: [ ]
It contains the list of inline CSS styles for styling the component's view.
styleUrls: [ ]
It contains the external stylesheet URLs for styling the component's view.
import { Component, signal } from '@angular/core';
@Component({
selector: 'app-root',
standalone: true,
template: `
<div style="text-align:center">
<h1>Welcome to {{ title() }}!</h1>
<p>Edit this component to get started.</p>
</div>
`,
styles: [`
h1 { color: green; }
`]
})
export class AppComponent {
title = signal('my-angular-app');
}
Component Lifecycle Hooks
A Angular component/directive has a lifecycle that provide visibility into these key life moments and the ability to act when they occur. Once component/directive is getting created, Angular calls the constructor first, and then it calls the lifecycle hook methods in the following sequence.
| Hook | Purpose and Timing |
|---|---|
| ngOnChanges() | Called before ngOnInit() every time, as soon as a bound input property changes. |
| ngOnInit() | Called after ngOnChanges() once the angular component is initialized. |
| ngDoCheck() | Called immediately after ngOnChanges() and ngOnInit() during every change detection run. |
| ngAfterContentInit() | Called once after the first ngDoCheck() and as soon as an angular performs any content projection into the view. |
| ngAfterContentChecked() | Called every time after the ngAfterContentInit() and every subsequent ngDoCheck() as soon as the projected content has been checked. |
| ngAfterViewInit() | Called once after the first ngAfterContentChecked() and as soon as the component view and its child views has been initialized. |
| ngAfterViewChecked() | Called after the ngAfterViewInit() and every subsequent ngAfterContentChecked() and every time the component view and its child views have been checked. |
| ngOnDestroy() | Called once the component is about to destroyed. It unsubscribe Observables and detach event handlers to avoid memory leaks. |
import {
Component, OnInit, OnChanges, DoCheck,
AfterContentInit, AfterContentChecked,
AfterViewInit, AfterViewChecked, OnDestroy,
signal, input, SimpleChanges
} from '@angular/core';
@Component({
selector: 'app-lifecycle',
standalone: true,
template: `<p>Check the console for lifecycle hook output.</p>`
})
export class LifecycleComponent implements OnInit, OnChanges, DoCheck,
AfterContentInit, AfterContentChecked, AfterViewInit, AfterViewChecked, OnDestroy {
name = input('Angular');
ngOnChanges(changes: SimpleChanges) { console.log('ngOnChanges', changes); }
ngOnInit() { console.log('ngOnInit'); }
ngDoCheck() { console.log('ngDoCheck'); }
ngAfterContentInit() { console.log('ngAfterContentInit'); }
ngAfterContentChecked() { console.log('ngAfterContentChecked'); }
ngAfterViewInit() { console.log('ngAfterViewInit'); }
ngAfterViewChecked() { console.log('ngAfterViewChecked'); }
ngOnDestroy() { console.log('ngOnDestroy'); }
}
Standalone Components (Angular 21 Default)
In Angular 21, all new components are standalone by default. Standalone components do not require an NgModule - they declare their own dependencies directly in the imports array of the @Component decorator. This simplifies the application structure significantly.
Signal-based Component Inputs (Angular 21)
Angular 21 introduces signal-based component APIs as the modern way to define inputs, outputs, and two-way bindings:
import { Component, input, output } from '@angular/core';
@Component({
selector: 'app-greeting',
standalone: true,
template: `
<p>Hello, {{ name() }}!</p>
<button (click)="greet.emit(name())">Say Hi</button>
`
})
export class GreetingComponent {
name = input.required<string>(); // required signal input
greet = output<string>(); // signal output
}
import { Component } from '@angular/core';
import { GreetingComponent } from './greeting.component';
@Component({
selector: 'app-root',
standalone: true,
imports: [GreetingComponent],
template: `
<app-greeting
name="Angular 21"
(greet)="onGreet($event)"
/>
<p>{{ message }}</p>
`
})
export class AppComponent {
message = '';
onGreet(name: string) {
this.message = \`Greeted: \${name}\`;
}
}
Level Up Your Angular Skills
Master Angular with these hand-picked resources
- Angular components are the building blocks of every Angular app - each has a template, class, and styles.
- Standalone components (default in Angular 17+) do not need NgModule - import dependencies directly.
-
Use
@Input()to pass data into a component;@Output()with EventEmitter to emit events up. -
In Angular 17+, use
input()signal andoutput()function instead of decorators. -
The
@Componentselector defines the HTML tag used to render the component. -
Use
OnPushchange detection strategy for better performance in large apps. - Component lifecycle: constructor -> ngOnChanges -> ngOnInit -> ngDoCheck -> ngAfterViewInit -> ngOnDestroy.
@Input() data: any
@Input({ required: true }) data!: string
Subscribing to observables in ngOnInit without unsubscribing
Use takeUntilDestroyed() or async pipe
Putting business logic in the component class
Move business logic to a service