# 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 project API key with runtime translation permission.

**Prerequisites:**

- A single-page React app configured with the [SPA Quickstart](/docs/react/react-spa-quickstart)
- A [project API key](/docs/platform/dashboard/reference/api-keys#create-project-keys) starting with `gtx-api-`, with **Runtime translation** enabled

<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

In **Project > API Keys**, create a key with **Runtime translation** enabled. Full-access keys also work. To minimize risk, we recommend **Custom** permissions with **Runtime translation** set to **Enabled** and every other resource set to **None**.

Add your project ID and project key to `.env.local`, then pass them to your initialization function. The SDK still uses the `devApiKey` setting to enable development hot reload:

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

    ```bash title=".env.local"
    VITE_GT_PROJECT_ID="your-project-id"
    VITE_GT_DEV_API_KEY="gtx-api-your-runtime-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="gtx-api-your-runtime-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:** API keys may be used in client code served by your local development server. Never include any API key in deployed client bundles, regardless of its permissions. Restricting local development keys to **Runtime translation** is recommended to minimize risk.

</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.
