Quick answer: An Angular component is a TypeScript class that owns a piece of the screen. Angular creates it, renders its template, and destroys it when the screen is done with it. Lifecycle hooks — ngOnInit, ngOnChanges, ngAfterViewInit, ngOnDestroy — let you run code at those moments. Most components need only ngOnInit and ngOnDestroy.The sections below take Angular components apart in the order you meet them in a real project: the class, the template and styles, the lifecycle hooks, and the two Angular components hooks you can usually skip.

Anatomy of an Angular component

The @Component decorator gives Angular three facts about the class: the CSS selector it answers to, its template, and its styles. Everything else is ordinary TypeScript.

import { Component, input } from '@angular/core';

@Component({
  selector: 'app-badge',
  template: `<span class="badge">{{ label() }}</span>`,
  styles: `.badge { padding: 2px 8px; border-radius: 999px; background: #ccfbf1; }`,
})
export class BadgeComponent {
  label = input.required<string>();
}

You then use it in any template as <app-badge label="New" />. The selector is the element name; the inputs are the attributes.

Angular components: templates and styles, inline or separate files

Small components read well with inline template and styles. Once markup grows past a few dozen lines, split it into templateUrl and styleUrls — the files the CLI generates by default.

@Component({
  selector: 'app-dashboard',
  templateUrl: './dashboard.component.html',
  styleUrl: './dashboard.component.css',
})
export class DashboardComponent {}

Component styles are scoped. Angular adds a unique attribute to the template’s elements and rewrites the CSS to match, so a .card rule inside one component cannot leak into another. You get encapsulation without a naming convention such as BEM — though the utility-class approach used elsewhere on this site is a different, equally valid answer to the same problem.

Lifecycle hooks in order

HookRuns whenTypical use
ngOnChangesBefore ngOnInit, and whenever an input changesReact to a changed input
ngOnInitOnce, after the first input values are setFetch data, set up state
ngAfterViewInitAfter the component’s view rendersMeasure the DOM, start a chart
ngOnDestroyJust before Angular removes the componentCancel subscriptions, clear timers
import { Component, OnInit, OnDestroy, inject } from '@angular/core';
import { ProductService } from './product.service';

@Component({ selector: 'app-products', template: `...` })
export class ProductsComponent implements OnInit, OnDestroy {
  private service = inject(ProductService);
  private timer?: ReturnType<typeof setInterval>;

  ngOnInit() {
    this.timer = setInterval(() => this.service.refresh(), 30000);
  }

  ngOnDestroy() {
    clearInterval(this.timer);
  }
}

The implements clause is optional but useful: it makes the compiler check the signature, so a typo in ngOnDestory becomes an error instead of a hook that silently never runs.

Two hooks you can often skip now

With signals, a lot of ngOnChanges code disappears. A computed() value recalculates automatically when its source changes, so you rarely need to react to an input manually.

import { Component, input, computed } from '@angular/core';

@Component({ selector: 'app-price', template: `{{ withTax() | currency }}` })
export class PriceComponent {
  amount = input.required<number>();
  withTax = computed(() => this.amount() * 1.2);
}

There is no hook here at all. When amount changes, withTax knows, because it depends on the signal. Reach for lifecycle hooks when you touch something outside Angular — a timer, an external widget, a subscription — and let signals handle pure derivation.

Continue the series with templates and data binding. To learn components hands-on, Ampersand Academy teaches Angular with real projects, one-to-one.

Frequently asked questions

What is the difference between ngOnInit and the constructor?

The constructor runs before Angular has set the component’s inputs. ngOnInit runs after the first set of inputs is available, so it is the right place to fetch data or read input values.

Do Angular component styles leak to other components?

No. Angular scopes a component’s styles to its own template by adding a unique attribute and rewriting the selectors, so rules stay inside the component.

When should I use ngOnDestroy?

Use ngOnDestroy to clean up anything outside Angular’s control: clear timers, cancel subscriptions and release external widgets. Skipping it causes memory leaks in single-page apps.

Should I use an inline template or a separate file?

Use an inline template for small components of a few dozen lines or less. Split into templateUrl and styleUrl once the markup grows, which is what the CLI generates by default.

Do I still need ngOnChanges with signals?

Often no. A computed signal recalculates automatically when its inputs change, so manual change handling is rarely needed. Keep ngOnChanges for non-signal inputs or imperative logic.

Last updated on · Written by