Building Reusable UI Components & Design Patterns: reusable card, modal and alert components; component interaction with RxJS and Shared Services; ng-template, ng-container, ng-content; Smart vs Dumb Components

Good Angular apps are built from reusable components: a card, modal, or alert you write once and use everywhere, made flexible with content projection, and organised into smart components that fetch data and dumb components that just display it.

11 min read · 8 cards · 2 checks

Read in: English · हिन्दी · ગુજરાતી


Theory

Write once, use everywhere

FestConnect shows cards for events, pops up modals to confirm actions, and flashes alerts for messages, over and over across many pages. Writing that markup fresh each time would be wasteful and inconsistent. The Angular answer is reusable components: build a Card, a Modal, an Alert once, and drop them in wherever needed.

This lesson covers how to make components genuinely reusable with content projection, and how to organise them using the smart versus dumb pattern. These habits keep a growing app consistent and maintainable.

Theory

Content projection makes components flexible

A reusable Card is only useful if each caller can put different content inside it. That is what content projection provides.

ng-content acts as a slot: whatever markup the caller places inside your component's tags is projected into the template where ng-content sits. So one CardComponent can wrap an event, a profile, or a message, each caller supplies the inside. ng-template defines a chunk of template that is not rendered until you choose to use it (handy for modals and conditional content), and ng-container is an invisible grouping element that adds no extra DOM. Together they let you build flexible, reusable UI shells.

Practical

A reusable Card using ng-content (a slot)

@Component({
  selector: 'app-card',
  standalone: true,
  template: `
    <div class="card">
      <ng-content></ng-content>   <!-- caller's markup is projected here -->
    </div>
  `,
})
export class CardComponent {}

// Usage: whatever you put inside <app-card> appears in the slot:
// <app-card><h3>Robotics Workshop</h3><p>Sat 10am</p></app-card>

Theory

Smart vs dumb components

A useful pattern splits components by responsibility.

A smart (container) component knows about data and logic: it fetches events from a service, holds state, and handles actions. A dumb (presentational) component knows only how to display what it is given: it receives data through @Input and reports user actions through @Output, with no idea where the data came from.

So a smart EventsPage fetches the events and passes each to a dumb EventCard for display. Dumb components are highly reusable (they make no assumptions) and easy to test; smart components wire them to the app. For communication across distant components, they share state through RxJS in a shared service (as with the current user).

Practical

A dumb component: inputs in, events out

@Component({ selector: 'app-event-card', standalone: true,
  template: `<div (click)="select.emit(event)">{{ event.name }}</div>` })
export class EventCardComponent {
  @Input() event!: { name: string };     // data comes IN from the parent
  @Output() select = new EventEmitter();  // actions go OUT to the parent
}
// It knows nothing about services or where 'event' came from: pure display.
// A smart parent supplies [event]="..." and handles (select)="...".

Quiz

In the smart vs dumb pattern, what characterises a dumb (presentational) component?

  1. It fetches its own data from services and holds business logic
  2. It only displays data received via @Input and emits events via @Output, with no knowledge of where the data comes from
  3. It cannot be reused anywhere
  4. It is written in plain JavaScript, not TypeScript
Show the answer

It only displays data received via @Input and emits events via @Output, with no knowledge of where the data comes from

A dumb (presentational) component simply displays the data it receives through @Input and reports user actions through @Output, without knowing where the data originates, which makes it highly reusable and easy to test. Option A describes a SMART (container) component, which fetches data from services and holds logic. Option C is the opposite of the truth: dumb components are the MOST reusable precisely because they make no assumptions about data sources. Option D is wrong: all Angular components are TypeScript. The split is by responsibility: smart components handle data and logic, dumb components handle display.

Think first

Why separate components into smart and dumb at all?

Why not just let each component fetch its own data and display it? What does the smart/dumb split buy you? Then tap.

Show the answer

It buys REUSABILITY, TESTABILITY, and CLARITY by separating two different concerns, getting data and showing data, that tend to change for different reasons. A dumb component that only takes inputs and emits outputs can be reused ANYWHERE, because it makes no assumptions about services, APIs, or app state; the same EventCard can display an event from a database, from a search result, or from a test fixture, since all it needs is an event object passed in. That also makes it trivial to TEST: you hand it some data and check what it renders, with no need to mock services or network calls. Smart components, meanwhile, concentrate the messy parts, fetching, state, and wiring, in a few places, so the data flow of the app is easy to follow (data comes down through inputs, events bubble up through outputs). If instead every component fetched its own data, you would scatter API calls throughout the UI, duplicate logic, make components hard to reuse (each is tied to a specific data source), and make testing painful (every component needs its services mocked). The smart/dumb pattern is really an application of separation of concerns to the component tree, the same principle you have met throughout, and it keeps a large Angular app maintainable as it grows. Fetch in a few smart places, display in many reusable dumb ones.

Summary

Key takeaways

  • Reusable components (Card, Modal, Alert) are written once and used across the app for consistency.
  • Content projection makes them flexible: ng-content is a slot for the caller's markup.
  • ng-template defines template not rendered until used; ng-container groups without adding DOM.
  • Smart (container) components fetch data and hold logic; dumb (presentational) components only display via @Input and emit via @Output.
  • Dumb components are highly reusable and easy to test because they know nothing about data sources.
  • Distant components share changing state through RxJS in a shared service.
  • Memory hook: project content for flexibility; smart components fetch, dumb components display.

Study this properly

This page is the lesson to read. In Gri-Learn the same topic is a graded deck: the self-checks are scored and your weak topics are tracked. Free to start.

Start this topic

Already have an account? Sign in

More from Fundamentals of Angular (v17) for Single Page Applications

Gri-Learn · syllabus-mapped B.C.A. lessons in English, Hindi and Gujarati

Building Reusable UI Components & Design Patterns: reusable card, modal and alert components; component interaction with RxJS and Shared Services; ng-template, ng-container, ng-content; Smart vs Dumb Components · Fundamentals of Full Stack Web Development (Major-15-01) · Gri-Learn