Back to articles

Angular @Input / @Output: Passing Data Between Components

11 min
Front-endAngular

Angular @Input / @Output: Passing Data Between Components

Angular components communicate through two decorators:

  • @Input — passes data from a parent component down to a child
  • @Output — sends events from a child component up to a parent

@Input: Parent to Child

@Input lets a parent component pass a value into a child component.

Child Component

Declare the properties you want to receive with @Input:

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

@Component({
  standalone: true,
  selector: 'app-user-card',
  template: `
    <div>
      <h2>{{ name }}</h2>
      <p>{{ email }}</p>
    </div>
  `,
})
export class UserCardComponent {
  @Input() name = '';
  @Input() email = '';
}

Parent Component

Use property binding in the parent's template to pass values in:

TypeScript
import { Component } from '@angular/core';
import { UserCardComponent } from './user-card.component';

@Component({
  standalone: true,
  imports: [UserCardComponent],
  template: `
    <app-user-card
      [name]="userName"
      [email]="userEmail"
    />
  `,
})
export class AppComponent {
  userName = 'Charmy';
  userEmail = 'charmy@example.com';
}

For static string values, you can skip the binding syntax:

HTML
<app-user-card name="Charmy" email="charmy@example.com" />

@Input Options

Required Inputs

Angular 16 added a required option that makes an input mandatory. Missing it becomes a compile-time error:

TypeScript
@Input({ required: true }) name!: string;

Alias

If you want the parent to use a different property name, set an alias:

TypeScript
@Input('userName') name = '';

Parent usage:

HTML
<app-user-card [userName]="'Charmy'" />

@Output: Child to Parent

@Output lets a child component emit events up to its parent, typically using EventEmitter.

Child Component

Declare an EventEmitter property and call emit when something happens:

TypeScript
import { Component, Output, EventEmitter } from '@angular/core';

@Component({
  standalone: true,
  selector: 'app-counter',
  template: `
    <button (click)="increment()">+1</button>
    <p>{{ count }}</p>
  `,
})
export class CounterComponent {
  @Output() countChange = new EventEmitter<number>();

  count = 0;

  increment() {
    this.count++;
    this.countChange.emit(this.count);
  }
}

Parent Component

Listen to the event with event binding in the parent's template:

TypeScript
import { Component } from '@angular/core';
import { CounterComponent } from './counter.component';

@Component({
  standalone: true,
  imports: [CounterComponent],
  template: `
    <app-counter (countChange)="onCountChange($event)" />
    <p>Parent received: {{ latestCount }}</p>
  `,
})
export class AppComponent {
  latestCount = 0;

  onCountChange(count: number) {
    this.latestCount = count;
  }
}

$event holds the value passed to emit.


Full Example

Here's a complete example combining both @Input and @Output in a reusable input component:

Child — text input component

TypeScript
import { Component, Input, Output, EventEmitter } from '@angular/core';

@Component({
  standalone: true,
  selector: 'app-name-input',
  template: `
    <input
      [value]="value"
      (input)="onInput($event)"
      placeholder="Enter name"
    />
  `,
})
export class NameInputComponent {
  @Input() value = '';
  @Output() valueChange = new EventEmitter<string>();

  onInput(event: Event) {
    const input = event.target as HTMLInputElement;
    this.valueChange.emit(input.value);
  }
}

Parent

TypeScript
import { Component } from '@angular/core';
import { NameInputComponent } from './name-input.component';

@Component({
  standalone: true,
  imports: [NameInputComponent],
  template: `
    <app-name-input
      [value]="name"
      (valueChange)="name = $event"
    />
    <p>Hello, {{ name }}</p>
  `,
})
export class AppComponent {
  name = '';
}

input() and output() (Angular 17+)

Angular 17 introduced a Signal-based API — input() and output() — as a modern alternative to @Input and @Output.

input()

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

@Component({
  standalone: true,
  selector: 'app-user-card',
  template: `
    <h2>{{ name() }}</h2>
    <p>{{ email() }}</p>
  `,
})
export class UserCardComponent {
  name = input('');                  // optional, default ''
  email = input.required<string>();  // required
}

input() returns a Signal, so you read the value by calling it with ().

output()

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

@Component({
  standalone: true,
  selector: 'app-counter',
  template: `<button (click)="increment()">+1</button>`,
})
export class CounterComponent {
  countChange = output<number>();

  count = 0;

  increment() {
    this.count++;
    this.countChange.emit(this.count);
  }
}

@Input / @Output vs input() / output()

@Input / @Outputinput() / output()
StyleDecoratorsFunctions
Reading valuesDirect property accessCall with ()
Required inputs@Input({ required: true })input.required()
Signal integrationNoYes
Angular versionAll versionsv17+

Both approaches work today. For new projects, input() and output() are the recommended direction. Existing code can migrate gradually.


Conclusion

  • @Input — parent passes data down to a child via property binding
  • @Output — child sends events up to a parent via EventEmitter
  • Angular 17+ offers input() and output() as Signal-integrated alternatives — cleaner syntax and better integration with the modern Angular reactivity model