Ember Routes — URL-Based Navigation and Routing
In this tutorial, you will learn about Ember Routes. We cover key concepts, practical examples, and best practices to help you master this topic.
Ember Routes map URLs to templates and data. The router.js file defines the route map, and each route automatically loads its corresponding template. Routes handle data loading through the model hook and manage transitions through lifecycle hooks.
What You'll Learn
You will learn how to define routes, create dynamic segments, nest routes, control route ordering, and understand how Ember resolves which route matches a URL.
Why It Matters
Routing is the backbone of any Ember application. Every page, every navigation, every data load starts with the router. Mastering routes means mastering application flow.
Real-World Use
An e-commerce platform uses nested routes: /products/:category/:product_id for browsing, /cart for the shopping cart, and /checkout/:step for the multi-step checkout Process.
flowchart LR
A[/products] --> B[Route: products.index]
A --> C[/products/electronics] --> D[Route: products.category]
A --> E[/products/123] --> F[Route: products.product]
G[/cart] --> H[Route: cart]
I[/checkout/shipping] --> J[Route: checkout.shipping]
Basic Route Definition
Routes are defined in app/router.js inside the Router.map() callback.
// app/router.js
Router.map(function() {
this.route('about'); // /about
this.route('contact'); // /contact
this.route('faq'); // /faq
this.route('pricing'); // /pricing
});
Each route automatically expects:
- A route handler at
app/routes/about.js - A template at
app/templates/about.hbs
Dynamic Segments
Use { path: ':param' } to capture variable URL segments.
Router.map(function() {
this.route('post', { path: '/post/:post_id' });
this.route('user', { path: '/user/:username' });
this.route('category', { path: '/category/:slug' });
});
The route handler receives the dynamic segment as an argument to model():
// app/routes/post.js
import Route from '@ember/routing/route';
export default class PostRoute extends Route {
model(params) {
console.log('Loading post:', params.post_id);
return this.store.findRecord('post', params.post_id);
}
}
Nested Routes
Routes can be nested to create hierarchical URLs and shared layouts.
Router.map(function() {
this.route('settings', function() {
this.route('profile'); // /settings/profile
this.route('security'); // /settings/security
this.route('notifications'); // /settings/notifications
});
this.route('dashboard', function() {
this.route('analytics'); // /dashboard/analytics
this.route('reports'); // /dashboard/reports
});
});
Nested routes share the parent layout via {{outlet}}:
{{! app/templates/settings.hbs }}
<div class="settings-layout">
<aside>
<LinkTo @route="settings.profile">Profile</LinkTo>
<LinkTo @route="settings.security">Security</LinkTo>
<LinkTo @route="settings.notifications">Notifications</LinkTo>
</aside>
<main>
{{outlet}} {{! Child route renders here }}
</main>
</div>
Index Routes
Every route can have an index sub-route that renders when the parent URL is matched without additional segments.
Router.map(function() {
this.route('products', function() {
// /products renders products/index
this.route('product', { path: ':product_id' }); // /products/123
});
});
Create app/routes/products/index.js and app/templates/products/index.hbs for the index route.
Route Ordering
Ember matches routes in the order they are defined. Define more specific routes before dynamic ones.
// GOOD: specific routes before dynamic
Router.map(function() {
this.route('popular'); // /popular
this.route('recent'); // /recent
this.route('post', { path: ':post_id' }); // /123
});
// BAD: dynamic route catches everything
Router.map(function() {
this.route('post', { path: ':post_id' });
this.route('popular'); // NEVER reached — :post_id matches "popular"
});
Wildcard Routes
Use { path: '*path' } for catch-all routes like 404 pages.
Router.map(function() {
this.route('not-found', { path: '/*path' });
});
// app/routes/not-found.js
import Route from '@ember/routing/route';
export default class NotFoundRoute extends Route {
model(params) {
console.warn('404 — Unknown route:', params.path);
return params;
}
}
Transition and URL Generation
// From a component or route
import { inject as service } from '@ember/service';
import Component from '@glimmer/component';
export default class NavComponent extends Component {
@service router;
goToPost(id) {
// Transition to route
this.router.transitionTo('post', id);
// Generate URL without navigating
let url = this.router.urlFor('post', id);
console.log('URL would be:', url);
}
}
{{! Template: LinkTo for navigation }}
<LinkTo @route="post" @model={{post.id}}>Read more</LinkTo>
Common Mistakes
- Defining routes in the wrong order. Dynamic segments catch all values. Define static routes before dynamic ones.
- Forgetting
{{outlet}}in parent templates. Nested route templates never render without an{{outlet}}in the parent. - Using
this.routewithout a callback inside nested routes. Resources with children usefunction()callback. Resources without children omit it. - Not creating index routes for nested resources.
/productsexpectsproducts/index.js. Without it, the template is blank. - Using
pathincorrectly.{ path: '/blog/:post_id' }defines the URL pattern. Do not include the leading/inside path — Ember adds it.
Practice Questions
- How do you define a dynamic segment in an Ember route?
- What is the purpose of
{{outlet}}in nested routes? - How do you create a catch-all 404 route?
- Why does route ordering matter?
- Challenge: Create a router with nested routes for a blog:
/blog,/blog/:category,/blog/:category/:post_id, and/about. Add an index route for blog. Verify URL matching works correctly.
FAQ
Mini Project
Create an Ember app with the following routes: home (index), products/:category/:product_id, cart, checkout/:step (with steps: shipping, payment, confirm), and a catch-all 404. Add navigation in application.hbs. Verify all routes render correct templates.
What's Next
Now that you understand routes, explore Ember Route Hooks for data loading. Then learn Ember Nested Routes for complex layouts.
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro