Navigate Programmatically With `router.transitionTo`
Transitions
Use the router service to navigate from code — works in routes, components, and services. Pass dynamic segments and query params as needed.
What you'll learn
- Inject the router service
- Call router.transitionTo with a route name
- Pass dynamic segments and queryParams
<LinkTo> handles 90% of navigation declaratively. For the rest — after form submission, on auth changes, after a delay — use the router service to navigate from code.
Inject the Router Service
// app/components/login-form.js
import Component from '@glimmer/component';
import { service } from '@ember/service';
import { action } from '@ember/object';
export default class LoginForm extends Component {
@service router;
@service auth;
@action
async submit(event) {
event.preventDefault();
await this.auth.login(this.args.email, this.args.password);
this.router.transitionTo('dashboard');
}
} transitionTo accepts a route name (dotted like admin.users), not a URL.
Dynamic Segments
this.router.transitionTo('post', postId);
this.router.transitionTo('comment', postId, commentId); Pass segments as positional arguments in the order they appear in app/router.js.
Query Params
this.router.transitionTo('posts', {
queryParams: { page: 2, sort: 'newest' },
}); replaceWith vs transitionTo
this.router.replaceWith('signin'); // no browser history entry
this.router.transitionTo('signin'); // adds a history entry Use replaceWith for redirects (failed auth, deprecated URLs) so the user’s back button does not return to the broken page.
Aborting a Transition
In a route’s beforeModel:
beforeModel(transition) {
if (!this.auth.isLoggedIn) {
transition.abort();
this.router.transitionTo('signin');
}
}