How to build a language switcher component in TanStack Start v1

Switch languages while staying on the same page

Problem

When users switch languages in a multilingual application, they expect to remain on the same page viewing the same content in the new language. A poorly implemented language switcher treats language selection as navigation to a different destination, often redirecting users to the homepage and losing their place entirely. This breaks user flow and creates frustration, especially when users are deep in a workflow or viewing specific content. The challenge is to build a switcher that changes only the language segment of the URL while preserving the rest of the path, search parameters, and hash.

Solution

Build a language switcher component that reads the current URL pathname and constructs links for each available language by replacing the locale segment in the path. Extract the current locale from the URL structure, then generate new paths for other supported languages by substituting the locale portion while keeping all other URL segments intact. Use these paths to render links that allow users to switch languages without losing their current page context.

Steps

1. Define supported locales

Create a configuration file that lists all languages your application supports and identifies the default locale.

export const locales = ["en", "fr", "es", "de"] as const;

export type Locale = (typeof locales)[number];

export const defaultLocale: Locale = "en";

This configuration serves as the single source of truth for available languages and will be used to generate switcher links.

2. Create a helper to extract the current locale from the pathname

Write a utility function that parses the pathname and extracts the locale segment if present.

import { defaultLocale, locales, type Locale } from "./locales";

export function getLocaleFromPathname(pathname: string): Locale {
  const segments = pathname.split("/").filter(Boolean);
  const firstSegment = segments[0];

  if (firstSegment && locales.includes(firstSegment as Locale)) {
    return firstSegment as Locale;
  }

  return defaultLocale;
}

This function inspects the first path segment and returns it if it matches a supported locale, otherwise falling back to the default.

3. Create a helper to build localized paths

Write a function that takes the current pathname and a target locale, then constructs a new path with the locale segment replaced.

import { defaultLocale, locales, type Locale } from "./locales";

export function getLocalizedPath(
  pathname: string,
  targetLocale: Locale,
): string {
  const segments = pathname.split("/").filter(Boolean);
  const firstSegment = segments[0];

  const hasLocalePrefix =
    firstSegment && locales.includes(firstSegment as Locale);

  if (hasLocalePrefix) {
    segments[0] = targetLocale;
  } else {
    segments.unshift(targetLocale);
  }

  return "/" + segments.join("/");
}

This function either replaces an existing locale segment or prepends the target locale to the path, ensuring the new URL points to the same page in a different language.

4. Build the language switcher component

Create a component that uses the current location to generate links for all supported languages.

import { Link } from "@tanstack/react-router";
import { useLocation } from "@tanstack/react-router";
import { locales, type Locale } from "./locales";
import { getLocaleFromPathname, getLocalizedPath } from "./locale-helpers";

export function LanguageSwitcher() {
  const location = useLocation();
  const currentLocale = getLocaleFromPathname(location.pathname);

  return (
    <nav>
      <ul>
        {locales.map((locale) => {
          const isActive = locale === currentLocale;
          const localizedPath = getLocalizedPath(location.pathname, locale);

          return (
            <li key={locale}>
              <Link
                to={localizedPath}
                search={location.search}
                hash={location.hash}
                aria-current={isActive ? "page" : undefined}
              >
                {locale.toUpperCase()}
              </Link>
            </li>
          );
        })}
      </ul>
    </nav>
  );
}

The component reads the current pathname, determines the active locale, and renders a link for each supported language that preserves the current page structure, search parameters, and hash fragment. Users can switch languages while staying on the same logical page.