Skip to content

TypeScript integration

next-intl integrates seamlessly with TypeScript right out of the box, requiring no additional setup.

However, you can optionally provide supplemental type definitions for your messages and formats to enable autocompletion and improve type safety.

Messages

Messages can be strictly typed to ensure you’re using valid keys.

messages.json
{
  "About": {
    "title": "Hello"
  }
}
function About() {
  // ✅ Valid namespace
  const t = useTranslations('About');
 
  // ✖️ Unknown message key
  t('description');
 
  // ✅ Valid message key
  t('title');
}

To enable this validation, add a global type definition file in your project root (e.g. global.d.ts):

global.d.ts
import en from './messages/en.json';
 
type Messages = typeof en;
 
declare global {
  // Use type safe message keys with `next-intl`
  interface IntlMessages extends Messages {}
}

You can freely define the interface, but if you have your messages available locally, it can be helpful to automatically create the interface based on the messages from your default locale by importing it.

Formats

Global formats that are referenced in calls like format.dateTime can be strictly typed to ensure you’re using valid format names across your app.

function Component() {
  const format = useFormatter();
 
  // ✅ Valid format
  format.number(2, 'precise');
 
  // ✅ Valid format
  format.list(['HTML', 'CSS', 'JavaScript'], 'enumeration');
 
  // ✖️ Unknown format string
  format.dateTime(new Date(), 'unknown');
 
  // ✅ Valid format
  format.dateTime(new Date(), 'short');
}

To enable this validation, export the formats that you’re using in your request configuration:

i18n/request.ts
import {Formats} from 'next-intl';
 
export const formats = {
  dateTime: {
    short: {
      day: 'numeric',
      month: 'short',
      year: 'numeric'
    }
  },
  number: {
    precise: {
      maximumFractionDigits: 5
    }
  },
  list: {
    enumeration: {
      style: 'long',
      type: 'conjunction'
    }
  }
} satisfies Formats;
 
// ...

Now, a global type definition file in the root of your project can pick up the shape of your formats and use them for declaring the IntlFormats interface:

global.d.ts
import {formats} from './src/i18n/request';
 
type Formats = typeof formats;
 
declare global {
  // Use type safe formats with `next-intl`
  interface IntlFormats extends Formats {}
}

Troubleshooting

If you’re encountering problems, double check that:

  1. Your interface uses the correct name.
  2. You’re using TypeScript version 5 or later.
  3. You’re using correct paths for all modules you’re importing into your global declaration file.
  4. Your type declaration file is included in tsconfig.json.
  5. Your editor has loaded the most recent type declarations. When in doubt, you can restart.

Docs

 · 

Examples

 · 

Blog