How to remember language selection across sessions in TanStack Start v1
Store user's explicit language choice
Problem
When a user explicitly selects a language, that choice represents a deliberate preference that should persist beyond the current browser session. Without persistence, the application forgets this preference on every visit, forcing users to reselect their language repeatedly. This creates friction and signals that the application does not respect user choices, degrading the overall experience and potentially causing users to abandon the site before completing their intended tasks.
Solution
Store the user's language choice in a persistent cookie when they make an explicit selection. On subsequent visits, check for this stored preference before falling back to automatic detection methods like browser headers. If a valid stored language is found, redirect the user from the site root to that language's path, ensuring they land directly in their preferred locale without additional steps.
Steps
1. Create a server function to store the language preference
Server functions let you define server-only logic that can be called from anywhere in your application. Define a function that writes the selected locale to a cookie.
import { createServerFn } from "@tanstack/react-start";
import { setCookie } from "@tanstack/react-start/server";
const LOCALE_COOKIE = "user_locale";
const COOKIE_MAX_AGE = 60 * 60 * 24 * 365;
export const saveLocalePreference = createServerFn({ method: "POST" })
.validator((locale: string) => locale)
.handler(async ({ data }) => {
setCookie(LOCALE_COOKIE, data, {
maxAge: COOKIE_MAX_AGE,
path: "/",
sameSite: "lax",
});
return { success: true };
});
This server function uses setCookie from @tanstack/react-start/server to store the locale preference, making it available on future requests.
2. Call the save function when the user selects a language
In your language switcher component, invoke the server function after the user makes a selection.
import { useNavigate } from "@tanstack/react-router";
import { saveLocalePreference } from "./locale-preference";
export function LanguageSwitcher({ currentLocale }: { currentLocale: string }) {
const navigate = useNavigate();
const handleLocaleChange = async (newLocale: string) => {
await saveLocalePreference({ data: newLocale });
navigate({ to: `/${newLocale}` });
};
return (
<select
value={currentLocale}
onChange={(e) => handleLocaleChange(e.target.value)}
>
<option value="en">English</option>
<option value="es">Español</option>
<option value="fr">Français</option>
</select>
);
}
This ensures the preference is saved before navigating to the new locale.
3. Create a server function to read the stored preference
Define a function that retrieves the stored locale from the cookie.
import { createServerFn } from "@tanstack/react-start";
import { getCookie } from "@tanstack/react-start/server";
const LOCALE_COOKIE = "user_locale";
export const getStoredLocale = createServerFn({ method: "GET" }).handler(
async () => {
const stored = getCookie(LOCALE_COOKIE);
return stored || null;
},
);
The getCookie function from @tanstack/react-start/server reads the cookie value set in previous visits.
4. Check for stored preference on the root route
Use the redirect function in a route's beforeLoad callback to trigger a redirect when a stored preference is found.
import { createFileRoute, redirect } from "@tanstack/react-router";
import { getStoredLocale } from "./locale-preference";
const SUPPORTED_LOCALES = ["en", "es", "fr"];
const DEFAULT_LOCALE = "en";
export const Route = createFileRoute("/")({
beforeLoad: async () => {
const stored = await getStoredLocale();
if (stored && SUPPORTED_LOCALES.includes(stored)) {
throw redirect({ to: `/${stored}` });
}
throw redirect({ to: `/${DEFAULT_LOCALE}` });
},
});
This checks the stored preference first and redirects to that locale if valid, otherwise falling back to the default.
5. Validate the stored locale against supported languages
Add validation to ensure the stored value is a recognized locale before redirecting.
import { createFileRoute, redirect } from "@tanstack/react-router";
import { getStoredLocale } from "./locale-preference";
const SUPPORTED_LOCALES = ["en", "es", "fr", "de", "ja"] as const;
function isValidLocale(value: string | null): value is string {
return value !== null && SUPPORTED_LOCALES.includes(value as any);
}
export const Route = createFileRoute("/")({
beforeLoad: async () => {
const stored = await getStoredLocale();
const locale = isValidLocale(stored) ? stored : "en";
throw redirect({ to: `/${locale}` });
},
});
This prevents redirects to invalid or unsupported locales, protecting against cookie tampering or stale values.