Skip to main content

TanStack Start Integration

Full-featured LocaleLens integration with TanStack Start

Full-Featured Demo
Full-Featured Demo

This demo shows a production-style, end-to-end integration with TanStack Start, including SSR, authentication, protected routes, and server-side translation loading. It demonstrates how LocaleLens works in a real-world full-stack React application.

This repository is not a starter template — it's a reference implementation showing how LocaleLens fits into a full production-style app.

→ View the demo on GitHub

Recommended starting point for advanced integrations

Who this is for

  • Building an SSR app with TanStack Start
  • Handling auth and protected routes
  • Wanting server-side i18n without client-side plumbing

What this demo covers

  • SSR with server-side loaders
  • Authentication with protected routes
  • API keys kept server-side via server functions
  • Cookie-based locale persistence
  • Accept-Language header detection
  • Multiple locales (EN, DE, ES) with runtime switching

Looking for something simpler?

Why TanStack Start + LocaleLens?

TanStack Start's server functions create a clear boundary between server and client code. This is perfect for LocaleLens because:

  • API keys stay on the server — The LocaleLens API key never reaches the browser
  • SSR translations — Pages render with translations already loaded (no flash of untranslated content)
  • No client-side i18n plumbing — Translations are resolved before rendering
  • Caching potential — Server-side fetches can be cached at the edge
Architecture

The demo uses a clear server/client split:

┌─────────────────────────────────────────────────────────────┐
│                         SERVER                               │
├─────────────────────────────────────────────────────────────┤
│  detectLocale()      → Read cookie / Accept-Language        │
│  getTranslations()   → Fetch from LocaleLens API (with key) │
│  loadTranslations()  → Combined helper for route loaders    │
│  requireAuth()       → Check auth before rendering          │
└─────────────────────────────────────────────────────────────┘
                              │
                              ▼ Loader data (locale + translations)
┌─────────────────────────────────────────────────────────────┐
│                         CLIENT                               │
├─────────────────────────────────────────────────────────────┤
│  I18nProvider        → Receives translations from loader    │
│  useTranslation()    → Access t() function                  │
│  setLocale()         → Triggers router invalidation         │
└─────────────────────────────────────────────────────────────┘

Key insight: The client never fetches translations directly — only resolved strings are passed from server loaders.

Copy this pattern: The detectLocale loadTranslations root loader flow can be lifted directly into your own TanStack Start app.

Quick Start

1. Clone and install

git clone https://github.com/localelens/localelens-demo-tanstack-start
cd localelens-demo-tanstack-start
pnpm install

2. Configure environment

cp .env.example .env

Add your LocaleLens credentials:

LOCALELENS_PROJECT_ID=your_project_id
LOCALELENS_API_KEY=your_api_key

3. Run the demo

pnpm dev

You'll get a public landing page, locale switcher, and a protected dashboard with server-rendered translations.

Key Implementation Details

Server Functions

src/lib/localelens.ts contains server-only functions:

// Detect locale from cookie or Accept-Language header
export async function detectLocale(): Promise<Locale> {
  const cookieStore = await getWebRequest()
  // ... cookie parsing and Accept-Language detection
}

// Fetch translations (server-side only, API key safe)
export async function getTranslations(locale: Locale) {
  const response = await fetch(
    `${API_URL}/projects/${PROJECT_ID}/translations/${locale}`,
    { headers: { Authorization: `Bearer ${API_KEY}` } }
  )
  return response.json()
}

Root Loader

Translations are loaded once in the root layout:

// src/routes/__root.tsx
export const Route = createRootRoute({
  loader: async () => {
    const { locale, translations } = await loadTranslations()
    return { locale, translations }
  },
  component: RootComponent,
})

Using Translations

import { useTranslation } from '../lib/i18n'

function MyPage() {
  const t = useTranslation()
  return <h1>{t('my.page.title')}</h1>
}
Protected Routes

The demo includes a protected dashboard route that requires authentication:

// src/routes/dashboard.tsx
import { createFileRoute } from '@tanstack/react-router'
import { requireAuth } from '../lib/auth'

export const Route = createFileRoute('/dashboard')({
  beforeLoad: () => requireAuth(),
  component: DashboardPage,
})

This demonstrates how LocaleLens integrates with authentication — translations are available on both public and protected routes.