# General Translation React SDKs (gt-react, gt-next, gt-react-native): Next.js Pages Router Quickstart
URL: https://generaltranslation.com/en-US/docs/react/nextjs-pages-router-quickstart.mdx
Docs index: https://generaltranslation.com/llms.txt
Description: Add multiple languages to a Next.js Pages Router app with General Translation in under 10 minutes.

By the end of this guide, your Next.js Pages Router app will display content in multiple languages, with a language switcher your users can interact with.

In the Pages Router, `gt-next` works through `getServerSideProps`: on each request, the server resolves the user's locale, loads a translations snapshot, and passes both to a [`<GTProvider>`](/docs/react/reference/components/gt-provider) in `_app.tsx` so the first render is already translated.

The `gt-next/server` entry is App Router-only and does not work with the Pages Router.

**Prerequisites:**
- A Next.js app using the **Pages Router** (Next.js 13.0.0 or later, excluding 15.2.1 and 15.2.2)
- Node.js 18+

<Callout type='info'>
  **Note:** If you use the App Router, follow the [Next.js App Router Quickstart](/docs/react/nextjs-quickstart) instead. It uses server components and needs no `getServerSideProps` wiring.
</Callout>


## Quickstart [#quickstart]

### 1. Install the packages

`gt-next` is the library that powers translations in your app. `gt` is the CLI tool that prepares translations for production.

<Tabs items={['npm', 'yarn', 'bun', 'pnpm']}>
  <Tab value="npm">
  ```bash
  npm i gt-next
  npm i -D gt
  ```
  </Tab>
  <Tab value="yarn">
  ```bash
  yarn add gt-next
  yarn add --dev gt
  ```
  </Tab>
  <Tab value="bun">
  ```bash
  bun add gt-next
  bun add --dev gt
  ```
  </Tab>
  <Tab value="pnpm">
  ```bash
  pnpm add gt-next
  pnpm add --save-dev gt
  ```
  </Tab>
</Tabs>


### 2. Create a translation config file

Create a **`gt.config.json`** file in your project root. This tells the library which languages you support:

```json title="gt.config.json"
{
  "defaultLocale": "en",
  "locales": ["en", "es", "fr", "ja"],
  "files": {
    "gt": {
      "output": "public/_gt/[locale].json"
    }
  }
}
```

- **`defaultLocale`** — the language your app is written in (your source language).
- **`locales`** — every locale available in your app. Include `defaultLocale` because Next.js internationalized routing requires it, then add the languages you want to translate into. Pick any from the [supported locales list](/docs/platform/dashboard/reference/supported-locales).
- **`files.gt.output`** — where the CLI saves translation files. `[locale]` is replaced with each language code (e.g., `public/_gt/es.json`).

Add `public/_gt/` to your **`.gitignore`** — these files are generated, not hand-written:

```txt title=".gitignore"
public/_gt/
```

### 3. Configure Next.js internationalized routing

The Pages Router uses [Next.js internationalized routing](https://nextjs.org/docs/pages/guides/internationalization) for locale-prefixed URLs and request locale detection. Import your locale settings into `next.config.ts`, then wrap the config with `withGTConfig`:

```ts title="next.config.ts"
import type { NextConfig } from 'next';
import { withGTConfig } from 'gt-next/config';
import gtConfig from './gt.config.json';

const nextConfig: NextConfig = {
  i18n: {
    locales: gtConfig.locales,
    defaultLocale: gtConfig.defaultLocale,
  },
};

export default withGTConfig(nextConfig);
```

Next.js keeps the default locale at `/` and prefixes the other locales, such as `/es` and `/fr`. You do not need `gt-next` middleware or a `pages/[locale]` route segment. See [Pages Router locale routing](/docs/react/nextjs/pages-router-middleware) for detection and migration details.


### 4. Add a load function for local translations

Create a **[`loadTranslations`](/docs/react/reference/functions/load-translations)** file in your project root (or `src/` directory). This tells `gt-next` how to load the translation files generated by the CLI:

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

<Callout type="info">
  **Note:** These translation files do not exist until you create them with [`npx gt generate`](/docs/cli/reference/commands/generate) (no API key needed) or [`npx gt translate`](/docs/cli/reference/commands/translate) (with credentials). Until then the bundler warns about the missing `public/_gt` directory, and the `try`/`catch` above returns `{}` so the app still runs with untranslated content.
</Callout>

`withGTConfig` automatically detects a `loadTranslations.[js|ts]` file in your project root or `src/` directory — no additional configuration needed.

<Callout type='info'>
  **Note:** Local translations are bundled with your app, so they load instantly with no reliance on external services. See [Storing translations](/docs/react/guides/storing-translations) for details and trade-offs.
</Callout>


### 5. Wrap getServerSideProps on your pages

Wrap each page's `getServerSideProps` with **`withGTServerSideProps`**. On every request, it reads the locale that Next.js resolved into `context.locale`, loads a translations snapshot for that locale, and injects both into your page props:

```tsx title="pages/index.tsx"
import type { GetServerSideProps } from 'next';
import { withGTServerSideProps } from 'gt-next';

export const getServerSideProps: GetServerSideProps = withGTServerSideProps(
  async (context) => {
    return {
      props: {
        // your own props
      },
    };
  }
);
```

If a page doesn't need server-side props of its own, call it with no arguments:

```tsx title="pages/about.tsx"
import { withGTServerSideProps } from 'gt-next';

export const getServerSideProps = withGTServerSideProps();
```

`withGTServerSideProps` adds `locale` and `translations` to your props (plus an internal `enableI18n` flag). If your inner function returns a `redirect` or `notFound`, it passes the result through untouched without loading translations.


### 6. Add the GTProvider to your app

The **[`GTProvider`](/docs/react/reference/components/gt-provider)** component gives your entire app access to translations. In `_app.tsx`, pull the injected props out of `pageProps` and pass them to the provider. The **`WithGTServerSideProps`** type describes the injected shape:

```tsx title="pages/_app.tsx"
import type { AppProps } from 'next/app';
import Router from 'next/router';
import { GTProvider, type WithGTServerSideProps } from 'gt-next';

export default function App({
  Component,
  pageProps,
}: AppProps<WithGTServerSideProps>) {
  const { locale, translations } = pageProps;

  return (
    <GTProvider
      locale={locale}
      translations={translations}
      _reload={({ locale: nextLocale }) => {
        void Router.push(Router.pathname, Router.asPath, {
          locale: nextLocale,
        });
      }}
    >
      <Component {...pageProps} />
    </GTProvider>
  );
}
```

Because the locale and translations arrive with the server response, the first render is already in the user's language — no client-side loading state. The `_reload` callback gives locale changes to the Next.js router so it loads the selected locale's page props.


### 7. Mark content for translation

Now, wrap any text you want translated with the **[`<T>`](/docs/react/reference/components/t)** component. [`<T>`](/docs/react/reference/components/t) stands for "translate":

```tsx title="pages/index.tsx"
import { T } from 'gt-next';

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

You can wrap as much or as little JSX as you want inside [`<T>`](/docs/react/reference/components/t). Everything inside it — text, nested elements, even formatting — gets translated as a unit.


### 8. Add a language switcher

Drop in a **[`<LocaleSelector>`](/docs/react/reference/components/locale-selector)** so users can change languages:

```tsx title="pages/index.tsx"
import { T, LocaleSelector } from 'gt-next';

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

[`LocaleSelector`](/docs/react/reference/components/locale-selector) renders a dropdown populated with the languages from your `gt.config.json`. When the user picks a language, the callback in `_app.tsx` navigates to the localized URL and Next.js saves the choice to the `NEXT_LOCALE` cookie. The server then renders the selected locale.


### 9. 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:

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

Get your free keys at [dash.generaltranslation.com](https://dash.generaltranslation.com/en-US/signin) or by running:

```bash
npx gt auth
```

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

  Never expose `GT_API_KEY` to the browser or commit it to source control.
</Callout>


### 10. 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 [http://localhost:3000](http://localhost:3000) and use the language dropdown to switch languages. You should see your content translated.

<Callout type="info">
  **Note:** In development, translations happen on-demand, so 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>


### 11. Translate strings (not just JSX)

For plain strings — like `placeholder` attributes, `aria-label` values, or `alt` text — use the **[`useGT`](/docs/react/reference/hooks/use-gt)** hook:

```tsx title="pages/contact.tsx"
import { useGT } from 'gt-next';

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

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


### 12. 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:

```json title="package.json"
{
  "scripts": {
    "build": "npx gt translate && next build"
  }
}
```

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

```bash
GT_PROJECT_ID=your-project-id
GT_API_KEY=gtx-api-your-production-key
```

<Callout type="warn">
  **Warning:** Production keys start with `gtx-api-` (not `gtx-dev-`). Get one from [dash.generaltranslation.com](https://dash.generaltranslation.com). Never prefix it with `NEXT_PUBLIC_`.
</Callout>

That's it — your app is now multilingual. 🎉


## Troubleshooting [#troubleshooting]

<Accordions>
  <Accordion title="Do I need withGTServerSideProps on every page?">
    Yes — [`<GTProvider>`](/docs/react/reference/components/gt-provider) requires the `locale` and `translations` props, and they only exist on pages whose `getServerSideProps` is wrapped. For pages that don't fetch their own data, export the no-argument form:

    ```tsx
    export const getServerSideProps = withGTServerSideProps();
    ```
  </Accordion>
  <Accordion title="Can I use getStaticProps instead?">
    Yes. Wrap the page with `withGTStaticProps` and keep passing the generated props to [`GTProvider`](/docs/react/reference/components/gt-provider) in `_app.tsx`. See the [Pages Router static site generation guide](/docs/react/nextjs/pages-router-static-site-generation) for the complete setup.
  </Accordion>
  <Accordion title="The language isn't changing when I use the dropdown">
    Confirm that `_reload` calls `Router.push` with the selected `locale` option, as shown above. After a selection, the URL should use the locale prefix and the `NEXT_LOCALE` cookie should contain that locale.
  </Accordion>
  <Accordion title="Translations are slow in development">
    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`](/docs/cli/reference/commands/translate).
  </Accordion>
</Accordions>

## Next steps

- /docs/react/guides/translating-jsx
- /docs/react/guides/translating-strings
- /docs/react/guides/managing-locales
- /docs/react/guides/formatting-variables

## Sitemap

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