# General Translation React SDKs (gt-react, gt-next, gt-react-native): Developing with SPA translations
URL: https://generaltranslation.com/en-US/docs/react/guides/developing-spa-translations.mdx
Docs index: https://generaltranslation.com/llms.txt
Description: How to preview General Translation translations while developing a single-page React app.

Development translations let you preview translated content as you edit your SPA. They require the GT compiler and a development API key.

**Prerequisites:**

- A single-page React app configured with the [SPA Quickstart](/docs/react/react-spa-quickstart)
- A development API key that starts with `gtx-dev-`

<Callout type="warn">
  **CommonJS limitation:** Development hot reloading requires ESM because the compiler injects top-level `await`. It does not work in applications compiled as CommonJS. Production translations still work with the CommonJS startup pattern in the [SPA Quickstart](/docs/react/react-spa-quickstart).
</Callout>

## Setup [#setup]

### 1. Install the compiler

Install `@generaltranslation/compiler` as a development dependency:

<Tabs items={['npm', 'yarn', 'bun', 'pnpm']}>
  <Tab value="npm">

  ```bash
  npm i -D @generaltranslation/compiler
  ```

  </Tab>
  <Tab value="yarn">

  ```bash
  yarn add --dev @generaltranslation/compiler
  ```

  </Tab>
  <Tab value="bun">

  ```bash
  bun add --dev @generaltranslation/compiler
  ```

  </Tab>
  <Tab value="pnpm">

  ```bash
  pnpm add --save-dev @generaltranslation/compiler
  ```

  </Tab>
</Tabs>

### 2. Add the compiler plugin

Enable development hot reload in the config shared by the CLI and compiler:

```json title="gt.config.json"
{
  "files": {
    "gt": {
      "output": "src/_gt/[locale].json",
      "parsingFlags": {
        "devHotReload": true
      }
    }
  }
}
```

Then add the plugin for your bundler.

<Callout type="info">
  **Bazel and Rolldown:** The compiler does not provide dedicated adapters for these build systems. [Store translations locally](/docs/react/guides/storing-translations) instead of relying on compiler-powered hot reload.
</Callout>

<Tabs items={['Vite', 'webpack', 'Rollup', 'Rspack', 'esbuild']}>
  <Tab value="Vite">

    ```ts title="vite.config.ts"
    import react from '@vitejs/plugin-react';
    import { vite as gtCompiler } from '@generaltranslation/compiler'; // [!code highlight]
    import { defineConfig } from 'vite';

    export default defineConfig({
      plugins: [react(), gtCompiler()], // [!code highlight]
    });
    ```

    `gtCompiler()` loads the root `gt.config.json` automatically.

  </Tab>
  <Tab value="webpack">

    Install `dotenv` as a development dependency so webpack can read `.env` and `.env.local`, then add the compiler before the rest of your plugins. Use `DefinePlugin` to expose only development credentials to browser code:

    ```js title="webpack.config.mjs"
    import { webpack as gtCompiler } from '@generaltranslation/compiler';
    import dotenv from 'dotenv';
    import webpack from 'webpack';
    import gtConfig from './gt.config.json' with { type: 'json' };

    dotenv.config({ path: '.env' });
    dotenv.config({ path: '.env.local', override: true });

    export default (_env, argv) => {
      const isProduction = (argv.mode ?? 'production') === 'production';

      return {
        // Keep your existing webpack settings.
        plugins: [
          gtCompiler({ ...gtConfig }),
          new webpack.DefinePlugin({
            'process.env.GT_PROJECT_ID': JSON.stringify(
              isProduction ? '' : (process.env.GT_PROJECT_ID ?? '')
            ),
            'process.env.GT_DEV_API_KEY': JSON.stringify(
              isProduction ? '' : (process.env.GT_DEV_API_KEY ?? '')
            ),
          }),
        ],
      };
    };
    ```

    See the complete [`gt-react` webpack example](https://github.com/generaltranslation/gt/tree/main/examples/webpack-spa) for loaders, local translation files, and development server settings.

  </Tab>
  <Tab value="Rollup">

    Register the compiler before your other Rollup plugins:

    ```js title="rollup.config.mjs"
    import { rollup as gtCompiler } from '@generaltranslation/compiler';

    export default {
      input: 'src/index.ts',
      plugins: [
        gtCompiler(),
        // Your other Rollup plugins
      ],
    };
    ```

    Rollup cannot analyze a fully dynamic translation import. List each locale with a static import specifier:

    ```ts title="src/loadTranslations.ts"
    const translationLoaders = {
      es: () => import('./_gt/es.json'),
      fr: () => import('./_gt/fr.json'),
    };

    export default async function loadTranslations(locale: string) {
      const loader =
        translationLoaders[locale as keyof typeof translationLoaders];
      return loader ? (await loader()).default : {};
    }
    ```

    Plain Rollup does not provide the development credential integration shown for Vite and webpack. Regenerate local translation files when source content changes. See the complete [`gt-react` Rollup example](https://github.com/generaltranslation/gt/tree/main/examples/rollup-spa).

  </Tab>
  <Tab value="Rspack">

    Add the Rspack adapter to your plugins:

    ```js title="rspack.config.mjs"
    import { rspack as gtCompiler } from '@generaltranslation/compiler';

    export default {
      plugins: [gtCompiler()],
    };
    ```

    Development translation also requires exposing `GT_PROJECT_ID` and `GT_DEV_API_KEY` to browser code without including either value in production bundles.

  </Tab>
  <Tab value="esbuild">

    Add the esbuild adapter to the `plugins` array:

    ```js title="build.mjs"
    import { build } from 'esbuild';
    import { esbuild as gtCompiler } from '@generaltranslation/compiler';

    await build({
      entryPoints: ['src/index.ts'],
      bundle: true,
      format: 'esm',
      outdir: 'dist',
      plugins: [gtCompiler()],
    });
    ```

    *Note: The esbuild adapter does not support automatic JSX injection. Wrap translatable JSX explicitly or use another adapter when `enableAutoJsxInjection` is required.*

  </Tab>
</Tabs>

See the complete [`gt-react` Vite example](https://github.com/generaltranslation/gt/tree/main/examples/vite-spa) for a full Vite setup.

### 3. Add development credentials

Get a development API key at [dash.generaltranslation.com](https://dash.generaltranslation.com/en-US/signin) or by running:

```bash
npx gt auth
```

Then add your project ID and development API key to `.env.local` and pass them to your initialization function:

<Tabs items={['Vite', 'webpack']}>
  <Tab value="Vite">

    ```bash title=".env.local"
    VITE_GT_PROJECT_ID="your-project-id"
    VITE_GT_DEV_API_KEY="your-dev-api-key"
    ```

    ```ts
    await initializeGTSPA({
      ...gtConfig,
      projectId: import.meta.env.VITE_GT_PROJECT_ID,
      devApiKey: import.meta.env.DEV
        ? import.meta.env.VITE_GT_DEV_API_KEY
        : undefined,
      loadTranslations,
    });
    ```

  </Tab>
  <Tab value="webpack">

    ```bash title=".env.local"
    GT_PROJECT_ID="your-project-id"
    GT_DEV_API_KEY="your-dev-api-key"
    ```

    ```ts
    await initializeGTSPA({
      ...gtConfig,
      projectId: process.env.GT_PROJECT_ID,
      devApiKey: process.env.GT_DEV_API_KEY,
      loadTranslations,
    });
    ```

    The webpack config above replaces both values with empty strings in production builds, so development credentials are not included in the production bundle.

  </Tab>
</Tabs>

<Callout type="warn">

**Development only:** Use a key starting with `gtx-dev-`. Never expose a production key that starts with `gtx-api-` in browser code.

</Callout>

### 4. Start developing

Start your development server and switch to a non-default locale. When you edit translatable content, the compiler registers the change and `gt-react` requests an updated development translation.

## Next steps

- /docs/react/guides/storing-translations
- /docs/react/guides/configuring
- /docs/react/guides/translating-jsx
- /docs/react/guides/managing-locales

## Sitemap

See the full [sitemap](https://generaltranslation.com/sitemap.md) for all pages.
