Back to articles

Angular @Input / @Output: Passing Data Between Components

11 min
Front-endAngular

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: `
    

{{ name }}

{{ email }}

`, }) 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: `
    
  `,
})
export class AppComponent {
  userName = 'Charmy';
  userEmail = 'charmy@example.com';
}

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

HTML

@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

@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: `
    
    

{{ count }}

`, }) export class CounterComponent { @Output() countChange = new EventEmitter(); 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: `
    
    

Parent received: {{ latestCount }}

`, }) 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: `
    
  `,
})
export class NameInputComponent {
  @Input() value = '';
  @Output() valueChange = new EventEmitter();

  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: `
    
    

Hello, {{ name }}

`, }) 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: `
    

{{ name() }}

{{ email() }}

`, }) export class UserCardComponent { name = input(''); // optional, default '' email = input.required(); // 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: ``,
})
export class CounterComponent {
  countChange = output();

  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