TanStack Router provides flexible and highly customizable primitives that can be composed to support common internationalization (i18n) routing patterns, such as optional path parameters, route rewriting, and type-safe params. This enables clean, SEO-friendly URLs, flexible locale handling, and seamless integration with i18n libraries.
This guide covers:
This pattern relies exclusively on TanStack Router features. It is suitable when:
Optional path parameters are ideal for implementing locale-aware routing without duplicating routes.
;/{-$locale}/abotu
This single route matches:
// Route: /{-$locale}/about
export const Route = createFileRoute('/{-$locale}/about')({
component: AboutComponent,
})
function AboutComponent() {
const { locale } = Route.useParams()
const currentLocale = locale || 'en'
const content = {
en: { title: 'About Us' },
fr: { title: 'À Propos' },
es: { title: 'Acerca de' },
}
return <h1>{content[currentLocale].title}</h1>
}
// Route: /{-$locale}/blog/{-$category}/$slug
export const Route = createFileRoute('/{-$locale}/blog/{-$category}/$slug')({
beforeLoad: ({ params }) => {
const locale = params.locale || 'en'
const validLocales = ['en', 'fr', 'es', 'de']
if (params.locale && !validLocales.includes(params.locale)) {
throw new Error('Invalid locale')
}
return { locale }
},
})
<Link
to="/{-$locale}/blog/{-$category}/$slug"
params={(prev) => ({
...prev,
locale: prev.locale === 'en' ? undefined : 'fr',
})}
>
Français
</Link>
type Locale = 'en' | 'fr' | 'es' | 'de'
function isLocale(value?: string): value is Locale {
return ['en', 'fr', 'es', 'de'].includes(value as Locale)
}
TanStack Router is library-agnostic. You can integrate any i18n solution by mapping locale state to routing behavior.
Below is a recommended pattern using Paraglide.
This pattern combines TanStack Router with a client-side i18n library. It is suitable when:
Paraglide provides type-safe translations, locale detection, and URL localization that pair naturally with TanStack Router.
GitHub example: https://github.com/TanStack/router/tree/main/examples/react/i18n-paraglide
npx @inlang/paraglide-js@latest init
import { paraglideVitePlugin } from '@inlang/paraglide-js'
paraglideVitePlugin({
project: './project.inlang',
outdir: './app/paraglide',
})
The router's rewrite option enables bidirectional URL transformation, perfect for locale prefixes. For comprehensive documentation on URL rewrites including advanced patterns, see the URL Rewrites guide.
import { deLocalizeUrl, localizeUrl } from './paraglide/runtime'
const router = createRouter({
routeTree,
rewrite: {
input: ({ url }) => deLocalizeUrl(url),
output: ({ url }) => localizeUrl(url),
},
})
This pattern integrates i18n at the routing and server layers. It is suitable when:
GitHub example: https://github.com/TanStack/router/tree/main/examples/react/start-i18n-paraglide
import { paraglideMiddleware } from './paraglide/server'
export default {
fetch(req: Request) {
return paraglideMiddleware(req, () => handler.fetch(req))
},
}
import { getLocale } from '../paraglide/runtime'
;<html lang={getLocale()} />
For offline or client-only environments:
import { shouldRedirect } from '../paraglide/runtime'
beforeLoad: async () => {
const decision = await shouldRedirect({ url: window.location.href })
if (decision.redirectUrl) {
throw redirect({ href: decision.redirectUrl.href })
}
}
To ensure every route has translations, you can derive translated pathnames directly from the TanStack Router route tree.
import { FileRoutesByTo } from '../routeTree.gen'
import { Locale } from '@/paraglide/runtime'
This guarantees:
import { localizeHref } from './paraglide/runtime'
export const prerenderRoutes = ['/', '/about'].map((path) => ({
path: localizeHref(path),
prerender: { enabled: true },
}))
https://intlayer.org/doc/environment/tanstack-start