Component Basics
Components are the fundamental building blocks of every Angular application. A component bundles three things together — a TypeScript class that holds the data and behavior, an HTML template that describes what the user sees, and CSS styles that are scoped to that template. The @Component decorator is what ties these pieces into a single, reusable unit that Angular can render, update, and compose into a UI tree. Understanding this trio is the foundation for everything else you build.
The @Component decorator
A component is just a class annotated with the @Component decorator. The decorator is a function from @angular/core that attaches metadata to the class, telling Angular how to instantiate and render it. At minimum you supply a selector (how the component is used in markup) and either a template or templateUrl.
import { Component } from '@angular/core';
@Component({
selector: 'app-greeting',
standalone: true,
template: `<h1>Hello, Angular!</h1>`,
styles: `h1 { color: #2563eb; }`,
})
export class GreetingComponent {}
In modern Angular (v17+) components are standalone by default — they declare their own dependencies through an imports array rather than being registered in an NgModule. The standalone: true flag is optional in v19 since it is the default, but it is shown here for clarity.
Tip: The component class itself is plain TypeScript. Everything that makes it “Angular” comes from the metadata in the decorator — Angular reads it at compile time to generate efficient rendering code.
The three parts of a component
Every component combines a class, a template, and styles. You can keep them inline (good for small components) or split them into separate files (better for larger ones).
| Part | Inline option | External option | Purpose |
|---|---|---|---|
| Logic | the class body | the class body | State, methods, dependencies |
| Template | template: '...' | templateUrl: './x.html' | The rendered HTML |
| Styles | styles: '...' | styleUrl: './x.css' | Scoped CSS |
A typical generated component splits the template and styles into their own files:
import { Component } from '@angular/core';
@Component({
selector: 'app-counter',
standalone: true,
templateUrl: './counter.component.html',
styleUrl: './counter.component.css',
})
export class CounterComponent {}
The matching template lives in counter.component.html, keeping markup separate from logic. Use the selector to learn more about naming and using selectors.
Class logic with signals
The class holds your component’s state and the methods that change it. Modern Angular uses signals — reactive values created with signal() — instead of plain properties for state that drives the view. Reading a signal in the template subscribes that view to changes; calling .set() or .update() schedules a re-render automatically.
import { Component, signal } from '@angular/core';
@Component({
selector: 'app-counter',
standalone: true,
template: `
<p>Count: {{ count() }}</p>
<button (click)="increment()">Add one</button>
`,
})
export class CounterComponent {
count = signal(0);
increment(): void {
this.count.update((n) => n + 1);
}
}
Notice the signal is called like a function in the template (count()) to read its current value. The (click) syntax is event binding, and {{ ... }} is interpolation — the two simplest ways a template talks to its class.
Connecting the template to the class
The template is the bridge between your class and the DOM. Angular gives you a small, declarative syntax for moving data in both directions:
{{ expression }}— interpolation renders a value into the DOM.[property]="expr"— property binding sets a DOM property or component input.(event)="handler()"— event binding calls a class method.@if,@for,@switch— the built-in control flow blocks.
The control-flow blocks replaced the older *ngIf and *ngFor directives and require no imports:
import { Component, signal } from '@angular/core';
@Component({
selector: 'app-task-list',
standalone: true,
template: `
@if (tasks().length > 0) {
<ul>
@for (task of tasks(); track task) {
<li>{{ task }}</li>
}
</ul>
} @else {
<p>No tasks yet.</p>
}
`,
})
export class TaskListComponent {
tasks = signal(['Write docs', 'Review PR']);
}
Output:
• Write docs
• Review PR
The mandatory track expression in @for lets Angular identify items so it can update the DOM efficiently instead of re-rendering the whole list.
Scoped styles
Styles declared in styles or styleUrl apply only to that component’s template by default. Angular achieves this with view encapsulation — it rewrites your selectors and adds unique attributes to the rendered elements so a h1 rule in one component never leaks into another. This means you can use simple, generic selectors without fear of global collisions.
@Component({
selector: 'app-badge',
standalone: true,
template: `<span class="badge">New</span>`,
styles: `
.badge {
background: #16a34a;
color: white;
padding: 2px 8px;
border-radius: 999px;
}
`,
})
export class BadgeComponent {}
The encapsulation strategy is configurable; see view encapsulation for the emulated, none, and shadow-DOM options.
Best Practices
- Keep components small and focused — one responsibility per component makes them easier to test and reuse.
- Use signals for reactive state instead of plain class fields so the view updates automatically.
- Prefer the new
@if/@for/@switchcontrol flow over the legacy structural directives in new code. - Always provide a
trackexpression in@forto keep list rendering efficient. - Use external
templateUrl/styleUrlfiles once a template grows beyond a handful of lines. - Default to standalone components — they avoid
NgModuleboilerplate and are the supported path forward. - Give selectors a consistent, app-specific prefix (e.g.
app-) to avoid clashing with native elements.