Question
In Angular, how can you navigate from one routed component to another while passing data along, without rendering the target component as a child directive?
For example, suppose FirstComponent contains a button that should navigate to SecondComponent and make some values available there.
<div class="button" (click)="routeWithData()">Pass data and route</div>
export class FirstComponent {
constructor(private router: Router) {}
property1!: number;
property2!: string;
property3!: TypeXY; // custom object, not a primitive
routeWithData() {
// navigate here
}
}
A simple navigation might look like this:
this.router.navigate(['SecondComponent']);
And passing simple values in the URL might look like this:
this.router.navigate(['SecondComponent', { p1: this.property1, p2: this.property2 }]);
The concern is that URL-based routing works well for primitives, but not for complex objects like property3.
Another option would be to render the target component directly and pass data with @Input():
<app-second [p3]="property3"></app-second>
But that is not the goal here, because the requirement is to route to the component, not nest it inside the current template.
A shared service also seems possible:
- Store data in the service before navigating
- Read the data from the service in
SecondComponent
Is using a service the usual or most elegant solution, or are there better ways to pass data between routed Angular components with as little code as possible?
Short Answer
By the end of this page, you will understand the main ways to pass data between Angular routed components, when to use each one, and why some methods work better for simple values while others are better for complex objects. You will also learn the trade-offs between route parameters, query parameters, router navigation state, and shared services.
Concept
In Angular, routed components are created by the router, not by a parent component template. That means @Input() binding does not work the same way it does for nested components.
When you navigate to another route, Angular creates that component from the route configuration. Because of that, data must be passed through mechanisms the router understands, or through shared application state.
The common options are:
- Route parameters: good for required, URL-friendly values like IDs
- Query parameters: good for optional filters, search terms, pagination, and small primitive values
- Navigation state: good for temporary data passed during navigation without putting it in the URL
- Shared service: good for complex objects, shared state, or data that multiple components may need
Why this matters:
- URLs should remain meaningful and bookmarkable when possible
- Complex objects usually should not be serialized into the URL
- Routed components should be able to reload correctly if the page is refreshed
- Some data is app state, not route state
A useful rule is:
- If the data identifies what page to show, put it in the route
- If the data changes how the page behaves, query params may fit
- If the data is temporary or complex, use navigation state or a service
For modern Angular applications, the most practical choices are usually:
- Route params for IDs
- Query params for optional URL-visible values
- Shared service or state management for complex objects
- Navigation extras
statefor temporary non-URL data
For example, instead of passing a full object, you often pass its in the route and let load the full object again. That is usually the cleanest and most reliable approach.
Mental Model
Think of Angular routing like sending someone to a new office.
- Route params are like writing the office number on the address label
- Query params are like adding extra delivery notes
- Navigation state is like handing the courier a private note that is not written on the envelope
- Shared service is like storing a file in a shared cabinet that both offices can access
Now imagine sending a whole chair through the address label. That does not make sense. In the same way, large or complex objects usually do not belong in the URL.
So the mental model is:
- Put small identifying information in the route
- Put optional URL-visible settings in query params
- Put rich shared data in a service or app state
- Use navigation state only for temporary handoff data
Syntax and Examples
1. Route parameters
Use route parameters when the destination component can load what it needs from an ID.
this.router.navigate(['/second', this.property1]);
Route configuration:
const routes: Routes = [
{ path: 'second/:id', component: SecondComponent }
];
Read it in the target component:
import { ActivatedRoute } from '@angular/router';
export class SecondComponent {
constructor(private route: ActivatedRoute) {}
ngOnInit() {
const id = this.route.snapshot.paramMap.get();
.(id);
}
}
Step by Step Execution
Consider this example using a route parameter and a service together.
// first.component.ts
routeWithData() {
this.dataService.setData(this.property3);
this.router.navigate(['/second', this.property1]);
}
// second.component.ts
ngOnInit() {
const id = this.route.snapshot.paramMap.get('id');
const item = this.dataService.getData();
console.log('Route ID:', id);
console.log('Shared object:', item);
}
Here is what happens step by step:
- The user clicks the button in
FirstComponent.
Real World Use Cases
Passing an ID to load a record
A product list page routes to a product details page:
this.router.navigate(['/products', product.id]);
The details page uses the ID to request fresh data from an API.
Sending search filters in the URL
A reports page stores filters in query params:
this.router.navigate(['/reports'], {
queryParams: { status: 'open', page: 2 }
});
This is useful because the URL can be bookmarked or shared.
Passing temporary form data during navigation
A multi-step form may use navigation state for data collected in the previous step.
this.router.navigate(['/checkout/review'], {
state: { draftOrder: order }
});
Sharing selected objects between pages
An admin dashboard may store a selected object in a service before routing to an editor page.
Real Codebase Usage
In real Angular projects, developers usually avoid passing full objects through the URL.
Common patterns include:
1. Pass only an ID, then fetch data
This is the most common and reliable pattern.
this.router.navigate(['/users', user.id]);
Then in the target component:
const id = this.route.snapshot.paramMap.get('id');
this.userService.getUserById(id!).subscribe();
Why teams prefer it:
- URLs stay clean
- Pages can reload correctly
- Data comes from a single source of truth
2. Use guard clauses when route data is missing
When using service-based state or navigation state, the destination may not have data after refresh.
ngOnInit() {
const data = this.dataService.getData();
(!data) {
..([]);
;
}
. = data;
}
Common Mistakes
Using click instead of (click)
Broken:
<div class="button" click="routeWithData()">Pass data and route</div>
Correct Angular event binding:
<div class="button" (click)="routeWithData()">Pass data and route</div>
Trying to use @Input() with a routed component
Broken idea:
<app-second [p3]="property3"></app-second>
This renders SecondComponent inside the current template. It does not navigate through the router.
Use @Input() only for parent-child component relationships in templates.
Comparisons
| Approach | Best for | Visible in URL | Works with complex objects | Survives refresh well | Typical use |
|---|---|---|---|---|---|
| Route params | Required identifiers | Yes | No | Yes | /users/42 |
| Query params | Optional settings | Yes | Usually no | Yes | ?page=2&sort=name |
| Navigation state | Temporary navigation data | No | Yes | Not reliably | Passing data during one navigation |
| Shared service | In-memory shared state |
Cheat Sheet
Quick reference
Navigate with a route param
this.router.navigate(['/second', id]);
Read a route param
const id = this.route.snapshot.paramMap.get('id');
Navigate with query params
this.router.navigate(['/second'], {
queryParams: { page: 1, filter: 'active' }
});
Read query params
const page = this.route.snapshot.queryParamMap.get('page');
Navigate with temporary state
FAQ
How do I pass data to another component through Angular routing?
Use route params, query params, navigation state, or a shared service depending on the type of data. IDs usually go in the route, while complex objects usually go in a service or navigation state.
Can I pass an object through Angular route parameters?
Technically you can serialize data, but it is usually not a good idea. Complex objects do not belong in the URL in most cases. Pass an ID instead, or use a service.
Should I use @Input() for routed components?
No. @Input() is for components rendered directly in a parent template. Routed components are created by the router.
What is the best way to pass complex data between Angular routes?
A shared service is a common choice for complex objects. If the page must survive refreshes, pass an ID in the route and reload the object from a data source.
Is Angular router navigation state better than a service?
It depends. Navigation state is convenient for one-time temporary data during navigation. A service is better for broader shared state and reuse.
What happens to service-passed data if the page refreshes?
If the service stores data only in memory, the data is lost on refresh. That is why route IDs plus reloading are often more reliable.
When should I use query parameters instead of route parameters?
Use query parameters for optional values like filters, search, sorting, pagination, or tab state. Use route parameters for required path identity.
Mini Project
Description
Build a small Angular flow where a user selects a product in one component and navigates to a details page. The route should pass the product ID in the URL, while a shared service temporarily holds the full selected object. This demonstrates the difference between route-friendly data and complex in-memory data.
Goal
Create a routed navigation flow that passes an ID through the URL and accesses a complex object through a shared Angular service.
Requirements
- Create a
ProductServicethat stores and returns a selected product object. - In the first component, save the selected product to the service before navigating.
- Navigate to the second component using the product ID as a route parameter.
- In the second component, read the route parameter and the stored product.
- Add a fallback message if the service no longer has the product after refresh.
Keep learning
Related questions
@Directive vs @Component in Angular: Differences, Use Cases, and When to Use Each
Learn the difference between @Directive and @Component in Angular, including use cases, examples, and when to choose each.
Angular (change) vs (ngModelChange): What’s the Difference?
Learn the difference between Angular (change) and (ngModelChange), when each fires, and which one to use in forms and inputs.
Angular @ViewChild Returning Undefined: Lifecycle, Child Components, and Fixes
Learn why Angular @ViewChild can be undefined, when it becomes available, and how to access child components correctly using lifecycle hooks.