Vite + React Integration
@lingo.dev/compiler integrates with Vite through a plugin that works with both SPA and SSR setups.
Setup
1. Install Package
pnpm install @lingo.dev/compiler
2. Configure Vite
Add lingoCompilerPlugin to your Vite config:
// vite.config.ts
import { defineConfig } from "vite";
import react from "@vitejs/plugin-react";
import { lingoCompilerPlugin } from "@lingo.dev/compiler/vite";
export default defineConfig({
plugins: [
lingoCompilerPlugin({
sourceRoot: "src",
sourceLocale: "en",
targetLocales: ["es", "de", "fr"],
models: "lingo.dev",
dev: {
usePseudotranslator: true,
},
}),
react(),
],
});
Plugin order: Place lingoCompilerPlugin before react() plugin. This ensures the compiler transforms your JSX before React processes it.
3. Add Provider
Wrap your app with LingoProvider in your entry point:
// src/main.tsx
import { StrictMode } from "react";
import { createRoot } from "react-dom/client";
import { LingoProvider } from "@lingo.dev/compiler/react";
import App from "./App";
import "./index.css";
createRoot(document.getElementById("root")!).render(
<StrictMode>
<LingoProvider>
<App />
</LingoProvider>
</StrictMode>
);
Important: Place LingoProvider as high as possible in your component tree. If using TanStack Router or React Router, place LingoProvider above the router provider.
SPA Setup
For single-page applications, the setup above is sufficient. Locale is managed client-side.
Language Switcher
"use client";
import { useLingoContext } from "@lingo.dev/compiler/react";
export function LanguageSwitcher() {
const { locale, setLocale } = useLingoContext();
return (
<div>
<label>Language:</label>
<select value={locale} onChange={(e) => setLocale(e.target.value)}>
<option value="en">English</option>
<option value="es">Español</option>
<option value="de">Deutsch</option>
<option value="fr">Français</option>
</select>
</div>
);
}
SSR Setup (React Router, Remix, TanStack Start)
For SSR frameworks, you may need to handle locale detection on the server.
Custom Locale Detection
Create .lingo/locale-resolver.server.ts for server-side logic:
// .lingo/locale-resolver.server.ts
export async function getServerLocale(): Promise<string> {
// Access request context (framework-specific)
// Example: parse cookies, headers, or database
return "en"; // Return detected locale
}
And .lingo/locale-resolver.client.ts for client-side:
// .lingo/locale-resolver.client.ts
export function getClientLocale(): string {
return localStorage.getItem("locale") || "en";
}
export function persistLocale(locale: string): void {
localStorage.setItem("locale", locale);
}
See Custom Locale Resolvers for framework-specific examples.
TanStack Router Integration
For TanStack Router, place LingoProvider above RouterProvider:
// src/main.tsx
import { StrictMode } from "react";
import { createRoot } from "react-dom/client";
import { RouterProvider, createRouter } from "@tanstack/react-router";
import { LingoProvider } from "@lingo.dev/compiler/react";
import { routeTree } from "./routeTree.gen";
const router = createRouter({ routeTree });
createRoot(document.getElementById("root")!).render(
<StrictMode>
<LingoProvider>
<RouterProvider router={router} />
</LingoProvider>
</StrictMode>
);
This ensures translations are available throughout your routed components and context isn't broken by code splitting.
React Router Integration
For React Router v6/v7:
// src/main.tsx
import { StrictMode } from "react";
import { createRoot } from "react-dom/client";
import { BrowserRouter } from "react-router-dom";
import { LingoProvider } from "@lingo.dev/compiler/react";
import App from "./App";
createRoot(document.getElementById("root")!).render(
<StrictMode>
<LingoProvider>
<BrowserRouter>
<App />
</BrowserRouter>
</LingoProvider>
</StrictMode>
);
HMR and Development
The compiler fully supports Vite's Hot Module Replacement (HMR). When you update translatable text:
- The compiler detects the change
- Translation server generates new translations (or pseudotranslations)
- HMR updates your component without full reload
- Component state is preserved
Fast Refresh works normally—the compiler doesn't interfere with Vite's HMR.
Build Configuration
Development Build
{
dev: {
usePseudotranslator: true, // Fast fake translations
}
}
Run npm run dev for instant feedback with pseudotranslations.
Production Build
{
buildMode: "cache-only", // Use pre-generated translations
}
Run npm run build. Translations come from .lingo/metadata.json—no API calls needed.
Best practice: Generate real translations in CI before production builds. See Build Modes.
Code Splitting
The compiler respects Vite's code splitting. Each lazy-loaded chunk includes only the translations it needs.
// Lazy-loaded route
const Dashboard = lazy(() => import("./pages/Dashboard"));
// Dashboard component's translations are bundled with the Dashboard chunk
Translations are automatically tree-shaken—only used translations are included in each chunk.
TypeScript
The compiler is fully typed:
import type { LingoConfig } from "@lingo.dev/compiler";
const config: LingoConfig = {
sourceRoot: "src",
sourceLocale: "en",
targetLocales: ["es", "de"],
models: "lingo.dev",
};
Environment Variables
Use Vite's environment variable system for API keys:
# .env
VITE_LINGO_API_KEY=your_key_here
Access in config:
{
models: "lingo.dev",
// API key is automatically read from LINGODOTDEV_API_KEY env variable
}
Never commit API keys. Add .env to .gitignore.
Common Issues
"Cannot find module '@lingo.dev/compiler/react'"
Ensure the package is installed: pnpm install @lingo.dev/compiler
HMR breaks after adding LingoProvider
Check that lingoCompilerPlugin is placed before react() plugin in your Vite config.
Translations not showing in production
Verify buildMode: "cache-only" and that .lingo/metadata.json has translations for all locales.
Context broken with code splitting
Ensure LingoProvider is placed above your router provider (TanStack Router, React Router, etc.). Otherwise, code-split routes may lose context.
Port 60000 already in use The translation server auto-finds available ports (60000-60099). If all are in use, configure manually:
{
dev: {
translationServerStartPort: 61000, // Use different port range
}
}
Next Steps
- Configuration Reference — All configuration options
- Custom Locale Resolvers — Customize locale detection
- Manual Overrides — Override specific translations
- Best Practices — Recommended patterns