Easily style active links in Tanstack Router
TanStack Router gives you two ways to style the active link in a navbar — activeProps for component-level prop merging, and a data-status="active" attribute on the rendered anchor for CSS-only targeting. Here's both, with the gotchas.
With activeProps
1import { Link } from '@tanstack/react-router'
2
3const items = [
4 { name: 'Dashboard', to: '/dashboard' },
5 { name: 'Messages', to: '/messages' },
6] as const
7
8export function MainNav() {
9 return (
10 <nav className='hidden md:flex items-center space-x-4 lg:space-x-6 mx-4 text-gray-500 dark:text-gray-400'>
11 {items.map(({ name, to }) => (
12 <Link
13 key={name}
14 to={to}
15 className='text-sm font-medium transition-colors hover:text-black dark:hover:text-white'
16 activeProps={{ className: 'text-black dark:text-white' }}
17 >
18 {name}
19 </Link>
20 ))}
21 </nav>
22 )
23}activeProps shallow-merges into the Link's props when the route matches — className strings concatenate, other props overwrite.
You can also pass a function if you need to read the current state:
1activeProps={(state) => ({
2 className: state.location.pathname.startsWith('/admin')
3 ? 'text-red-600'
4 : 'text-black',
5})}inactiveProps does the same for the not-matching case.
The / gotcha: activeOptions
By default Link to='/' is considered active for any nested route too — /dashboard, /messages, all of them. That's almost never what you want for the home link. Pin it with activeOptions:
1<Link to='/' activeProps={{ className: 'text-black' }} activeOptions={{ exact: true }}>
2 Home
3</Link>activeOptions also takes includeSearch and includeHash if those should factor into the match.
CSS-only with data-status
Every TanStack <Link> renders an <a> with data-status="active" when matched. That means you can skip activeProps entirely and target it with CSS — or Tailwind's data-attribute variant:
1<Link
2 to={to}
3 className='text-sm font-medium text-gray-500 dark:text-gray-400 hover:text-black dark:hover:text-white data-[status=active]:text-black dark:data-[status=active]:text-white'
4>
5 {name}
6</Link>Cleaner if you're happy with one className expression. activeProps wins when the active styling diverges enough from the inactive styling that splitting them is more readable.
Type-safe to
Since router v1 stable (Dec 2023), to='/dashboard' is type-checked against your route tree at build time — no need to import route objects (dashboardRoute.fullPath) or compose paths manually. The same API carries over to TanStack Start, the SSR meta-framework built on the router.
Docs: Navigation.