← Back to the seriesThe Galactic Guide to Angular · 3 / 10

Components: Angular’s building blocks

Components: Angular’s building blocks

Welcome to chapter three! The development environment is ready, so from here on we write code. We start with Angular's heart: components.


In the first chapter, I said that in Angular "everything revolves around components". Now I will show what that means in practice: by the end, you will know how to create one, when it makes sense to do so, and how to think in components when structuring an application.


🧩 What is a component?


A component is a reusable piece of interface that brings together three things:


  • HTML: structure.
  • CSS: styling.
  • TypeScript: logic.

The classic analogy is LEGO, and for once it is the right one. Each brick has a shape and color (HTML and CSS), and some have special features (TypeScript). Bricks combine into larger things, the same piece can be reused in different places, and if one type of brick needs changing, you change it once and the change applies everywhere.


Open any website and you will spot them immediately: the header with a logo and menu, the product card with an image, price and button, the contact form, the footer. Anything repeated, or with its own logic, is a good candidate for a component.


Why it helps


Here are the reasons that actually matter in daily work:


  • Reuse: write a button once and use it in fifty places.
  • Readability: many small, focused files rather than 500 lines of HTML.
  • Maintenance: fix a card's bug in one place.
  • Teamwork: everyone works on their component without stepping on each other's toes.
  • Testing: check each piece in isolation without starting the whole app.

As an app grows, adding a feature often becomes a matter of combining components you already have.


🔧 The anatomy of an Angular component


In Angular, a component is a TypeScript class decorated with @Component:

TYPESCRIPT
1import { Component } from '@angular/core';
2
3@Component({
4  selector: 'app-saluto',           // the tag to use in HTML
5  template: `<h1>{{ titolo }}</h1>`, // the template
6  styles: [`h1 { color: rebeccapurple; }`]
7})
8export class SalutoComponent {
9  titolo = 'My first component';
10}

There are three parts:


  1. The decorator @Component, telling Angular "this class is a component" and defining its selector, template and styles.
  2. The class, containing properties (data) and methods (actions).
  3. The template, ordinary HTML plus Angular's binding syntax.

A word about standalone components


If you encountered Angular a few years ago, you will remember declaring every component inside an NgModule, with the associated boilerplate. Version 14 introduced standalone components, independent components that directly import what they need. Since version 19, standalone is the default behavior: you no longer even need to write standalone: true.


This guide uses modern Angular, so we stick to standalone. We will mention NgModules only when they help explain existing code.

If a tutorial asks you to declare the component in an NgModule, or insists on standalone: true, it was written for an older Angular version. It is not wrong, but it is no longer the recommended approach.

🛠 Creating our first component


Let us pick up the project from the previous chapter:

BASH
cd il-mio-primo-progetto
code .

Generate a component with the CLI (the shortcut is ng g c benvenuto):

BASH
ng generate component benvenuto

The CLI creates four files:

BASH
1src/app/benvenuto/
2├── benvenuto.component.ts       # the logic
3├── benvenuto.component.html     # the template
4├── benvenuto.component.css      # the styles
5└── benvenuto.component.spec.ts  # the tests

The generated TypeScript file is already a standalone component, ready to use:

TYPESCRIPT
1import { Component } from '@angular/core';
2
3@Component({
4  selector: 'app-benvenuto',
5  imports: [],
6  templateUrl: './benvenuto.component.html',
7  styleUrl: './benvenuto.component.css'
8})
9export class BenvenutoComponent {
10
11}

Now customize it. Add a couple of properties and two methods:

TYPESCRIPT
1import { Component } from '@angular/core';
2
3@Component({
4  selector: 'app-benvenuto',
5  imports: [],
6  templateUrl: './benvenuto.component.html',
7  styleUrl: './benvenuto.component.css'
8})
9export class BenvenutoComponent {
10  nome = 'developer';
11  messaggio = 'Welcome to Angular!';
12  numeroVisite = 0;
13
14  incrementaVisite() {
15    this.numeroVisite++;
16  }
17
18  cambiaNome(nuovoNome: string) {
19    this.nome = nuovoNome;
20  }
21}

Then the template, in benvenuto.component.html:

HTML
1<div class="card-benvenuto">
2  <h2>{{ messaggio }}</h2>
3  <p>Hello <strong>{{ nome }}</strong>!</p>
4  <p>Hai cliccato {{ numeroVisite }} volte.</p>
5
6  <button (click)="incrementaVisite()">Increment</button>
7  <button (click)="cambiaNome('Angular developer')">Change name</button>
8</div>

And a little styling in benvenuto.component.css:

CSS
1.card-benvenuto {
2  max-width: 400px;
3  margin: 2rem auto;
4  padding: 2rem;
5  border: 1px solid #ddd;
6  border-radius: 12px;
7  text-align: center;
8}
9
10.card-benvenuto button {
11  margin: 0.5rem 0.25rem 0;
12  padding: 0.5rem 1rem;
13  cursor: pointer;
14}

The component exists, but nobody is using it. Open src/app/app.component.html and replace its contents with:

HTML
1<div class="container">
2  <h1>La mia app Angular</h1>
3  <app-benvenuto></app-benvenuto>
4</div>

The last step, the one beginners always forget: the component must be imported by the component using it. In src/app/app.component.ts:

TYPESCRIPT
1import { Component } from '@angular/core';
2import { BenvenutoComponent } from './benvenuto/benvenuto.component';
3
4@Component({
5  selector: 'app-root',
6  imports: [BenvenutoComponent], // without this, the tag is not recognized
7  templateUrl: './app.component.html',
8  styleUrl: './app.component.css'
9})
10export class AppComponent {
11  title = 'il-mio-primo-progetto';
12}

Start the server with ng serve, go to http://localhost:4200 and play with the two buttons: you are already using properties, events and interpolation without writing any "glue" code. That is the point of Angular.


🔄 Data binding in brief


Our component already uses two types of data binding, the mechanism through which the template and class communicate. There are four in total:


Interpolation {{ }}: displays a property's value in the template.

HTML
<h2>{{ messaggio }}</h2>

Event binding (evento): executes a method when something happens.

HTML
<button (click)="incrementaVisite()">Click</button>

Property binding [proprieta]: sets an HTML property from a class property.

HTML
<img [src]="urlImmagine" [alt]="descrizione">

Two-way binding [(ngModel)]: synchronizes an input and a property in both directions.

HTML
<input [(ngModel)]="nome" placeholder="Il tuo nome">

For now, recognizing them is enough. Later, we will dedicate a whole chapter to data binding and the signals I mentioned in chapter one.


A component's lifecycle


Every component has a life: it is created, reacts to changes, and eventually gets destroyed. Angular lets you hook into these moments with lifecycle hooks, special methods called automatically:


  • ngOnInit() runs once at initialization: perfect for loading data.
  • ngOnChanges() runs when the component's inputs change.
  • ngOnDestroy() runs before removal: the right place to clean up timers and subscriptions.
TYPESCRIPT
1import { Component, OnInit, OnDestroy } from '@angular/core';
2
3@Component({ /* ... */ })
4export class MyComponent implements OnInit, OnDestroy {
5  ngOnInit() {
6    console.log('component initialized');
7  }
8
9  ngOnDestroy() {
10    console.log('component destroyed');
11  }
12}

You do not need to learn them all now. ngOnInit and ngOnDestroy cover most real cases; we will meet the others along the way.


A few good habits


Three rules that have saved me plenty of headaches on real projects:


  • Consistent names: file user-card.component.ts, class UserCardComponent, selector app-user-card. The CLI already generates these; do not fight it.
  • Small components: if a template goes beyond 100 lines, it probably contains two or three components waiting to get out.
  • One responsibility: UserCardComponent displays a user. If it also displays the list, details, form and validation, something has gone wrong.

The CLI commands I actually use


Beyond ng g c nome, these flags often come in handy:

BASH
1# in a subfolder
2ng g c features/user-profile
3
4# without a test file
5ng g c mio-comp --skip-tests
6
7# inline template and styles for tiny components
8ng g c mio-comp --inline-template --inline-style
9
10# preview the generated files without creating anything
11ng g c mio-comp --dry-run

--dry-run is especially valuable: before generating something in a large project, you can check where the files will end up.


Where we are now


You now know what a component is, why it is the basic unit of any Angular app, how to create one with the CLI and how to connect data and events to its template. That is quite a lot: this way of thinking will carry you through the rest of the guide.


In the next chapter, the Router enters the scene: we will turn our components into actual navigable pages, just as anyone opening a web application expects.

Fullstack developer in Milan. Writes about Angular, the JavaScript ecosystem and AI.