Components rarely work in isolation, parents pass data down to children, and children notify parents of events. This lesson covers @Input()/input() and @Output()/output(), the two mechanisms Angular provides for this communication.
What Are Inputs and Outputs?
An input lets a parent component pass data into a child. An output lets a child component emit an event a parent can listen to. Together, they form the primary way components communicate in a component tree without tightly coupling them together.
Classic Angular uses the @Input() and @Output() decorators with EventEmitter. Since Angular 17.1+, the newer input() and output() functions offer a signal-friendly alternative with better type inference.
The parent passes max in via property binding and listens for ratingChange via event binding.
@Input/@Output vs input()/output()
// classic decorators
@Input() value = '';
@Output() valueChange = new EventEmitter<string>();
// modern signal-based functions (Angular 17.1+)
value = input('');
valueChange = output<string>();
@Input() marks a class property as settable from a parent's template via property binding.
@Output() exposes an EventEmitter a parent can listen to via event binding.
input() returns a read-only signal; read its value by calling it as a function: value().
output() returns an emitter-like object with an .emit() method, used the same way as @Output() in templates.
Input/Output Cheatsheet
Both the classic decorator API and the modern signal-based functions.
Task
Classic
Modern
Declare an input
@Input() name = '';
name = input('');
Required input
@Input({ required: true }) id!: string;
id = input.required<string>();
Read an input value
this.name
this.name()
Declare an output
@Output() saved = new EventEmitter<void>();
saved = output<void>();
Emit an output
this.saved.emit()
this.saved.emit()
Bind an input (parent)
[name]="value"
[name]="value" (same)
Bind an output (parent)
(saved)="onSaved()"
(saved)="onSaved()" (same)
Required and Aliased Inputs
Inputs can be marked required, causing a compile-time error if a parent forgets to bind them, and can be aliased so the public template name differs from the internal property name.
Typing an EventEmitter<T> (or output<T>()) ensures parents receive strongly typed data in $event, catching mismatches at compile time rather than at runtime.
Naming an input x and its matching output xChange lets consumers use Angular's "banana in a box" two-way binding syntax on your own custom component, exactly like [(ngModel)].
@Input() value = '';
@Output() valueChange = new EventEmitter<string>();
<!-- usage -->
<app-custom-input [(value)]="username"></app-custom-input>
Common Mistakes
Trying to mutate a parent's data directly from a child instead of emitting an output event and letting the parent decide.
Forgetting the Change suffix convention (xChange) when building a two-way bindable input/output pair.
Not typing EventEmitter<T>/output<T>(), losing type safety on the parent's $event.
Mixing @Input()/@Output() and input()/output() inconsistently within the same component without a clear reason.
Key Takeaways
Inputs (@Input()/input()) pass data from a parent down into a child component.
Outputs (@Output()/output()) let a child emit events a parent can listen to.
The modern input()/output() functions integrate naturally with Angular signals.
Naming an input/output pair x/xChange enables custom two-way binding syntax.
Pro Tip
Favor the modern input()/output() functions in new standalone components, they read naturally alongside signals and give better type inference for required and aliased inputs than the decorator-based API.
You now understand parent-child communication with inputs and outputs. Next, learn about @ViewChild and @ContentChild for direct access to child elements and components.