Backbone History — Managing Browser Navigation
In this tutorial, you will learn about Backbone History. We cover key concepts, practical examples, and best practices to help you master this topic.
Backbone.History is the engine behind Backbone's routing system. It listens for URL changes, matches them against defined routes, and dispatches the appropriate handler. It supports both hash-based URLs and HTML5 pushState.
What You'll Learn
You'll learn how Backbone.History works, how to configure it for hash and pushState modes, how it handles the back button, and how to manage URL fragments manually.
Why It Matters
Browser navigation is the foundation of UX in SPAs. Users expect the back button to work. Backbone.History makes this possible with minimal configuration.
Real-World Use
A multi-step configuration wizard uses Backbone.History to let users navigate between steps using the browser back/forward buttons. Each step has a unique URL that can be bookmarked.
flowchart LR
A[popstate/hashchange] --> B[Backbone.History]
B --> C{Match route?}
C -->|Yes| D[Execute handler]
C -->|No| E[Do nothing]
D --> F[Update URL]
F --> A
Starting History
Backbone.history.start() begins monitoring URL changes. It dispatches the current route immediately.
var Router = Backbone.Router.extend({
routes: {
'': 'home',
'about': 'about'
},
home: function() { console.log('Home loaded'); },
about: function() { console.log('About loaded'); }
});
var router = new Router();
// Start history — dispatches the current URL
Backbone.history.start();
console.log('Current fragment:', Backbone.history.fragment);
Expected output:
Home loaded
Current fragment: (based on current URL)
Hash-Based Routing (Default)
The default mode uses the URL hash: http://example.com/#tasks/42. This works without server configuration.
var Router = Backbone.Router.extend({
routes: {
'tasks': 'list',
'tasks/:id': 'detail'
},
detail: function(id) {
console.log('Hash routing. Task ID:', id);
console.log('Fragment:', Backbone.history.fragment);
}
});
var r = new Router();
Backbone.history.start();
r.navigate('tasks/99', { trigger: true });
Expected output:
Hash routing. Task ID: 99
Fragment: tasks/99
PushState (HTML5 History API)
PushState removes the hash, creating clean URLs: http://example.com/tasks/42. This requires server configuration to serve index.html for all routes.
var Router = Backbone.Router.extend({
routes: {
'tasks': 'list',
'tasks/:id': 'detail'
},
detail: function(id) {
console.log('PushState routing. Task ID:', id);
}
});
var r = new Router();
Backbone.history.start({ pushState: true });
// Click a link — prevent default, navigate manually
$(document).on('click', 'a[href^="/"]', function(e) {
e.preventDefault();
var path = $(e.currentTarget).attr('href').replace(/^\//, '');
r.navigate(path, { trigger: true });
});
Expected output:
PushState routing. Task ID: 99
Handling the Root
When the app lives in a subdirectory, set the root option.
// App served from http://example.com/myapp/
var Router = Backbone.Router.extend({
routes: {
'': 'home',
'dashboard': 'dashboard'
},
home: function() { console.log('Home at root of app'); },
dashboard: function() { console.log('Dashboard'); }
});
var r = new Router();
Backbone.history.start({
root: '/myapp/'
});
console.log('Root set to:', Backbone.history.options.root);
Expected output:
Home at root of app
Root set to: /myapp/
Silent Start and Manual Dispatch
Use {silent: true} to start history without dispatching the current route. Useful when you want to handle the initial route manually.
var Router = Backbone.Router.extend({
routes: {
'': 'home',
'settings': 'settings'
},
home: function() { console.log('Home'); },
settings: function() { console.log('Settings'); }
});
var r = new Router();
// Start silently — no route dispatch
Backbone.history.start({ silent: true });
console.log('Started silently, fragment:', Backbone.history.fragment);
// Later, load the initial route
r.navigate(Backbone.history.fragment, { trigger: true });
Expected output:
Started silently, fragment: (current URL fragment)
Home
Custom URL Manipulation
Access Backbone.History directly for advanced URL operations.
var Router = Backbone.Router.extend({
routes: { '*path': 'catchAll' },
catchAll: function(path) { console.log('Route:', path); }
});
var r = new Router();
Backbone.history.start();
// Get current fragment
console.log('Current:', Backbone.history.fragment);
// Navigate and replace current history entry
r.navigate('tasks/1', { trigger: true, replace: true });
// Multiple navigations
r.navigate('tasks/2', { trigger: true });
r.navigate('tasks/3', { trigger: true });
// Go back in history
Backbone.history.on('route', function() {
console.log('Back/forward pressed. Now at:', Backbone.history.fragment);
});
Expected output:
Current: (varies)
Route: tasks/1
Route: tasks/2
Route: tasks/3
Stopping History
Backbone.history.stop() stops listening for URL changes. Useful for cleanup in tests or when tearing down an app.
var Router = Backbone.Router.extend({
routes: { 'test': 'test' },
test: function() { console.log('Test route'); }
});
var r = new Router();
Backbone.history.start();
r.navigate('test', { trigger: true });
// Stop listening
Backbone.history.stop();
console.log('History stopped');
// This will NOT trigger the route
r.navigate('test', { trigger: true });
console.log('No route should fire after stop');
Expected output:
Test route
History stopped
No route should fire after stop
Common Mistakes
- Starting history before defining all routes. Routes defined after
Backbone.history.start()are not matched. Define all routes first. - Not configuring the server for pushState. Without server-side fallback to
index.html, pushState URLs cause 404 errors on page refresh. - Multiple
Backbone.history.start()calls. Calling start twice throws an error. CheckBackbone.History.startedbefore starting. - Using hash routing and pushState together. Choose one mode. Mixing them causes inconsistent behavior across browsers.
- Not calling
navigate()withtrigger:truefor programmatic navigation. The URL changes but the View does not update. Always includetrigger:trueunless you intend to stay silent.
Practice Questions
- What is the difference between hash routing and pushState?
- What server configuration does pushState require?
- How do you navigate without adding a browser history entry?
- What does
{silent: true}do when starting history? - Challenge: Create a Router that supports both hash and pushState modes. Detect pushState support and fall back to hash if unavailable. Log which mode is active.
FAQ
Mini Project
Create a simple app with three pages (Home, Products, Contact) using Backbone.History with hash routing. Add navigation links that use navigate() with trigger:true. Track route changes and log them. Add a back-button handler that detects when the user navigates back.
What's Next
Now that you understand History, learn Backbone Events for the custom event system. Then explore Backbone Event Aggregator for decoupled component communication.
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro