Backbone Routers — URL-Based Navigation
In this tutorial, you will learn about Backbone Routers. We cover key concepts, practical examples, and best practices to help you master this topic.
Backbone Routers map URL fragments to application states. When the URL changes, the Router executes a method that typically renders the appropriate View. This makes single-page applications navigable, bookmarkable, and history-aware.
What You'll Learn
You'll learn how to define routes, extract URL parameters, trigger route methods, handle route events, and structure navigation in a Backbone application.
Why It Matters
Without a Router, users cannot bookmark pages or use the browser's back button. Routers make SPAs behave like traditional web pages while staying on a single page load.
Real-World Use
A security dashboard has routes like #alerts/critical, #reports/daily, and #settings/notifications. Each route loads the corresponding View, and users can bookmark any page.
flowchart LR
A[URL change] --> B[Backbone.History]
B --> C[Router matches route]
C --> D[Route handler]
D --> E[Create View]
E --> F[Render into #app]
Defining a Router
Use Backbone.Router.extend() with a routes hash that maps URL patterns to method names.
var AppRouter = Backbone.Router.extend({
routes: {
'': 'home',
'tasks': 'showTasks',
'tasks/:id': 'showTask',
'tasks/:id/edit': 'editTask',
'about': 'showAbout',
'*path': 'notFound'
},
home: function() {
console.log('Navigated to home');
},
showTasks: function() {
console.log('Navigated to tasks list');
},
showTask: function(id) {
console.log('Navigated to task:', id);
},
editTask: function(id) {
console.log('Editing task:', id);
},
showAbout: function() {
console.log('Navigated to about page');
},
notFound: function(path) {
console.log('Route not found:', path);
}
});
var router = new AppRouter();
Backbone.history.start();
// Simulate navigation
router.navigate('tasks/42', { trigger: true });
Expected output:
Navigated to task: 42
Route Parameters and Splats
Routes support three parameter types: :param for named segments, *splat for catch-all segments, and optional segments with parentheses.
var TaskRouter = Backbone.Router.extend({
routes: {
// Named parameters
'user/:userId/task/:taskId': 'showTask',
// Splat (catch-all)
'files/*filepath': 'showFile',
// Optional segments
'search/:query(/page/:num)': 'search',
// Query string style (manual)
'filter/:category/:status': 'filter'
},
showTask: function(userId, taskId) {
console.log('User:', userId, 'Task:', taskId);
},
showFile: function(filepath) {
console.log('File path:', filepath);
},
search: function(query, num) {
console.log('Search:', query, 'Page:', num || 1);
},
filter: function(category, status) {
console.log('Filter:', category, status);
}
});
var router = new TaskRouter();
Backbone.history.start();
router.navigate('user/5/task/99', { trigger: true });
router.navigate('files/documents/reports/q3.pdf', { trigger: true });
router.navigate('search/backbone/page/3', { trigger: true });
Expected output:
User: 5 Task: 99
File path: documents/reports/q3.pdf
Search: backbone Page: 3
Route Events
Routers fire events when navigating. Listen for route:name or the generic route event.
var AnalyticsRouter = Backbone.Router.extend({
routes: {
'': 'home',
'products/:id': 'showProduct'
},
initialize: function() {
this.on('route', function(route, params) {
console.log('Page viewed:', route, params);
// Track analytics
this.trackPageView(route, params);
});
this.on('route:showProduct', function(id) {
console.log('Product view analytics for:', id);
});
},
trackPageView: function(route, params) {
console.log('Sending to analytics:', '/' + route + (params.length ? '/' + params.join('/') : ''));
},
home: function() {
// Render home view
},
showProduct: function(id) {
this.on('route:showProduct', function() {
console.log('Product loaded:', id);
});
}
});
var router = new AnalyticsRouter();
Backbone.history.start();
router.navigate('products/42', { trigger: true });
Expected output:
Page viewed: showProduct ['42']
Product view analytics for: 42
Sending to analytics: /showProduct/42
Router with Views
The real power of Routers is connecting URLs to Views.
var HomeView = Backbone.View.extend({
render: function() {
this.$el.html('<h1>Welcome</h1><p>Home page content</p>');
return this;
}
});
var TaskListView = Backbone.View.extend({
tagName: 'ul',
render: function() {
this.$el.html('<li>Task 1</li><li>Task 2</li><li>Task 3</li>');
return this;
}
});
var TaskDetailView = Backbone.View.extend({
render: function() {
this.$el.html('<h1>Task ' + this.options.id + '</h1><p>Task details here</p>');
return this;
}
});
var AppRouter = Backbone.Router.extend({
routes: {
'': 'home',
'tasks': 'listTasks',
'tasks/:id': 'showTask'
},
home: function() {
var view = new HomeView();
$('#app').html(view.render().el);
},
listTasks: function() {
var view = new TaskListView();
$('#app').html(view.render().el);
},
showTask: function(id) {
var view = new TaskDetailView({ id: id });
$('#app').html(view.render().el);
}
});
var router = new AppRouter();
Backbone.history.start();
router.navigate('tasks/42', { trigger: true });
console.log($('#app').html());
Expected output:
<h1>Task 42</h1><p>Task details here</p>
Hash-Based vs PushStateurlAppend
Backbone supports both hash-based URLs (#tasks/1) and HTML5 History API (/tasks/1).
// Hash-based (default)
Backbone.history.start();
// PushState (requires server-side support)
Backbone.history.start({ pushState: true });
// Root option for apps not at the domain root
Backbone.history.start({
pushState: true,
root: '/app/'
});
// Silent start (no initial route dispatch)
Backbone.history.start({ silent: true });
Use navigate with {trigger: true, replace: true} to update the URL without creating a browser history entry.
var Router = Backbone.Router.extend({
routes: {
'': 'home',
'tasks': 'tasks'
},
home: function() { console.log('Home'); },
tasks: function() { console.log('Tasks'); }
});
var r = new Router();
Backbone.history.start();
// Navigate with history entry
r.navigate('tasks', { trigger: true });
// Navigate without history entry (replace)
r.navigate('tasks/123', { trigger: true, replace: true });
Common Mistakes
- Forgetting to call
Backbone.history.start(). Without it, routes are never matched. The Router exists but does nothing. - Using absolute paths instead of route fragments. Do not include
#in route definitions. Backbone adds it automatically. - Not defining a catch-all route for 404s. Without
'*path': 'notFound', unmatched routes silently do nothing. - Creating multiple Router instances. A Backbone app typically has one Router. Multiple routers can cause conflicting route handlers.
- Assuming
navigate()triggers the route by default. Thetrigger: trueoption is required. Without it, the URL changes but no handler runs.
Practice Questions
- What is the difference between
:paramand*splatin routes? - How do you start Backbone history?
- What does
navigate()do without thetriggeroption? - How do you use HTML5 pushState with Backbone?
- Challenge: Create a blog Router with routes for posts list, single post by slug, posts by category, and author page. Each route should render the corresponding View into
#content.
FAQ
Mini Project
Create a DocsRouter with routes for /guide, /guide/:section, /api/:module/:method, /search/:query, and a catch-all. Each route renders a View into #main. Add breadcrumb navigation that updates on route change.
What's Next
Now that you understand Routers, learn Backbone History for deeper control over browser navigation. Then explore Backbone Events for the application event system.
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro