Capítulo 43 de 57

Chapter 43: Render Optimizations

Core Idea

TanStack Router minimizes unnecessary re-renders through structural sharing of URL-derived state (like search params) and fine-grained select subscriptions on router state hooks, so components only re-render when the specific data they depend on actually changes.

Key Concepts

  • Structural sharing: the router preserves referential stability between re-renders for unchanged parts of state stored in the URL. E.g. navigating /details?foo=f1&bar=b1 to /details?foo=f1&bar=b2 keeps search.foo referentially identical, only search.bar changes.
  • Fine-grained selectors (select): hooks like useRouterState, useSearch, and others accept a select property to subscribe to only a subset of router state, so the component re-renders only when that subset changes.
  • Structural sharing with selectors: when a select function returns a newly computed object each call (e.g. combining fields into a new shape), the component re-renders every time by default, since object identity changes even if values don't.
  • structuralSharing option: enables deep-equality-aware sharing for select results, preventing re-renders when the computed object's contents are unchanged. Off by default for backward compatibility (may change in v2); enable via defaultStructuralSharing: true on the router, or per-hook with structuralSharing: true.
  • JSON-compatible constraint: structural sharing only works with JSON-serializable data; TypeScript will raise a compile error if a select function tries to return non-JSON-compatible values (like a Date instance) while structuralSharing: true is set.

Code Examples

// component won't re-render when `bar` changes
const foo = Route.useSearch({ select: ({ foo }) => foo })
  • What it demonstrates: a fine-grained selector isolating a single search param to avoid re-rendering on unrelated changes.
const result = Route.useSearch({
  select: (search) => ({ foo: search.foo, hello: `hello ${search.foo}` }),
  structuralSharing: true,
})
  • What it demonstrates: enabling structural sharing per-hook so a newly-computed object from select doesn't cause spurious re-renders when its contents are unchanged.

Key Takeaways

  1. Use select on router-state hooks whenever a component only needs part of the state, this is the primary lever for avoiding unnecessary re-renders.
  2. If select computes a new object/array each call, also enable structuralSharing (globally via defaultStructuralSharing or per-hook) to get real re-render savings.
  3. Structural sharing requires JSON-compatible return values from select; class instances and similar non-serializable types will fail type-checking when combined with structuralSharing: true.

Connects To

  • Ch 36: Data Loading, useLoaderData/useSearch-style hooks discussed there benefit from the same select/structural-sharing patterns.