What Are input() and output() in Modern Angular?
In modern Angular, input() and output() are APIs used for communication between parent and child components.
At the simplest level:
Parent Component
│
│ input()
▼
Child Component
│
│ output()
▼
Parent Component
input()→ Child receives data from the parent.output()→ Child emits an event/data to the parent.
They provide modern alternatives to the traditional:
@Input()
@Output() + EventEmitter
1. What is input()?
input() creates an input property that allows a child component to receive data from its parent.
Import it from:
import { input } from '@angular/core';
Example:
name = input<string>();
The child can now receive name from its parent.
Why Do We Need input()?
Suppose we have:
ProductList
Parent
│
│ Product information
▼
Product
Child
The parent owns the product information:
productName = 'Laptop';
The child needs that information to display it.
Instead of duplicating the data, the parent passes it to the child.
input() Code Sample
Parent Component
product-list.ts
import { Component } from '@angular/core';
import { Product } from '../product/product';
@Component({
selector: 'app-product-list',
imports: [Product],
templateUrl: './product-list.html'
})
export class ProductList {
productName = 'Laptop';
}
product-list.html
<app-product
[name]="productName">
</app-product>
Child Component
product.ts
import { Component, input } from '@angular/core';
@Component({
selector: 'app-product',
templateUrl: './product.html'
})
export class Product {
name = input<string>();
}
product.html
<h2>{{ name() }}</h2>
Output:
Laptop
How input() Works
The parent has:
productName = 'Laptop';
The parent passes it:
<app-product [name]="productName">
</app-product>
The child receives it:
name = input<string>();
The child reads it:
{{ name() }}
Flow:
PARENT
productName = "Laptop"
│
│
│ [name]="productName"
▼
CHILD
name = input<string>()
│
▼
name()
│
▼
"Laptop"
Why Do We Use name() Instead of name?
This is an important difference from @Input().
input() returns an InputSignal.
Therefore:
name = input<string>();
name is a signal-like reactive value.
To read its current value:
name()
So in HTML:
<h2>{{ name() }}</h2>
not:
<h2>{{ name }}</h2>
Required Input
Sometimes the child must receive a value.
Use:
product = input.required<Product>();
Example:
export class ProductCard {
product = input.required<ProductModel>();
}
Parent must provide it:
<app-product-card
[product]="selectedProduct">
</app-product-card>
This is useful when the component cannot function correctly without the input.
Input with Default Value
You can provide a default value:
quantity = input(1);
If the parent doesn't provide anything:
quantity = 1
The parent can override it:
<app-product [quantity]="5">
</app-product>
Now:
quantity() = 5
Input with Type
You can strongly type inputs:
name = input<string>();
price = input<number>();
available = input<boolean>();
product = input<ProductModel>();
This provides TypeScript type checking.
Traditional @Input() vs Modern input()
Traditional
import { Input } from '@angular/core';
export class Product {
@Input()
name = '';
}
Template:
{{ name }}
Modern
import { input } from '@angular/core';
export class Product {
name = input<string>();
}
Template:
{{ name() }}
Comparison
@Input() |
input() |
|---|---|
| Decorator API | Signal-based API |
| Traditional approach | Modern approach |
name |
name() |
| Normal property | Input signal |
| Widely found in existing apps | Preferred in modern Angular code |
2. What is output()?
output() creates an output event that allows a child component to send an event or value to its parent.
Import:
import { output } from '@angular/core';
Example:
productAdded = output<string>();
The child can emit:
this.productAdded.emit('Laptop');
Why Do We Need output()?
Suppose the child contains an Add to Cart button:
ProductList
Parent
▲
│
│ Product selected
│
Product
Child
│
└── Add to Cart
When the user clicks the button, the child needs to notify its parent.
That's what output() provides.
output() Code Sample
Child
product.ts
import { Component, output } from '@angular/core';
@Component({
selector: 'app-product',
templateUrl: './product.html'
})
export class Product {
productAdded = output<string>();
addToCart() {
this.productAdded.emit('Laptop');
}
}
product.html
<button (click)="addToCart()">
Add to Cart
</button>
Parent
product-list.html
<app-product
(productAdded)="onProductAdded($event)">
</app-product>
product-list.ts
export class ProductList {
onProductAdded(product: string) {
console.log('Added:', product);
}
}
How output() Works
First, the child creates an output:
productAdded = output<string>();
Then the child emits:
this.productAdded.emit('Laptop');
The parent listens:
(productAdded)="onProductAdded($event)"
The emitted value becomes:
$event = "Laptop"
Then Angular calls:
onProductAdded('Laptop');
Complete flow:
CHILD
User clicks button
│
▼
addToCart()
│
▼
productAdded.emit("Laptop")
│
▼
OUTPUT EVENT
│
▼
PARENT
(productAdded)="onProductAdded($event)"
│
▼
$event = "Laptop"
│
▼
onProductAdded("Laptop")
Passing an Object with output()
In real applications, you will commonly emit an object.
interface ProductModel {
id: number;
name: string;
price: number;
}
Child:
productAdded = output<ProductModel>();
Emit:
addToCart() {
const product: ProductModel = {
id: 1,
name: 'Laptop',
price: 50000
};
this.productAdded.emit(product);
}
Parent:
<app-product
(productAdded)="onProductAdded($event)">
</app-product>
Parent:
onProductAdded(product: ProductModel) {
console.log(product.name);
}
Traditional @Output() vs Modern output()
Traditional Approach
import {
EventEmitter,
Output
} from '@angular/core';
export class Product {
@Output()
productAdded = new EventEmitter<string>();
addToCart() {
this.productAdded.emit('Laptop');
}
}
Modern Approach
import { output } from '@angular/core';
export class Product {
productAdded = output<string>();
addToCart() {
this.productAdded.emit('Laptop');
}
}
The parent listens the same way:
<app-product
(productAdded)="onProductAdded($event)">
</app-product>
output() vs EventEmitter
| Traditional | Modern |
|---|---|
@Output() |
output() |
EventEmitter<T> |
OutputEmitterRef<T> |
| Decorator based | Function based |
.emit() |
.emit() |
| Common in older/existing code | Recommended for new Angular code |
One advantage of the modern syntax is that you don't need:
@Output()
or explicitly create:
new EventEmitter<T>()
Instead:
saved = output<string>();
input() and output() Together
Let's combine both.
Child
import {
Component,
input,
output
} from '@angular/core';
@Component({
selector: 'app-product',
template: `
<h2>{{ name() }}</h2>
<button (click)="addToCart()">
Add to Cart
</button>
`
})
export class Product {
name = input.required<string>();
productAdded = output<string>();
addToCart() {
this.productAdded.emit(this.name());
}
}
Parent:
export class ProductList {
productName = 'Laptop';
onProductAdded(product: string) {
console.log(product);
}
}
Parent HTML:
<app-product
[name]="productName"
(productAdded)="onProductAdded($event)">
</app-product>
Complete Flow
This single line is extremely important:
<app-product
[name]="productName"
(productAdded)="onProductAdded($event)">
</app-product>
It contains both directions of communication:
PARENT
productName = "Laptop"
│
│
[name]="productName"
│
▼
CHILD
name = input<string>()
│
│ User clicks
▼
productAdded = output<string>()
│
│
emit(this.name())
│
▼
PARENT
(productAdded)="onProductAdded($event)"
The easiest rule to remember:
PARENT
│
│ input()
▼
CHILD
│
│ output()
▼
PARENT
Why Modern Angular Introduced These APIs
The newer APIs integrate component communication more naturally with Angular's modern reactive model.
In particular, input():
- Produces a signal-based input.
- Works naturally with
computed()andeffect(). - Provides concise required-input syntax.
- Reduces decorator-based code.
- Provides strongly typed APIs.
For example:
firstName = input.required<string>();
lastName = input.required<string>();
fullName = computed(
() => `${this.firstName()} ${this.lastName()}`
);
When either input changes, the computed value can react accordingly.
Advantages
1. Cleaner Syntax
Instead of:
@Output()
saved = new EventEmitter<string>();
you can write:
saved = output<string>();
2. Signal-Based Inputs
input() integrates directly with Angular Signals:
name = input<string>();
3. Required Inputs
Very clear syntax:
product = input.required<Product>();
4. Strong Typing
input<Product>()
output<Product>()
provide TypeScript type checking.
5. Reactive Programming
Inputs can naturally participate in:
computed()
effect()
and other signal-based logic.
6. Less Boilerplate
Modern APIs reduce the amount of decorator and EventEmitter code required.
Disadvantages
Existing Applications Use Older Syntax
Many Angular projects still contain:
@Input()
@Output()
EventEmitter
Therefore, Angular developers should understand both styles.
Different Input Access Syntax
Developers coming from @Input() need to remember:
Traditional:
name
Modern:
name()
because input() creates a signal.
Mixing Styles Can Be Confusing
A large application may contain both:
@Input()
and:
input()
especially during gradual modernization.
Key Points
input()
Used for:
Parent → Child
Example:
name = input<string>();
Parent:
<app-product [name]="productName">
</app-product>
Read it using:
name()
Required:
name = input.required<string>();
Default:
quantity = input(1);
output()
Used for:
Child → Parent
Declare:
productAdded = output<string>();
Emit:
this.productAdded.emit('Laptop');
Parent listens:
<app-product
(productAdded)="onProductAdded($event)">
</app-product>
Modern vs Traditional Syntax
Traditional Angular Modern Angular
@Input() input()
│ │
▼ ▼
Parent → Child Parent → Child
@Output() output()
+ │
EventEmitter │
│ │
▼ ▼
Child → Parent Child → Parent
The essential comparison:
| Direction | Traditional | Modern |
|---|---|---|
| Parent → Child | @Input() |
input() |
| Child → Parent | @Output() + EventEmitter |
output() |
Interview Questions & Answers
Q1. What is input() in Angular?
input() is the modern signal-based API for defining a component input. It allows a child component to receive data from its parent.
name = input<string>();
Q2. What is output() in Angular?
output() defines a custom component event that allows a child component to emit data or notify its parent.
saved = output<string>();
Q3. How do you read an input() value?
Because input() returns an input signal, read it by calling it:
name()
For example:
<h2>{{ name() }}</h2>
Q4. How do you emit an output() event?
Use:
this.saved.emit('Laptop');
The parent can receive it using:
(saved)="onSaved($event)"
Q5. How do you create a required input?
Use:
product = input.required<Product>();
This indicates that the parent must provide the input.
Q6. Does output() require EventEmitter?
No.
Modern Angular:
saved = output<string>();
doesn't require explicitly creating an EventEmitter.
Q7. What is the difference between input() and output()?
input() receives data into the child, while output() sends an event/data out of the child.
Parent
│
│ input()
▼
Child
│
│ output()
▼
Parent
Q8. Are @Input() and @Output() obsolete?
They are older decorator-based APIs and remain important in existing Angular codebases. For modern Angular development, the signal-based input() and output() APIs provide a more concise approach, with input() integrating directly with Angular Signals.
Interview-Ready Answer
input()andoutput()are modern Angular APIs for component communication.input()creates a signal-based input that receives data from a parent component, whileoutput()creates a custom event that a child can emit to its parent. They provide modern alternatives to the traditional@Input()and@Output()withEventEmitterpatterns.