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:
Prefix-based and optional-locale routing
Advanced routing patterns for i18n
Language navigation and switching
SEO considerations
Type safety
Integration patterns with i18n libraries (Paraglide)
This pattern relies exclusively on TanStack Router features. It is suitable when:
You want full control over translations
You already manage translations manually
You do not need automatic locale detection
Optional path parameters are ideal for implementing locale-aware routing without duplicating routes.
/{-$locale}/aboutThis single route matches:
/about (default locale)
/en/about
/fr/about
/es/about
// 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:
You want type-safe translations
You want localized URLs
You do not need server-side rendering
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 initimport { 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:
You use TanStack Start
You need SSR or streaming
You want locale-aware redirects and metadata
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:
No missing translations
Full type safety
Compiler feedback for routing mistakes
import { localizeHref } from './paraglide/runtime'
export const prerenderRoutes = ['/', '/about'].map((path) => ({
path: localizeHref(path),
prerender: { enabled: true },
}))https://intlayer.org/doc/environment/tanstack-start