Nested Routes

Parent Template Renders `{{outlet}}`, Children Fill It In

Nested Routes

Parent routes can render shared markup; child routes are rendered inside the parent's `{{outlet}}`. Perfect for sidebars, tabs, and dashboards.

4 min read Level 2/5 #ember#routing#nesting
What you'll learn
  • Nest routes in the router
  • Use `{{outlet}}` in a parent template
  • Apply a shared layout via a parent route

Nested routes let you share layout, data, and authorization across a family of pages. The parent’s template renders once, and child templates plug in via {{outlet}}.

Declare Nested Routes

// app/router.js
Router.map(function () {
  this.route('admin', function () {
    this.route('users');
    this.route('posts');
    this.route('settings');
  });
});

This produces URLs /admin/users, /admin/posts, /admin/settings and a /admin index. The route files live at app/routes/admin.js, app/routes/admin/users.js, and so on.

Render the Parent Layout

{{! app/templates/admin.hbs }}
<div class="admin-layout">
  <aside class="sidebar">
    <LinkTo @route="admin.users">Users</LinkTo>
    <LinkTo @route="admin.posts">Posts</LinkTo>
    <LinkTo @route="admin.settings">Settings</LinkTo>
  </aside>
  <main class="content">
    {{outlet}}
  </main>
</div>

{{outlet}} is where the active child renders.

Child Template

{{! app/templates/admin/users.hbs }}
<h1>Users</h1>
<table>
  {{#each @model as |user|}}
    <tr><td>{{user.name}}</td><td>{{user.email}}</td></tr>
  {{/each}}
</table>

Parent Data Reaches Children

Models loaded by the parent are available to children via this.modelFor('admin'):

// app/routes/admin/users.js
import Route from '@ember/routing/route';

export default class AdminUsersRoute extends Route {
  async model() {
    const admin = this.modelFor('admin'); // the parent's model
    return this.store.query('user', { tenant: admin.tenantId });
  }
}
The model Hook →