Overview
Angular provides two primary mechanisms for component communication: inputs for passing data from parent to child, and outputs for emitting events from child to parent.
Signature
@ Input ( options ?: string | Input ): PropertyDecorator
Parameters
Either an alias string or a configuration object.
Alternative name for the input in templates. @ Input ({ alias: 'userName' }) name ! : string ;
Whether the input must be provided. Causes compile error if missing. @ Input ({ required: true }) userId ! : number ;
Function to transform the input value before assignment. @ Input ({ transform: booleanAttribute }) disabled : boolean = false ;
import { Component , Input } from '@angular/core' ;
@ Component ({
selector: 'app-user-card' ,
standalone: true ,
template: `
<div class="card">
<h3>{{name}}</h3>
<p>{{email}}</p>
<span class="badge">{{role}}</span>
</div>
`
})
export class UserCardComponent {
@ Input () name : string = '' ;
@ Input () email : string = '' ;
@ Input () role : string = 'user' ;
}
Usage:
< app-user-card
name = "John Doe"
email = "john@example.com"
role = "admin" >
</ app-user-card >
import { Component , Input } from '@angular/core' ;
@ Component ({
selector: 'app-product' ,
standalone: true ,
template: `
<div>
<h4>{{productName}}</h4>
<p>Price: ${ { productPrice } } </p>
</div>
`
})
export class ProductComponent {
@ Input ({ alias: 'name' }) productName !: string ;
@ Input ({ alias: 'price' }) productPrice !: number ;
}
Usage:
< app-product name = "Laptop" [price] = "999" ></ app-product >
import { Component , Input } from '@angular/core' ;
@ Component ({
selector: 'app-profile' ,
standalone: true ,
template: `
<div>
<h2>{{userId}}</h2>
<p>{{bio || 'No bio available'}}</p>
</div>
`
})
export class ProfileComponent {
// Compile error if not provided
@ Input ({ required: true }) userId !: string ;
// Optional input
@ Input () bio ?: string ;
}
import { Component , Input , booleanAttribute , numberAttribute } from '@angular/core' ;
@ Component ({
selector: 'app-toggle' ,
standalone: true ,
template: `
<button
[disabled]="disabled"
[style.width.px]="width">
{{label}}
</button>
`
})
export class ToggleComponent {
@ Input ({ transform: booleanAttribute }) disabled = false ;
@ Input ({ transform: numberAttribute }) width = 100 ;
@ Input () label = 'Toggle' ;
}
Usage:
<!-- String "true" converted to boolean true -->
< app-toggle disabled = "true" width = "200" ></ app-toggle >
<!-- Boolean attribute converted to true -->
< app-toggle disabled ></ app-toggle >
Signature
input < T >( initialValue ?: T , options ?: InputOptions < T > ): InputSignal < T >
input . required < T >( options ?: InputOptions < T > ): InputSignal < T >
import { Component , input , computed } from '@angular/core' ;
@ Component ({
selector: 'app-counter' ,
standalone: true ,
template: `
<div>
<p>Count: {{count()}}</p>
<p>Double: {{doubled()}}</p>
<p>Label: {{label()}}</p>
</div>
`
})
export class CounterComponent {
// Optional with default
count = input ( 0 );
// Optional without default
label = input < string >();
// Derived value
doubled = computed (() => this . count () * 2 );
}
Usage:
< app-counter [count] = "5" label = "Items" ></ app-counter >
import { Component , input } from '@angular/core' ;
interface Item {
id : number ;
name : string ;
description : string ;
}
@ Component ({
selector: 'app-item-detail' ,
standalone: true ,
template: `
<div>
<h3>{{item().name}}</h3>
<p>{{item().description}}</p>
</div>
`
})
export class ItemDetailComponent {
// Required signal input
item = input . required < Item >();
}
date-display.component.ts
import { Component , input } from '@angular/core' ;
@ Component ({
selector: 'app-date-display' ,
standalone: true ,
template: `
<div>Date: {{formattedDate()}}</div>
`
})
export class DateDisplayComponent {
date = input ( '' , {
transform : ( value : string | Date ) => {
if ( typeof value === 'string' ) {
return new Date ( value );
}
return value ;
}
});
formattedDate = computed (() => {
const d = this . date ();
return d ? d . toLocaleDateString () : '' ;
});
}
@Output Decorator
Signature
@ Output ( alias ?: string ): PropertyDecorator
Parameters
Alternative name for the output in templates.
Basic Output Example
import { Component , Output , EventEmitter } from '@angular/core' ;
@ Component ({
selector: 'app-button' ,
standalone: true ,
template: `
<button (click)="handleClick()">
{{label}}
</button>
`
})
export class ButtonComponent {
@ Output () clicked = new EventEmitter < void >();
label = 'Click Me' ;
handleClick () {
this . clicked . emit ();
}
}
Usage:
< app-button (clicked) = "onButtonClick()" ></ app-button >
Output with Data
import { Component , Output , EventEmitter } from '@angular/core' ;
@ Component ({
selector: 'app-search' ,
standalone: true ,
template: `
<input
#input
type="text"
(input)="onSearch(input.value)">
`
})
export class SearchComponent {
@ Output () search = new EventEmitter < string >();
onSearch ( query : string ) {
this . search . emit ( query );
}
}
Usage:
< app-search (search) = "handleSearch($event)" ></ app-search >
handleSearch ( query : string ) {
console . log ( 'Search query:' , query );
}
Output with Alias
import { Component , Output , EventEmitter } from '@angular/core' ;
@ Component ({
selector: 'app-dropdown' ,
standalone: true ,
template: `
<select (change)="onValueChange($event)">
<option value="1">Option 1</option>
<option value="2">Option 2</option>
</select>
`
})
export class DropdownComponent {
@ Output ( 'valueChange' ) valueChanged = new EventEmitter < string >();
onValueChange ( event : Event ) {
const value = ( event . target as HTMLSelectElement ). value ;
this . valueChanged . emit ( value );
}
}
Usage:
< app-dropdown (valueChange) = "onDropdownChange($event)" ></ app-dropdown >
output() Function (Signal-Based)
Signature
output < T = void > ( options ?: OutputOptions ): OutputEmitterRef < T >
Signal Output Example
modern-button.component.ts
import { Component , input , output } from '@angular/core' ;
@ Component ({
selector: 'app-modern-button' ,
standalone: true ,
template: `
<button (click)="handleClick()">
{{label()}}
</button>
`
})
export class ModernButtonComponent {
label = input ( 'Click' );
clicked = output < void >();
handleClick () {
this . clicked . emit ();
}
}
Output with Custom Event Type
import { Component , output } from '@angular/core' ;
interface ItemSelectedEvent {
id : number ;
name : string ;
timestamp : Date ;
}
@ Component ({
selector: 'app-item-list' ,
standalone: true ,
template: `
<div *ngFor="let item of items" (click)="selectItem(item)">
{{item.name}}
</div>
`
})
export class ItemListComponent {
itemSelected = output < ItemSelectedEvent >();
items = [
{ id: 1 , name: 'Item 1' },
{ id: 2 , name: 'Item 2' }
];
selectItem ( item : { id : number , name : string }) {
this . itemSelected . emit ({
... item ,
timestamp: new Date ()
});
}
}
Two-Way Binding
NgModel Pattern
custom-input.component.ts
import { Component , Input , Output , EventEmitter } from '@angular/core' ;
@ Component ({
selector: 'app-custom-input' ,
standalone: true ,
template: `
<input
[value]="value"
(input)="onValueChange($event)">
`
})
export class CustomInputComponent {
@ Input () value : string = '' ;
@ Output () valueChange = new EventEmitter < string >();
onValueChange ( event : Event ) {
const newValue = ( event . target as HTMLInputElement ). value ;
this . valueChange . emit ( newValue );
}
}
Usage:
< app-custom-input [(value)] = "username" ></ app-custom-input >
Signal-Based Two-Way Binding
signal-input.component.ts
import { Component , model } from '@angular/core' ;
@ Component ({
selector: 'app-signal-input' ,
standalone: true ,
template: `
<input
[value]="value()"
(input)="value.set($any($event.target).value)">
`
})
export class SignalInputComponent {
value = model ( '' );
}
Usage:
< app-signal-input [(value)] = "username" ></ app-signal-input >
Parent-Child Communication Example
import { Component } from '@angular/core' ;
import { ChildComponent } from './child.component' ;
@ Component ({
selector: 'app-parent' ,
standalone: true ,
imports: [ ChildComponent ],
template: `
<div>
<h2>Parent Component</h2>
<p>Message from child: {{childMessage}}</p>
<app-child
[parentMessage]="parentMessage"
(messageToParent)="receiveMessage($event)">
</app-child>
</div>
`
})
export class ParentComponent {
parentMessage = 'Hello from parent' ;
childMessage = '' ;
receiveMessage ( message : string ) {
this . childMessage = message ;
}
}
import { Component , Input , Output , EventEmitter } from '@angular/core' ;
@ Component ({
selector: 'app-child' ,
standalone: true ,
template: `
<div>
<h3>Child Component</h3>
<p>{{parentMessage}}</p>
<button (click)="sendMessage()">Send to Parent</button>
</div>
`
})
export class ChildComponent {
@ Input () parentMessage : string = '' ;
@ Output () messageToParent = new EventEmitter < string >();
sendMessage () {
this . messageToParent . emit ( 'Hello from child' );
}
}
Best Practices
Use signal inputs (input()) for new code - they’re more performant
Mark inputs as required when they’re essential for component function
Use input transforms for common conversions (boolean, number)
Prefer descriptive event names for outputs (e.g., userSelected over select)
Use TypeScript types for output events to ensure type safety
Don’t mutate input objects - create new objects instead
Always complete EventEmitters in ngOnDestroy if they’re long-lived
Avoid complex logic in input setters
Don’t emit outputs in ngOnInit or constructor
Comparison: Decorator vs Function
Feature @Input / @Output input() / output() Type Decorator Function Reactivity Manual Signal-based Required inputs Via option Built-in support Transform Via option Via option Change detection Zone.js Signals Performance Standard Better
See Also
Component Inputs Learn about input properties
Component Outputs Master output events
Signals Understand signal reactivity
Two-Way Binding Implement two-way binding