Container and presentational components help organize your app by separating logic from display. This makes your code easier to understand and maintain.
Container and presentational components in Angular
/* Presentational component */ import { Component, Input } from '@angular/core'; @Component({ selector: 'app-user-card', standalone: true, template: ` <div> <h2>{{ user.name }}</h2> <p>{{ user.email }}</p> </div> ` }) export class UserCardComponent { @Input() user!: { name: string; email: string }; } /* Container component */ import { Component } from '@angular/core'; import { UserCardComponent } from './user-card.component'; @Component({ selector: 'app-user-container', standalone: true, imports: [UserCardComponent], template: ` <app-user-card [user]="user"></app-user-card> ` }) export class UserContainerComponent { user = { name: 'Alice', email: 'alice@example.com' }; }
Presentational components focus on how things look and receive data via @Input().
Container components handle data and logic, then pass data down to presentational components.
import { Component, Output, EventEmitter } from '@angular/core'; /* Presentational component with event output */ @Component({ selector: 'app-button', standalone: true, template: `<button (click)="clicked.emit()">Click me</button>` }) export class ButtonComponent { @Output() clicked = new EventEmitter<void>(); }
import { Component } from '@angular/core'; import { ButtonComponent } from './button.component'; /* Container listens to presentational event */ @Component({ selector: 'app-button-container', standalone: true, imports: [ButtonComponent], template: `<app-button (clicked)="onClick()"></app-button>` }) export class ButtonContainerComponent { onClick() { alert('Button clicked!'); } }
This example shows a presentational component MessageComponent that only displays a message and a clear button. The container MessageContainerComponent manages the message state and passes it down. It also handles showing and clearing the message.
Accessibility is included with role="alert" and aria-live="polite" so screen readers announce message changes.
import { Component, Input, Output, EventEmitter } from '@angular/core'; @Component({ selector: 'app-message', standalone: true, template: ` <div role="alert" aria-live="polite"> <p>{{ message }}</p> <button (click)="clear.emit()" aria-label="Clear message">Clear</button> </div> ` }) export class MessageComponent { @Input() message = ''; @Output() clear = new EventEmitter<void>(); } @Component({ selector: 'app-message-container', standalone: true, imports: [MessageComponent], template: ` <app-message [message]="currentMessage" (clear)="clearMessage()"></app-message> <button (click)="showMessage()">Show Message</button> ` }) export class MessageContainerComponent { currentMessage = ''; showMessage() { this.currentMessage = 'Hello from container!'; } clearMessage() { this.currentMessage = ''; } }
Use @Input() to pass data from container to presentational components.
Use @Output() with EventEmitter to send events from presentational to container.
Keep presentational components simple and reusable by avoiding data fetching or complex logic inside them.
Container components handle data and logic.
Presentational components focus on displaying data and UI.
This separation makes your app easier to maintain and test.