Author:
    Creation:2026-06-12Last update:2026-08-04

    Variants

    A variant is a set of content files that share the same dictionary key but each carry a different variant value. Intlayer serves the appropriate file based on the selector passed to useIntlayer.

    The variant value can take two forms:

    • A string — a single named alternative (A/B tests, seasonal banners, feature flags).
    • An object — a structured discriminator addressed by a set of fields (CMS records, user-specific copy, any content keyed by an opaque ID). The whole object is the identity: the selector must provide an equal object to resolve the entry.
    The object form replaces the former meta field. Anywhere you previously wrote meta: { id, … }, write variant: { id, … }, and select it with { variant: { id, … } }.

    Named (string) variants

    Each file represents one named alternative. Omitting variant (or setting it to "default") marks it as the fallback.

    hero-banner.content.ts
    import { t, type Dictionary } from "intlayer";
    
    const dictionary = {
      key: "hero-banner",
      variant: "default",
      content: {
        headline: t({
          en: "Build faster with Intlayer",
          fr: "Développez plus vite avec Intlayer",
        }),
        cta: t({ en: "Get started", fr: "Commencer" }),
      },
    } satisfies Dictionary;
    
    export default dictionary;
    hero-banner.black-friday.content.ts
    import { t, type Dictionary } from "intlayer";
    
    const dictionary = {
      key: "hero-banner",
      variant: "black_friday",
      content: {
        headline: t({
          en: "50 % off — today only",
          fr: "−50 % — aujourd'hui seulement",
        }),
        cta: t({ en: "Shop now", fr: "Acheter maintenant" }),
      },
    } satisfies Dictionary;
    
    export default dictionary;

    Partial variants

    A variant declares only the keys it overrides; the rest are inherited from the default entry.

    hero-banner.summer.content.ts
    import { t, type Dictionary } from "intlayer";
    
    const dictionary = {
      key: "hero-banner",
      variant: "summer",
      content: {
        headline: t({
          en: "Build faster all summer",
          fr: "Développez plus vite tout l'été",
        }),
      },
    } satisfies Dictionary;
    
    export default dictionary;
    tsx
    useIntlayer("hero-banner", { variant: "summer" });// → { headline: "Build faster all summer", cta: "Get started" } — `cta` inheriteduseIntlayer("hero-banner", { variant: "never-declared" });// → the default entry

    So you only add a variant file where the wording actually differs. A key resolves to null only when it declares variants but no default entry.

    Consuming named variants

    Default variant

    Hero.tsx
    import { useIntlayer } from "react-intlayer";
    
    export const Hero = () => {
      const { headline, cta } = useIntlayer("hero-banner");
      // → default variant
    
      return (
        <section>
          <h1>{headline}</h1>
          <a>{cta}</a>
        </section>
      );
    };

    Named variant

    tsx
    const { headline, cta } = useIntlayer("hero-banner", {  variant: "black_friday",});

    Named variant with explicit locale

    tsx
    const content = useIntlayer("hero-banner", {  variant: "black_friday",  locale: "fr",});

    Object (structured) variants

    An object variant addresses content by an arbitrary set of key-value pairs declared in the variant field — making it possible to model CMS records, user-specific copy, or any content whose key is an opaque ID. The whole object is the identity: the selector must provide an equal object for the entry to resolve.

    product.abc.content.ts
    import { t, type Dictionary } from "intlayer";
    
    const dictionary = {
      key: "product",
      variant: { id: "prod_abc", userId: "user_123" },
      content: {
        name: t({ en: "Widget Pro", fr: "Widget Pro" }),
        description: t({ en: "The best widget.", fr: "Le meilleur widget." }),
      },
    } satisfies Dictionary;
    
    export default dictionary;
    product.abcd.content.ts
    import { t, type Dictionary } from "intlayer";
    
    const dictionary = {
      key: "product",
      variant: { id: "prod_abcd", userId: "user_123" },
      content: {
        name: t({ en: "Widget Lite", fr: "Widget Lite" }),
        description: t({ en: "A lighter option.", fr: "Une option plus légère." }),
      },
    } satisfies Dictionary;
    
    export default dictionary;

    Consuming object variants

    Pass the matching object to variant. Every field declared on the dictionary must be provided and equal; otherwise the result is null. Field order does not matter.

    Product.tsx
    import { useIntlayer } from "react-intlayer";
    
    export const Product = ({
      productId,
      userId,
    }: {
      productId: string;
      userId: string;
    }) => {
      const content = useIntlayer("product", {
        variant: { id: productId, userId },
      });
    
      if (!content) return null;
    
      return <p>{content.description}</p>;
    };

    With explicit locale

    tsx
    const content = useIntlayer("product", {  variant: { id: "prod_abc", userId: "user_123" },  locale: "fr",});

    Missing field — no match

    ts
    // Returns null: `userId` is missing, so the object does not match the declared variantconst content = useIntlayer("product", { variant: { id: "prod_abc" } });

    Ambient variant

    Some variant dimensions are fixed for a whole session — the tenant, the school type, the plan tier. They are resolved once, and no component should have to pass them by hand.

    Do not wrap useIntlayer in your own hook to inject them. The build-time optimization only rewrites a literal useIntlayer("key") imported from the framework package, so nothing behind a wrapper gets bundled.

    Declare the variant once on the provider instead, exactly like locale:

    App.tsx
    import { IntlayerProvider } from "react-intlayer";
    
    export const App = ({ locale, schoolType }) => (
      <IntlayerProvider locale={locale} variant={schoolType}>
        <Hero />
      </IntlayerProvider>
    );

    Every dictionary read below the provider now resolves against that variant, and a call-site selector always wins:

    tsx
    useIntlayer("hero-banner");// → the provider variantuseIntlayer("hero-banner", { variant: "summer" });// → "summer" — replaces the provider variant, it is not extended

    Forms

    The variant prop accepts three forms:

    Form Meaning
    variant="school1" one named variant for every key
    variant={["school1", "default"]} an ordered preference chain
    variant={{ "hero-banner": "school1", default: "base" }} one variant per dictionary key

    Preference chain

    A chain is tried left to right against the entries each key declares, and the first declared one wins. When none is declared, the implicit default entry is used — exactly as for a single value.

    tsx
    <IntlayerProvider variant={["school1", "school2"]} />// `hero-banner` declares no `school1` entry but declares `school2` → "school2"// a key declaring neither                                          → the default entry

    So ["black_friday", "summer"] reads as "black friday if this key has one, else summer, else default". Chains are also accepted at the call site:

    tsx
    useIntlayer("hero-banner", { variant: ["black_friday", "summer"] });
    Note this is the mirror image of the array accepted by the variant field of a content file: there an array declares one entry per element, here it consumes them in priority order.

    Per-key map

    Address each dictionary key separately. The reserved default entry covers every key not listed:

    tsx
    <IntlayerProvider  variant={{    "hero-banner": "school1",    product: ["school1", "default"],    default: "base",  }}/>
    On a provider a plain object is always read as the per-key map, never as an object variant — the two are structurally identical. To pin an object variant globally, nest it under an entry: variant={{ default: { id: "prod_abc" } }}.

    Because the map's keys are checked against your declared dictionary keys, a typo — or an object variant written directly, such as variant={{ id: "prod_abc" }} — is a compile-time error.

    Loading mode

    Object variants are often loaded lazily. Set importMode on the dictionary to control this:

    ts
    const dictionary = {
      key: "product",
      importMode: "fetch", // or "dynamic"
      variant: { id: "prod_abc", userId: "user_123" },
      content: { … },
    } satisfies Dictionary;
    
    export default dictionary;

    See bundle optimization for details on static, dynamic, and fetch modes.

    Typical use-cases

    • A/B copy tests driven by an experiment key
    • Seasonal or promotional banners
    • Feature-flagged messaging
    • Locale-specific marketing campaigns
    • Per-product marketing copy managed in a CMS
    • User-specific or account-specific content
    • Any content keyed by an opaque runtime ID