Scroll Restoration and View Transitions

Purpose and Scope

Scroll restoration and transition UX sit at the boundary between routing correctness and perceived application polish. In a TanStack Router app, navigation changes the URL, route match set, loaders, and rendered nested layouts; the user also expects the viewport to land in a predictable place and for the screen change to feel intentional. This page explains where that concern belongs in this repository’s documentation spine and how to approach it when the app is a standalone Router application or a TanStack Start application built on Router. The source-backed theme is that Start inherits Router’s routing system rather than replacing it.

Sources: docs/start/framework/react/overview.md, docs/start/framework/react/comparison.md

TanStack Start is described as a full-stack React framework powered by TanStack Router, with full-document SSR, streaming, server functions, client/server builds, and build-tool support through Vite or Rsbuild. That matters for scroll and transition behavior because a Start app is not merely a client-side shell: initial HTML can be rendered on the server, data can stream progressively, and navigation can involve route loaders and server-side work. The navigation UX should therefore be designed around the route lifecycle, not around isolated component animations that ignore loading, redirects, or nested layout changes.

Sources: docs/start/framework/react/overview.md

The Start comparison page reinforces the same boundary. It explicitly directs readers who are looking for routing features to the TanStack Router comparison, while reserving the Start comparison for full-stack framework capabilities such as SSR, server functions, middleware, and deployment. Scroll restoration, active navigation feedback, nested route transitions, and route-driven view changes are routing-adjacent UX features, so they should be understood first as Router behavior. Start then adds the server-rendered and streaming environment where that behavior must continue to feel consistent across first load, client navigation, and redirects.

Sources: docs/start/framework/react/comparison.md

Relevant Source Files

  • docs/start/framework/react/guide/authentication-overview.md — Provides route protection architecture language, including redirects, layout routes, and the distinction between route UX and security boundaries. That framing is useful when scroll or transition behavior happens around authenticated route changes.
  • docs/start/framework/react/overview.md — Defines TanStack Start as a full-stack React framework powered by TanStack Router and lists the Start capabilities that affect navigation UX: SSR, streaming, server functions, middleware, full-stack builds, and end-to-end type safety.
  • docs/start/framework/react/build-from-scratch.md — Shows the minimum project wiring for a Start app: installing @tanstack/react-start and @tanstack/react-router, configuring the build plugin, and creating the router and root application files where navigation behavior is integrated.
  • docs/start/framework/react/comparison.md — Clarifies that Start’s routing features come from TanStack Router and that the Start comparison focuses on framework capabilities rather than replacing the Router documentation.
  • docs/start/framework/react/getting-started.md — Gives the recommended starting paths and links Router-focused examples, including scroll-restoration-oriented examples, after Start project creation.
  • docs/start/framework/react/guide/authentication-server-primitives.md — Separates route/UI guards from data/API authorization. This distinction helps prevent transition polish from being mistaken for an authorization boundary during protected navigations.

System-to-Code Mapping

The repository’s Start docs map scroll restoration and transition UX to two layers. The Router layer owns route matching, nested route rendering, route loaders, redirects, search and path parameters, and link-driven navigation. The Start layer supplies the full-stack runtime around that Router app: server rendering, streaming, server functions, middleware, and build outputs. In practice, this means that a scroll or view-transition decision should be coupled to route navigation state, but it should also be tested in the Start runtime where the first document render and subsequent client navigations may be produced through different execution paths.

Sources: docs/start/framework/react/overview.md, docs/start/framework/react/build-from-scratch.md

The build-from-scratch guide makes that layering concrete. A minimal Start project installs both @tanstack/react-start and @tanstack/react-router, then configures either Vite or Rsbuild with TanStack Start’s plugin. It also identifies two required application pieces: the router configuration and the root of the application. Those are the practical integration points for any route-level navigation UX. The router configuration determines how the route tree behaves, while the root application is where global UI, layout composition, and document-level concerns are normally connected.

Sources: docs/start/framework/react/build-from-scratch.md

A useful mental model is to treat scroll restoration as state reconciliation after a successful route transition. The URL has changed, the router has resolved a new set of matches, loaders may have supplied data, and nested layouts may have remained mounted or been replaced. The app should then choose whether to preserve the user’s existing position, restore a previous position, or move to a new default position. The supplied Start docs do not expose specific option names here, but they do show that this decision belongs alongside the Router-powered app shell rather than inside unrelated server-function or authentication code.

Sources: docs/start/framework/react/overview.md, docs/start/framework/react/build-from-scratch.md

View transitions should be reasoned about similarly. A transition is not only an animation; it is a contract between route state and rendered UI. If parent layouts persist while child routes change, the transition should usually emphasize the changing child content rather than repainting the entire document. If navigation redirects, blocks, or lands on a protected route, the transition should match the final route outcome rather than the attempted intermediate URL. The authentication docs are relevant because they describe redirects and layout-route protection as route UX patterns, not data security mechanisms.

Sources: docs/start/framework/react/guide/authentication-overview.md, docs/start/framework/react/guide/authentication-server-primitives.md

Execution Flow

A practical implementation flow starts by creating or opening a Router or Start project through the documented paths. The getting-started guide recommends TanStack Builder for the fastest setup, the TanStack CLI for local scaffolding, or cloning an example with npx gitpick. For a Start app, the build-from-scratch path installs React, @tanstack/react-start, @tanstack/react-router, TypeScript, and a supported build tool. Once the app runs, confirm that normal route navigation works before adding scroll restoration rules or transition effects, because broken route matching will make UX debugging misleading.

Sources: docs/start/framework/react/getting-started.md, docs/start/framework/react/build-from-scratch.md

npx @tanstack/cli@latest create
npx gitpick TanStack/router/tree/main/examples/react/start-basic start-basic
cd start-basic
npm install
npm run dev

For hand-wired Start projects, configure the build tool before working on route-level UX. The Start guide shows tanstackStart() in Vite configuration before the React plugin, and an equivalent Rsbuild setup using @tanstack/react-start/plugin/rsbuild. This matters because file-based routing, route tree generation, client/server builds, and framework integration need to be stable before evaluating scroll behavior. If the generated route tree or root app is not wired correctly, symptoms such as unexpected remounts, lost scroll positions, or jarring transitions may come from setup rather than from the transition logic itself.

Sources: docs/start/framework/react/build-from-scratch.md

import { defineConfig } from 'vite'
import { tanstackStart } from '@tanstack/react-start/plugin/vite'
import viteReact from '@vitejs/plugin-react'
 
export default defineConfig({
  server: {
    port: 3000,
  },
  plugins: [
    tanstackStart(),
    viteReact(),
  ],
})

After setup, evaluate navigation in three passes. First, test plain client navigation between sibling and nested routes so that scroll position and visual continuity can be observed without authentication or server mutations. Second, test routes whose data loads asynchronously, because the final layout height may differ between pending and resolved states. Third, test full-stack scenarios such as first-load SSR, streamed content, redirects, and protected layouts. Start’s overview explicitly calls out full-document SSR and streaming, so a polished implementation must be checked in those modes rather than only during local client-side transitions.

Sources: docs/start/framework/react/overview.md, docs/start/framework/react/guide/authentication-overview.md

Interaction with Auth, Redirects, and Protected Layouts

Authentication is one of the easiest places to create confusing navigation UX. The authentication overview recommends a layout route pattern for protecting route subtrees, centralizing authentication logic, and keeping authenticated and public route areas separated. That pattern is compatible with transition UX because parent layouts can define the shape of the authenticated area while child routes change inside it. However, transitions around login, logout, and redirects should be treated as route outcomes. The user should see the destination that the router actually resolves, not a transitional flash of a route they were never allowed to use.

Sources: docs/start/framework/react/guide/authentication-overview.md

The authentication server primitives guide adds an important constraint: route guards are for route UX, not the data authorization boundary. This distinction applies directly to view transitions. A pleasant animated redirect to a login screen does not protect private data, and a hidden or delayed component render does not authorize a server function. Any server function, server route, or API endpoint that reads or writes private data must authorize the request independently. Scroll restoration and transitions should improve comprehension of protected navigation, but they should never be used to mask unauthorized data access or replace server-side checks.

Sources: docs/start/framework/react/guide/authentication-server-primitives.md

This separation also helps with debugging. If a protected route redirects, first verify the authentication decision and final route destination, then evaluate whether scroll restoration and transitions run at the correct moment. If the route guard prevents entry, the app should restore or reset scroll for the route that actually renders. If a server function rejects a request after navigation, the transition may need to accommodate an error boundary or fallback state. The Start and Router model encourages those concerns to remain explicit: navigation chooses a route, data APIs authorize data, and UI transitions communicate the result.

Sources: docs/start/framework/react/guide/authentication-overview.md, docs/start/framework/react/guide/authentication-server-primitives.md

Practical Checklist

Use the getting-started page to choose the smallest reproducible project before debugging scroll or transition issues. If the issue is Router-specific, clone a Router-focused example rather than a full Start example. The getting-started source lists Router examples alongside Start examples, including entries such as location masking, authenticated routes, and scroll restoration-oriented material. That structure is a useful signal: isolate the route behavior first, then bring it back into the Start app if SSR, streaming, server functions, or deployment behavior changes the result.

Sources: docs/start/framework/react/getting-started.md

When reviewing an implementation, check the route tree before the animation. Nested layouts are a core Router value proposition, and the repository overview describes Router as designed for type safety, data-driven navigation, nested layouts, transitions, and error boundaries. In a nested tree, the parent route may remain mounted while only a child outlet changes. Scroll restoration rules and view transitions should reflect that composition. A route-level transition that ignores persistent layouts may feel like a full page reload even though the router is preserving useful application structure.

Sources: docs/start/framework/react/overview.md, docs/start/framework/react/build-from-scratch.md

Finally, test production-like behavior. Start’s documentation emphasizes full-stack builds, server rendering, streaming, and universal deployment, while the build guide sets up explicit dev and build scripts through Vite or Rsbuild. Scroll and transition behavior that feels correct in development can be affected by code splitting, SSR output, pending data, and redirects. A good next step is to run the app from an example or local build, navigate through nested routes, protected routes, and data-loading routes, and record whether the viewport and animation match the final route state.

Sources: docs/start/framework/react/overview.md, docs/start/framework/react/build-from-scratch.md

Next Steps

Start with the dedicated Router examples or guides when the question is specifically about scroll position, route matching, active links, or transition effects. Use the Start overview and build-from-scratch guide when the question is whether SSR, streaming, or the build pipeline changes that behavior. If authentication is involved, read the authentication overview and server primitives guide before polishing the transition, because redirects and authorization failures change which route actually renders. For adjacent topics, continue to pages on server-side rendering, route trees and outlets, navigation and links, authenticated routes, and debugging Router issues.