Detecting when the browser's "back" and "forward" buttons are pressed.

Programming
Published on October 18, 2025
Article Image

Introduction

How can we detect when a page transition occurs using the browser's "back" and "forward" buttons?

While there is a method using the popstate event 1, popstate does not occur during normal page transitions (moving to a different page). It is only meaningful when managing history with pushState within the same page. When moving to a different page, the previous page is destroyed, and thus all event handlers, including popstate, are also destroyed. Furthermore, information to determine whether it was a "back" or "forward" action is not provided.

There is also a method using performance.navigation.type 2. This can be used for transitions to different pages, but if HTML caching is disabled, the type value will always be 1: TYPE_RELOAD, making it indistinguishable from a normal page transition. Even when caching is enabled, the type value for "back" and "forward" is 2: TYPE_BACK_FORWARD, so, like popstate, no information is provided to determine whether it was a "back" or "forward" action. Furthermore, performance.navigation.type has been deprecated, so browsers may stop supporting it in the future.

Here, we introduce a method that combines sessionStorage and history.state 3.
The differences between each method are as follows.

Method Back/Forward Detection When Cache is Disabled Notes
popstate 4 For SPAs
performance.navigation.type ✓ (Old spec) ✗ (Always reload) Deprecated
sessionStorage + history.state For normal page transitions

Code

  • Normal page transition
  • Transition by back button
  • Transition by forward button
  • None of the above

The code to determine these is as follows.

window.addEventListener('pageshow', function () {
    // Get the timestamp from sessionStorage before the transition
    const prevTimestamp = Number(sessionStorage.getItem('prevTimestamp') ?? -1);

    // Get the timestamp saved in history.state
    let currentTimestamp = history.state?.timestamp ?? null;

    // Determine if it's a new page transition
    if (!currentTimestamp) {
        console.log('新しいページ遷移'); // New page transition
        currentTimestamp = Date.now();
        if (currentTimestamp === prevTimestamp) {
            currentTimestamp += 1; // I don't think the timestamps will be the same, but just in case
        }

        // Save timestamp to history.state
        history.replaceState({
            timestamp: currentTimestamp
        }, '');
    } else {
        console.log('履歴内の移動'); // Movement within history
        if (prevTimestamp > currentTimestamp) {
            console.log('戻るボタン'); // Back button
        } else if (prevTimestamp < currentTimestamp) {
            console.log('進むボタン'); // Forward button
        } else {
            console.log('どれでもない'); // None of the above
        }
    }

    sessionStorage.setItem('prevTimestamp', currentTimestamp);
});

Explanation

For example, let's consider a case where page transitions occur as follows.

History Order Timestamp Page
1 15:00 page1
2 15:10 page2
3 15:20 page3
4 * 15:30 page1

* indicates the currently displayed page.
Since a timestamp is set in history.state for each new page transition, it looks like this:

History Order history.state.timestamp Page
1 15:00 page1
2 15:10 page2
3 15:20 page3
4 * 15:30 page1

The prevTimestamp stored in Session Storage is 15:30.

Back

If you navigate with the "back" button here, you move to the 3rd page, and history.state.timestamp is 15:20.

prevTimestamp(15:30) > history.state.timestamp(15:20)

So we know it's a "back" action.

History Order history.state.timestamp Page
1 15:00 page1
2 15:10 page2
3 * 15:20 page3
4 15:30 page1

prevTimestamp is updated to 15:20.

Forward

Next, if you navigate with the "forward" button, you move to the 4th page, and history.state.timestamp is 15:30.

prevTimestamp(15:20) < history.state.timestamp(15:30)

So we know it's a "forward" action.

History Order history.state.timestamp Page
1 15:00 page1
2 15:10 page2
3 15:20 page3
4 * 15:30 page1

prevTimestamp is updated to 15:30.

New Page Transition

Now, let's try a normal page transition instead of "back" or "forward". Suppose you click a link within the page or directly type a URL into the browser's address bar to display page3.
At this time, a new entry is added to the history.

Since it's a new entry, history.state contains nothing.

Therefore, we know it's a "new page transition".

Prepare for the next determination by setting the current timestamp in history.state.

History Order history.state.timestamp Page
1 15:00 page1
2 15:10 page2
3 15:20 page3
4 15:30 page1
5 * 15:40 page3

prevTimestamp is updated to 15:40.

None of the above

Let's perform a normal page transition again. We'll navigate to page3 in the same way as before.
Alternatively, reloading will result in the same behavior.

At this time, no new entry is added to the history.
history.state.timestamp is set to 15:40.

prevTimestamp(15:40) = history.state.timestamp(15:40)

This is the "none of the above" pattern. The history state does not change.

History Order history.state.timestamp Page
1 15:00 page1
2 15:10 page2
3 15:20 page3
4 15:30 page1
5 * 15:40 page3

prevTimestamp remains 15:40.

It seems there is a browser history specification that states: if you display a page with the same URL as the currently displayed page, the history does not increase. This applies not only to the beginning of the history; if you re-display the same page while page1 or page2 is showing, the history will not increase.

While I used the phrase "none of the above," "transitioned to the same page" might be a more appropriate expression.

Notes

  • pageshow event 5
    When a page is restored from cache, the load event does not occur, so pageshow is used.
  • history.state
    In the code example, state is overwritten with history.replaceState, but if state is being used for other purposes, care must be taken to avoid mutual overwriting.

  • sessionStorage
    Since sessionStorage is data shared only within the same origin and same tab, it cannot be used for detection across different origins.

Footnotes

  1. Window: popstate イベント

  2. PerformanceNavigation.type

  3. History: state プロパティ

  4. If caching is disabled, navigating with the "back" or "forward" buttons will cause a reload, and thus the popstate handler will be destroyed.

  5. Window: pageshow イベント