Angular · Components

Components

Components are the building blocks of every Angular screen. Each one is a class with a template and styles — small, focused and reusable.

Beginner 5 min Lesson 5 of 38Examples target Angular 20 with standalone components. Each lesson states the version it was verified against.
Before you start:Project Structure
0/38 in Angular
On this page · 5 min · Beginner

Learning objectives#

  • Write a standalone component with @Component
  • Use separate template and style files or inline ones
  • Accept data with input()
  • Compose components and understand style encapsulation

Explanation#

@Component({ selector, template or templateUrl, styles or styleUrl, imports }) turns a class into a component. The selector is the custom HTML tag: <app-product-card>.

Small components can use inline template and styles; larger ones use separate .html and .css files (the CLI default).

input() declares data passed in from the parent: name = input.required<string>(); price = input(0). Inside the template read them like signals: {{ name() }}.

A parent uses a child by importing the class in its imports array and writing its tag: <app-product-card [name]="p.name" />.

Styles are encapsulated by default: CSS in a component affects only that component’s template, so class names never clash.

Syntax#

TypeScript Illustrative
@Component({
  selector: 'app-product-card',
  templateUrl: './product-card.html',
  styleUrl: './product-card.css',
})
export class ProductCard {
  name = input.required<string>();
  price = input(0);
}

Code example: A reusable card used three times#

TypeScriptproduct-list.ts Illustrative
import { Component, input } from '@angular/core';


@Component({
  selector: 'app-product-card',
  template: `
    <article class="card">
      <h3>{{ name() }}</h3>
      <p>₹{{ price() }}</p>
      @if (price() > 500) { <span class="badge">Premium</span> }
    </article>
  `,
  styles: `
    .card { border: 1px solid #ddd; border-radius: 8px; padding: 12px; width: 160px; }
    .badge { background: #fde68a; padding: 2px 6px; border-radius: 4px; }
  `,
})
export class ProductCard {
  name = input.required<string>();
  price = input(0);
}


@Component({
  selector: 'app-product-list',
  imports: [ProductCard],
  template: `
    <section class="grid">
      @for (p of products; track p.id) {
        <app-product-card [name]="p.name" [price]="p.price" />
      }
    </section>
  `,
  styles: `.grid { display: flex; gap: 12px; }`,
})
export class ProductList {
  products = [
    { id: 1, name: 'Pen', price: 20 },
    { id: 2, name: 'Notebook', price: 60 },
    { id: 3, name: 'Backpack', price: 1200 },
  ];
}
Expected output
Three bordered cards appear in a row: “Pen ₹20”, “Notebook ₹60” and “Backpack ₹1200” with a yellow “Premium” badge. The .card styles apply only inside ProductCard.

This example is illustrative. Copy or download it and run it in your own environment; the expected output is shown above.

Step-by-step explanation#

  1. ProductCard declares two inputs: a required name and a price with default 0.
  2. Its template reads inputs as signals: name(), price().
  3. ProductList imports ProductCard, then uses <app-product-card> inside @for.
  4. [name]="p.name" passes data from parent to child.
  5. Each component’s styles are scoped to its own template.

Real-world use case#

A design system team ships components like <app-button>, <app-card> and <app-data-table>. Feature teams import them and only pass inputs.

Common mistakes#

“is not a known element”

The parent forgot to add the child component to its imports array.

Reading inputs without ()

With signal inputs, {{ name }} shows the function. Use {{ name() }}.

Giant components

A 1,000-line component mixes concerns. Split it into smaller presentational components.

Best practices#

  • One component per file
  • Prefix selectors (app-, shop-)
  • Use input.required for mandatory data
  • Keep components presentational where possible

Remember this#

@Component = selector + template + styles + imports.

input() = data in; read with name().

Using a child? Add it to imports.

Practice exercise#

Your task: Create a UserBadge component with a required name input and an optional role input (default "Member"), showing "Asha (Admin)".

Need a hint?
name = input.required<string>(); role = input("Member");
Show solution
TypeScript Illustrative
import { Component, input } from '@angular/core';


@Component({
  selector: 'app-user-badge',
  template: `<span>{{ name() }} ({{ role() }})</span>`,
})
export class UserBadge {
  name = input.required<string>();
  role = input('Member');
}
// Usage: <app-user-badge name="Asha" role="Admin" />

Mini quiz#

Components quizMultiple choice Question 1 of 3

What does the selector define?

Score so far: 0/0

Want more? Practise all Angular MCQs · Angular interview questions

My notes#

Saved in this browser.

Progress is saved in this browser. Sign in to sync.