Skip to content
Merged
282 changes: 89 additions & 193 deletions docs/en-US/react/index.mdx
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
---
title: React Quickstart
description: Add multiple languages to your React app in under 10 minutes
description: Add multiple languages to your server-rendered React app in under 10 minutes
---

By the end of this guide, your React app will display content in multiple languages, with a language switcher your users can interact with.
By the end of this guide, your server-rendered React app will display content in multiple languages, with a language switcher your users can interact with.

**Prerequisites:**
- A React app (Vite, Create React App, or similar)
- A server-rendered React app (React Router or a custom SSR setup)
- Node.js 18+

<Callout type='info'>
**Want automatic setup?** Run `npx gt@latest` to configure everything with the [Setup Wizard](/docs/cli/init). This guide covers manual setup.
**Client-side only app?** If your app renders entirely in the browser with Vite, follow the [SPA quickstart](/docs/react/tutorials/quickstart-spa) instead — it skips the provider entirely.
</Callout>

---
Expand Down Expand Up @@ -58,7 +58,7 @@ Create a **`gt.config.json`** file in your project root. This tells the library
"locales": ["es", "fr", "ja"],
"files": {
"gt": {
"output": "public/_gt/[locale].json"
"output": "src/_gt/[locale].json"
}
}
}
Expand All @@ -68,116 +68,78 @@ Create a **`gt.config.json`** file in your project root. This tells the library
- **`locales`** — the languages you want to translate into. Pick any from the [supported locales list](/docs/platform/supported-locales).
- **`files`** — tells the CLI where to save translation files. The `output` path should match the import path in your `loadTranslations` function (Step 3).

<Callout type='info'>
**Using Vite?** Set the output path to `"src/_gt/[locale].json"` instead of `"public/_gt/[locale].json"`. Vite imports translation files as modules, so they should live inside `src/` rather than `public/`.
</Callout>

---

## Step 3: Create a translation loader

`gt-react` runs entirely on the client, so it needs a function to load translation files at runtime. Create a `loadTranslations` file:
Create a `loadTranslations` function that loads a locale's translation file. On the server this runs during rendering; the CLI generates the files when you run `npx gt translate`:

```ts title="src/loadTranslations.ts"
export default async function loadTranslations(locale: string) {
try {
const translations = await import(`../public/_gt/${locale}.json`);
const translations = await import(`./_gt/${locale}.json`);
return translations.default;
} catch (error) {
console.warn(`No translations found for ${locale}`);
return {};
}
}
```

This function loads JSON translation files from your `public/_gt/` directory. The CLI generates these files when you run `npx gt translate`.
---

<Accordions>
<Accordion title="Using Vite?">
Since Vite translation files live in `src/_gt/` (see Step 2), update the import path:

```ts title="src/loadTranslations.ts"
export default async function loadTranslations(locale: string) {
try {
const translations = await import(`./_gt/${locale}.json`);
return translations.default;
} catch (error) {
console.warn(`No translations found for ${locale}`);
return {};
}
}
```
</Accordion>
<Accordion title="Using Create React App?">
CRA's webpack config blocks dynamic `import()` from outside `src/`. Use `fetch()` instead:

```ts title="src/loadTranslations.ts"
export default async function loadTranslations(locale: string) {
try {
const response = await fetch(`${process.env.PUBLIC_URL}/_gt/${locale}.json`);
if (!response.ok) throw new Error('Not found');
return await response.json();
} catch (error) {
console.warn(`No translations found for ${locale}`);
return {};
}
}
```
</Accordion>
</Accordions>
## Step 4: Initialize the library

Call **`initializeGT`** at module scope in a file that loads on both the server and the client — your root route or layout is the natural place. It registers your config and translation loader once; the configuration is immutable for the lifetime of the app:

```tsx title="src/routes/root.tsx"
import { initializeGT } from 'gt-react';
import gtConfig from '../../gt.config.json';
import loadTranslations from '../loadTranslations';

initializeGT({
defaultLocale: gtConfig.defaultLocale,
locales: gtConfig.locales,
loadTranslations,
});
```

---

## Step 4: Add the GTProvider to your app
## Step 5: Load translations on the server

The **`GTProvider`** component gives your entire app access to translations. Wrap your app at the root level:
In your root route's loader (or equivalent server handler), resolve the request locale and fetch a translations snapshot with **`getTranslationsSnapshot`**, then pass both to **`<GTProvider>`**:

```tsx title="src/App.tsx"
import { GTProvider } from 'gt-react';
import gtConfig from '../gt.config.json';
import loadTranslations from './loadTranslations';
```tsx title="src/routes/root.tsx"
import {
GTProvider,
getTranslationsSnapshot,
parseLocale,
} from 'gt-react';

export default function App() {
// In your route loader (exact API depends on your framework)
export async function loader({ request }) {
const locale = parseLocale(request); // [!code highlight]
return {
locale,
translations: await getTranslationsSnapshot(locale), // [!code highlight]
};
}

export default function Root({ children }) {
const { locale, translations } = useLoaderData();
return (
<GTProvider config={gtConfig} loadTranslations={loadTranslations}>
{/* Your app content */}
<GTProvider locale={locale} translations={translations}>
{children}
</GTProvider>
);
}
```

`GTProvider` takes your config and translation loader as props. It manages locale state and makes translations available to all child components.

<Accordions>
<Accordion title="Using Create React App?">
CRA restricts imports to files inside `src/`, so `import gtConfig from '../gt.config.json'` will fail. Either copy `gt.config.json` into `src/`, or inline the config directly:

```tsx title="src/App.tsx"
import { GTProvider } from 'gt-react';
import loadTranslations from './loadTranslations';

export default function App() {
return (
<GTProvider
config={{
defaultLocale: 'en',
locales: ['es', 'fr', 'ja'],
}}
loadTranslations={loadTranslations}
>
{/* Your app content */}
</GTProvider>
);
}
```
</Accordion>
</Accordions>

---

## Step 5: Mark content for translation
## Step 6: Mark content for translation

Now, wrap any text you want translated with the **`<T>`** component. `<T>` stands for "translate":
Wrap any text you want translated with the **`<T>`** component. `<T>` stands for "translate":

```tsx title="src/components/Welcome.tsx"
import { T } from 'gt-react';
Expand All @@ -194,54 +156,60 @@ export default function Welcome() {
}
```

You can wrap as much or as little JSX as you want inside `<T>`. Everything inside it — text, nested elements, even formatting — gets translated as a unit.
For plain strings — like `placeholder` attributes or `aria-label` values — use the **`useGT`** hook:

```tsx title="src/components/ContactForm.tsx"
import { useGT } from 'gt-react';

export default function ContactForm() {
const gt = useGT();
return <input placeholder={gt('Enter your email')} />;
}
```

---

## Step 6: Add a language switcher
## Step 7: Add a language switcher

Drop in a **`<LocaleSelector>`** so users can change languages:

```tsx title="src/components/Welcome.tsx"
import { T, LocaleSelector } from 'gt-react';
```tsx title="src/components/Header.tsx"
import { LocaleSelector } from 'gt-react';

export default function Welcome() {
return (
<main>
<LocaleSelector />
<T>
<h1>Welcome to my app</h1>
<p>This content will be translated automatically.</p>
</T>
</main>
);
export default function Header() {
return <LocaleSelector />;
}
```

`LocaleSelector` renders a dropdown populated with the languages from your `gt.config.json`.
When the user picks a language, `gt-react` persists the choice in the `generaltranslation.locale` cookie and reloads the page, so the server re-renders everything in the new locale.

---

## Step 7: Set up environment variables (optional)
## Step 8: Set up environment variables (optional)

To see translations in development, you need API keys from General Translation. These enable **on-demand translation** — your app translates content in real time as you develop.

Create a **`.env.local`** file with the correct prefix for your bundler:
If your framework builds with Vite, `gt-react` reads these automatically during initialization:

<Tabs items={["Vite", "Create React App"]}>
<Tab value="Vite">
```bash title=".env.local"
VITE_GT_API_KEY="your-dev-api-key"
VITE_GT_PROJECT_ID="your-project-id"
```
</Tab>
<Tab value="Create React App">
```bash title=".env.local"
REACT_APP_GT_API_KEY="your-dev-api-key"
REACT_APP_GT_PROJECT_ID="your-project-id"
```
</Tab>
</Tabs>
```bash title=".env.local"
VITE_GT_PROJECT_ID="your-project-id"
VITE_GT_DEV_API_KEY="your-dev-api-key"
```

Otherwise, pass credentials to `initializeGT` yourself:

```ts
initializeGT({
defaultLocale: gtConfig.defaultLocale,
locales: gtConfig.locales,
loadTranslations,
projectId: process.env.GT_PROJECT_ID,
devApiKey:
process.env.NODE_ENV === 'development'
? process.env.GT_DEV_API_KEY
: undefined,
});
```

Get your free keys at [dash.generaltranslation.com](https://dash.generaltranslation.com/signup) or by running:

Expand All @@ -251,81 +219,11 @@ npx gt auth

<Callout type="warn">
For development, use a key starting with `gtx-dev-`. Production keys (`gtx-api-`) are for CI/CD only.

Client-side bundlers like Vite and CRA require environment variables to use specific prefixes (`VITE_` or `REACT_APP_`) to be exposed to the browser. Bare `GT_API_KEY` will not be available at runtime.
</Callout>

<Accordions>
<Accordion title="Can I use gt-react without API keys?">
Yes. Without API keys, `gt-react` works as a standard i18n library. You won't get on-demand translation in development, but you can still:
- Provide your own translation files manually
- Use all components (`<T>`, `<Var>`, `LocaleSelector`, etc.)
- Run `npx gt generate` to create translation file templates, then translate them yourself
</Accordion>
</Accordions>

---

## Step 8: See it working

Start your dev server:

<Tabs items={['npm', 'yarn', 'bun', 'pnpm']}>
<Tab value="npm">
```bash
npm run dev
```
</Tab>
<Tab value="yarn">
```bash
yarn dev
```
</Tab>
<Tab value="bun">
```bash
bun dev
```
</Tab>
<Tab value="pnpm">
```bash
pnpm dev
```
</Tab>
</Tabs>

Open your app and use the language dropdown to switch languages. You should see your content translated.

<Callout type="info">
In development, translations happen on-demand — you may see a brief loading state the first time you switch to a new language. In production, translations are pre-generated and load instantly.
</Callout>

---

## Step 9: Translate strings (not just JSX)

For plain strings — like `placeholder` attributes, `aria-label` values, or `alt` text — use the **`useGT`** hook:

```tsx title="src/components/ContactForm.tsx"
import { useGT } from 'gt-react';

export default function ContactForm() {
const gt = useGT();

return (
<form>
<input
placeholder={gt('Enter your email')}
aria-label={gt('Email input field')}
/>
<button type="submit">{gt('Send')}</button>
</form>
);
}
```

---

## Step 10: Deploy to production
## Step 9: Deploy to production

In production, translations are pre-generated at build time (no real-time API calls). Add the translate command to your build script:

Expand All @@ -337,7 +235,7 @@ In production, translations are pre-generated at build time (no real-time API ca
}
```

Set your **production** environment variables in your hosting provider (Vercel, Netlify, etc.):
Set your **production** environment variables in your hosting provider:

```bash
GT_PROJECT_ID=your-project-id
Expand Down Expand Up @@ -366,23 +264,21 @@ That's it — your app is now multilingual. 🎉
This is expected. In development, translations happen on-demand (your content is translated in real time via the API). This delay **does not exist in production** — all translations are pre-generated by `npx gt translate`.
</Accordion>
<Accordion title="Some translations are inaccurate">
Ambiguous text can lead to inaccurate translations. For example, "apple" could mean the fruit or the company. Add a `context` prop to help:
Ambiguous text can lead to inaccurate translations. For example, "apple" could mean the fruit or the company. Add a `$context` prop to help:

```jsx
<T context="the technology company">Apple</T>
<T $context="the technology company">Apple</T>
```

Both `<T>` and `useGT()` support the `context` option.
Both `<T>` and `useGT()` support the `$context` option.
</Accordion>
</Accordions>

---

## Next steps

- [**`<GTProvider>` API**](/docs/react/api/components/gtprovider) — Full reference for the provider's props
- [**`<T>` Component Guide**](/docs/react/guides/t) — Learn about variables, plurals, and advanced translation patterns
- [**String Translation Guide**](/docs/react/guides/strings) — Deep dive into `useGT`
- [**Variable Components**](/docs/react/guides/variables) — Handle dynamic content with `<Var>`, `<Num>`, `<Currency>`, and `<DateTime>`
- [**Pluralization**](/docs/react/api/components/plural) — Handle plural forms with the `<Plural>` component
- [**Deploying to Production**](/docs/react/tutorials/quickdeploy) — CI/CD setup, caching, and performance optimization
- [**Shared Strings**](/docs/react/guides/shared-strings) — Translate text in arrays, config objects, and shared data with `msg()`
Loading
Loading