Skip to main content
The @saykit/react package provides React-specific utilities for using Saykit in both client and server components.

Installation

npm install @saykit/react saykit

Package Exports

The package provides conditional exports for client and server environments:
  • Default import (@saykit/react): Automatically resolves to client or server based on React’s export conditions
  • Client import (@saykit/react/client): Explicitly import client-only utilities
  • Server import (@saykit/react/server): Explicitly import server-only utilities

Client Components

SayProvider

Wrap your client component tree with SayProvider to provide a localized Say instance via React context.
import { SayProvider } from '@saykit/react/client';
import type { Say } from 'saykit';

function RootLayout({ 
  locale, 
  messages, 
  children 
}: { 
  locale: string; 
  messages: Say.Messages; 
  children: React.ReactNode;
}) {
  return (
    <html lang={locale}>
      <body>
        <SayProvider locale={locale} messages={messages}>
          {children}
        </SayProvider>
      </body>
    </html>
  );
}

Props

locale
string
required
The current locale (e.g., "en", "fr")
messages
Say.Messages
required
The messages object for the current locale
children
React.ReactNode
Child components that will have access to the Say instance

useSay

Access the Say instance from any client component within a SayProvider.
'use client';

import { useSay } from '@saykit/react/client';

function MyComponent() {
  const say = useSay();
  
  return <p>{say.t({ id: 'greeting' })}</p>;
}
useSay must be called within a component wrapped by SayProvider, or it will throw an error.

Say Component (Client)

Use the Say component in client components for automatic message extraction:
'use client';

import { Say } from '@saykit/react';
import { useState } from 'react';

export default function ClientComponent() {
  const [count, setCount] = useState(0);
  
  return (
    <div>
      <Say>
        Hello from the <strong>client</strong>
      </Say>
      
      <Say.Plural
        _={count}
        one="You have # item"
        other="You have # items"
      />
    </div>
  );
}

Server Components

setSay

Set the Say instance for server components using setSay. This must be called before any getSay calls.
import { setSay } from '@saykit/react/server';
import { Say } from 'saykit';

const say = new Say({
  locales: ['en', 'fr'],
  messages: {
    en: await import('./locales/en/messages.json'),
    fr: await import('./locales/fr/messages.json'),
  },
});

await say.load('en');
say.activate('en');
setSay(say);

getSay

Retrieve the current Say instance in server components:
import { getSay } from '@saykit/react/server';

export default function ServerComponent() {
  const say = getSay();
  
  return <p>{say.t({ id: 'greeting' })}</p>;
}

Say Component (Server)

The Say component works seamlessly in server components:
import { Say } from '@saykit/react';

export default function ServerComponent() {
  const region = 'Server';
  
  return (
    <Say>
      Hello from the <strong>{region}</strong>
    </Say>
  );
}

unstable_createWithSay

Create a higher-order component that automatically initializes the Say instance for server components:
import 'server-only';
import { unstable_createWithSay } from '@saykit/react/server';
import { Say } from 'saykit';

const say = new Say({
  locales: ['en', 'fr'],
  messages: {
    en: await import('./locales/en/messages.json'),
    fr: await import('./locales/fr/messages.json'),
  },
});

export const withSay = unstable_createWithSay(say);
Then use it to wrap your server components:
import { withSay } from './i18n';
import type { Say } from 'saykit';

function RootLayout({
  locale,
  messages,
  children,
}: {
  locale: string;
  messages: Say.Messages;
  children: React.ReactNode;
}) {
  return (
    <html lang={locale}>
      <body>
        <SayProvider locale={locale} messages={messages}>
          {children}
        </SayProvider>
      </body>
    </html>
  );
}

export default withSay(
  RootLayout, 
  (props) => props.params.then(p => p.locale)
);

Say Component Features

Basic Usage

The Say component is a macro that extracts messages at build time:
import { Say } from '@saykit/react';

<Say>
  Welcome to our app!
</Say>

With Variables

<Say>
  Hello, <strong>{userName}</strong>!
</Say>

With React Components

<Say>
  Click <Link href="/about">here</Link> to learn more
</Say>

Pluralization

<Say.Plural
  _={itemCount}
  _0="No items"
  one="One item"
  other="# items"
/>
Number props must be prefixed with _ (e.g., _0, _1, _2) to avoid JSX parser issues.

Ordinals

<Say.Ordinal
  _={position}
  _1="#st"
  _2="#nd"
  _3="#rd"
  other="#th"
/>

Select (Gender/Category)

<Say.Select
  _={gender}
  male="He is online"
  female="She is online"
  other="They are online"
/>

Complete Example (Next.js App Router)

1

Create i18n configuration

Create src/i18n.ts for server-side Say setup:
src/i18n.ts
import 'server-only';
import { unstable_createWithSay } from '@saykit/react/server';
import { Say } from 'saykit';

const say = new Say({
  locales: ['en', 'fr'],
  messages: {
    en: await import('./locales/en/messages.json').then(m => m.default),
    fr: await import('./locales/fr/messages.json').then(m => m.default),
  },
});

export const withSay = unstable_createWithSay(say);
export default say;
2

Setup root layout

Wrap your app with SayProvider in the layout:
src/app/[locale]/layout.tsx
import { SayProvider } from '@saykit/react/client';
import type { Say } from 'saykit';
import say, { withSay } from '../../i18n';

export function generateStaticParams() {
  return say.map(([, locale]) => ({ locale }));
}

async function RootLayout({
  locale,
  messages,
  children,
}: {
  locale: string;
  messages: Say.Messages;
  children: React.ReactNode;
}) {
  return (
    <html lang={locale}>
      <body>
        <SayProvider locale={locale} messages={messages}>
          {children}
        </SayProvider>
      </body>
    </html>
  );
}

export default withSay(RootLayout, (p) => p.params.then(p => p.locale));
3

Use in server components

src/app/[locale]/server-component.tsx
import { Say } from '@saykit/react';

export default function ServerComponent() {
  const region = 'Server';
  
  return (
    <Say>
      Hello from the <strong>{region}</strong>
    </Say>
  );
}
4

Use in client components

src/app/[locale]/client-component.tsx
'use client';

import { Say } from '@saykit/react';

export default function ClientComponent() {
  const region = 'Client';
  
  return (
    <Say>
      Hello from the <strong>{region}</strong>
    </Say>
  );
}

Type Imports

Import types for proper TypeScript support:
import type { Say } from 'saykit';
import type { ReadonlySay } from 'saykit';

type Messages = Say.Messages;
type MessageDescriptor = Say.MessageDescriptor;

Notes

The Say component is a macro and requires the Babel plugin or unplugin to transform at build time. See Babel Plugin or Unplugin for setup instructions.
When using numeric props in JSX (like pluralization rules), prefix them with _ to avoid JSX parsing issues. For example, use _0 instead of 0.

Build docs developers (and LLMs) love