Skip to content

ViewTransitions should work on popstate #94369

@kgroat

Description

@kgroat

Link to the code that reproduces this issue

https://github.com/vercel/next-view-transition-example

To Reproduce

  1. Go to https://view-transition-example.vercel.app/blog
  2. Click "Read More" on any post
  3. Notice the avatars transition appropriately
  4. Click back in the browser
  5. Notice that the avatars don't transition back

Current vs. Expected behavior

Currently, the view transitions work correctly when navigating using <Link>, router.push(), or router.replace(), but not when navigating using the browser's native navigation tools.

I would expect the transitions to work regardless.

Provide environment information

Operating System:
  Platform: darwin
  Arch: arm64
  Version: Darwin Kernel Version 25.5.0: Mon Apr 27 20:38:56 PDT 2026; root:xnu-12377.121.6~2/RELEASE_ARM64_T6000
  Available memory (MB): 16384
  Available CPU cores: 10
Binaries:
  Node: 26.2.0
  npm: 11.13.0
  Yarn: N/A
  pnpm: 9.15.4
Relevant Packages:
  next: 16.0.10 // There is a newer version (16.2.7) available, upgrade recommended! 
  eslint-config-next: N/A
  react: 19.0.0
  react-dom: 19.0.0
  typescript: 5.8.2
Next.js Config:
  output: N/A

Which area(s) are affected? (Select all that apply)

Linking and Navigating

Which stage(s) are affected? (Select all that apply)

next dev (local), next start (local), Vercel (Deployed), Other (Deployed)

Additional context

I looked into app-router.tsx but it looks like it already uses startTransition for the popstate handler.

One thing I noticed that might be related is that during the in-app navigation, it renders the component, but when navigating using the browser's native navigation, it doesn't render but must be using a cached version. I don't know if this is related.

I have another application not using SSG and it does re-render the component, but still has the same behavior, so that could be a red herring.

I created the following small hacky component that can be placed in the root layout.tsx which fixes the issue, but it can introduce bugs and I'd prefer if this was fixed at the framework level.

'use client';
import { startTransition, useEffect } from 'react';
import { useRouter } from 'next/navigation';

export function BrowserNavigationTransition() {
  const router = useRouter();

  useEffect(() => {
    const abortController = new AbortController();

    window.addEventListener('popstate', (event: PopStateEvent) => {
      // 1. Prevent Next.js from processing the standard instant back navigation
      event.stopPropagation();

      // 2. Wrap the router event inside a transition
      startTransition(() => {
        // 3. Manually trigger a refresh to update the content based on the new URL
        router.refresh();
      });
    }, {
      signal: abortController.signal,
    });

    return () => abortController.abort();
  }, [router]);

  return null;
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    Linking and NavigatingRelated to Next.js linking (e.g., <Link>) and navigation.

    Type

    No fields configured for Bug.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions