JavaScript Window History

The window.history object provides access to the browser's session history. It allows you to navigate back and forward, and to manipulate the history stack using pushState() and replaceState() for modern single-page applications.

history.back() and history.forward()

history.back() navigates to the previous page in history (like clicking the browser's Back button). history.forward() navigates to the next page in history (like clicking Forward).

Basic Navigation
// Go back one page
// history.back();

// Go forward one page
// history.forward();

console.log('history.back() = browser Back button');
console.log('history.forward() = browser Forward button');

// Check history length
console.log('History entries: ' + history.length);

// Note: history.length includes the current page
console.log('A length of 1 means no previous pages');

history.go()

history.go() navigates to a specific page relative to the current position. Positive numbers go forward, negative numbers go back. go(0) reloads the current page.

Using history.go()
// Go back 2 pages
// history.go(-2);

// Go forward 1 page
// history.go(1);

// Reload current page
// history.go(0);
// history.go();  // Same as go(0)

console.log('history.go(-1) = same as back()');
console.log('history.go(1)  = same as forward()');
console.log('history.go(-2) = go back 2 pages');
console.log('history.go(0)  = reload current page');
console.log('history.go()   = reload current page');

console.log('\nhistory.length: ' + history.length);

history.pushState()

pushState() adds a new entry to the browser history stack without navigating to a new page. This is the foundation of client-side routing in single-page applications (SPAs).

Adding History Entries
// pushState(state, title, url)
// state: data associated with the history entry
// title: mostly ignored by browsers (pass empty string)
// url: the URL to show in the address bar

// history.pushState({ page: 1 }, '', '/page1');
// history.pushState({ page: 2 }, '', '/page2');
// history.pushState({ page: 3 }, '', '/page3');

console.log('pushState adds a history entry');
console.log('URL in address bar changes');
console.log('Page does NOT reload');
console.log('Back button now works with SPA routing');

// Simulating state management
const historyStack = [];
function pushState(state, url) {
  historyStack.push({ state, url });
  console.log('Pushed: ' + url + ' (state: ' + JSON.stringify(state) + ')');
}

pushState({ page: 'home' }, '/');
pushState({ page: 'about' }, '/about');
pushState({ page: 'contact' }, '/contact');
console.log('Stack size: ' + historyStack.length);

history.replaceState()

replaceState() modifies the current history entry instead of adding a new one. The URL and state are updated, but the history length stays the same.

Replacing History Entries
// replaceState(state, title, url)
// Replaces current entry instead of adding new one

// history.replaceState({ page: 'updated' }, '', '/new-url');

console.log('replaceState modifies the CURRENT entry');
console.log('Does NOT add a new entry');
console.log('history.length stays the same');

console.log('\nUse cases:');
console.log('  - Update URL after form submission');
console.log('  - Correct a URL without adding to history');
console.log('  - Store updated state for current page');

console.log('\npushState vs replaceState:');
console.log('  pushState: adds entry, length increases');
console.log('  replaceState: updates entry, length unchanged');

The popstate Event

The popstate event fires when the user navigates the history (clicks Back/Forward or calls history.back()/forward()/go()). It does NOT fire for pushState() or replaceState() calls.

Listening for Navigation
// Listen for back/forward navigation
// window.addEventListener('popstate', function(event) {
//   console.log('Navigation occurred!');
//   console.log('State: ' + JSON.stringify(event.state));
//   // Update the page content based on state
//   if (event.state) {
//     renderPage(event.state.page);
//   }
// });

// Simulating SPA routing
function handleRoute(state) {
  if (!state) {
    console.log('Rendering: Home (default)');
    return;
  }
  console.log('Rendering: ' + state.page);
}

// Simulating navigation events
handleRoute({ page: 'About' });
handleRoute({ page: 'Contact' });
handleRoute(null);  // Back to home

console.log('\npopstate fires on Back/Forward button clicks');
console.log('event.state contains the pushState data');
MethodAdds EntryPage ReloadFires popstate
pushState()YesNoNo
replaceState()No (replaces)NoNo
back()NoMay reloadYes
forward()NoMay reloadYes
go(n)NoMay reloadYes
📝 Note: pushState() and replaceState() only change the URL displayed in the address bar; they do NOT load a new page. Your JavaScript must handle the routing logic to update the page content accordingly. This is how modern SPA frameworks (React Router, Vue Router) implement client-side routing.
Exercise:
When does the popstate event fire?
Try it YourselfCtrl+Enter to run
Click Run to see the output here.