← Back to Articles         
input() and output() in Modern Angular

input() and output() in Modern Angular

Published on 29 Sep 2026     8 min read Angular
Angular Components

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() and effect().
  • 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() and output() are modern Angular APIs for component communication. input() creates a signal-based input that receives data from a parent component, while output() creates a custom event that a child can emit to its parent. They provide modern alternatives to the traditional @Input() and @Output() with EventEmitter patterns.

← Back to Articles