TanStack Router includes several optimizations to ensure your components only re-render when necessary. These optimizations include:
TanStack Router uses a technique called "structural sharing" to preserve as many references as possible between re-renders, which is particularly useful for state stored in the URL, such as search parameters.
For example, consider a details route with two search parameters, foo and bar, accessed like this:
const search = Route.useSearch()
const search = Route.useSearch()
When only bar is changed by navigating from /details?foo=f1&bar=b1 to /details?foo=f1&bar=b2, search.foo will be referentially stable and only search.bar will be replaced.
You can access and subscribe to the router state using various hooks like useRouterState, useSearch, and others. If you only want a specific component to re-render when a particular subset of the router state such as a subset of the search parameters changes, you can use partial subscriptions with the select property.
// component won't re-render when `bar` changes
const foo = Route.useSearch({ select: ({ foo }) => foo })
// component won't re-render when `bar` changes
const foo = Route.useSearch({ select: ({ foo }) => foo })
The select function can perform various calculations on the router state, allowing you to return different types of values, such as objects. For example:
const result = Route.useSearch({
select: (search) => {
return {
foo: search.foo,
hello: `hello ${search.foo}`,
}
},
})
const result = Route.useSearch({
select: (search) => {
return {
foo: search.foo,
hello: `hello ${search.foo}`,
}
},
})
Although this works, it will cause your component to re-render each time, since select is now returning a new object each time itโs called.
You can avoid this re-rendering issue by using "structural sharing" as described above. By default, structural sharing is turned off to maintain backward compatibility, but this may change in v2.
To enable structural sharing for fine grained selectors, you have two options:
const router = createRouter({
routeTree,
defaultStructuralSharing: true,
})
const router = createRouter({
routeTree,
defaultStructuralSharing: true,
})
const result = Route.useSearch({
select: (search) => {
return {
foo: search.foo,
hello: `hello ${search.foo}`,
}
},
structuralSharing: true,
})
const result = Route.useSearch({
select: (search) => {
return {
foo: search.foo,
hello: `hello ${search.foo}`,
}
},
structuralSharing: true,
})
Important
Structural sharing only works with JSON-compatible data. This means you cannot use select to return items like class instances if structural sharing is enabled.
In line with TanStack Router's type-safe design, TypeScript will raise an error if you attempt the following:
const result = Route.useSearch({
select: (search) => {
return {
date: new Date(),
}
},
structuralSharing: true,
})
const result = Route.useSearch({
select: (search) => {
return {
date: new Date(),
}
},
structuralSharing: true,
})
If structural sharing is enabled by default in the router options, you can prevent this error by setting structuralSharing: false.
Your weekly dose of JavaScript news. Delivered every Monday to over 100,000 devs, for free.