Creation:2025-09-22Last update:2026-01-26

    New Intlayer v8 - What's new?

    Welcome to Intlayer v8! This release focuses on enhancing developer experience with automatic content detection, ensuring data integrity with schema validation, and providing more control over dictionary management.

    www.youtube.com

    Table of contents


    Rich Content Evolution: Markdown & HTML

    Intlayer v8 brings major improvements to how rich content is handled, introducing HTML nodes (which didn't exist in v7) and unifying the API with Markdown nodes (which existed in v7 but have been enhanced).

    The Unified .use() API

    We introduced the .use() method for both Markdown and HTML nodes. This method allows you to customise the HTML tags or components used during rendering.

    • Component Replacement: You can easily replace HTML tags or custom components with your own framework components (e.g., replace <a> with NextLink or <CustomCmp> with a React component).
    • Type Safety: All functions for providing components are fully typed, ensuring you receive the correct props.

    Default Rendering Behaviour

    In v7, if no provider was defined, Markdown nodes were rendered as raw strings, often requiring external libraries to parse them.

    In v8, Intlayer includes its own internal Markdown parser. By default, Markdown nodes are now rendered directly as HTML without needing any external libraries.

    New Renderer & Provider Utilities

    We have introduced new standalone renderer functions and components to give you more control outside of the standard useIntlayer flow.

    • Markdown: MarkdownRenderer, useMarkdownRenderer, renderMarkdown. (Note: MarkdownProvider existed in v7 but now integrates with these new tools).
    • HTML: HTMLRenderer, useHTMLRenderer, renderHTML, HTMLProvider.

    Examples: Markdown Rendering Tools

    1. Using the Component:

    tsx
    import { MarkdownRenderer } from "react-intlayer/markdown";<MarkdownRenderer  forceBlock={true}  components={{    h1: ({ children }) => <h1 className="text-2xl">{children}</h1>  }}>  {"# My Title"}</MarkdownRenderer>

    2. Using the Hook:

    tsx
    import { useMarkdownRenderer } from "react-intlayer/markdown";const renderMarkdown = useMarkdownRenderer({  components: {    h1: ({ children }) => <h1 className="text-red-500">{children}</h1>  }});return <div>{renderMarkdown("# My Title")}</div>;

    3. Using the Utility Function:

    tsx
    import { renderMarkdown } from "react-intlayer/markdown";const html = renderMarkdown("# My Title", {  forceBlock: true});

    Examples: HTML Rendering Tools

    1. Using the Component:

    tsx
    import { HTMLRenderer } from "react-intlayer/html";<HTMLRenderer  components={{    p: ({ children }) => <p className="mb-4">{children}</p>  }}>  {"<p>Hello World</p>"}</HTMLRenderer>

    2. Using the Hook:

    tsx
    import { useHTMLRenderer } from "react-intlayer/html";const renderHTML = useHTMLRenderer({  components: {    strong: ({ children }) => <b className="font-bold">{children}</b>  }});return <div>{renderHTML("<p>Hello <strong>World</strong></p>")}</div>;

    3. Using the Utility Function:

    tsx
    import { renderHTML } from "react-intlayer/html";const html = renderHTML("<p>Hello World</p>");

    For more details, see the HTML Content Documentation and Markdown Documentation.


    Custom URL Rewrites

    Intlayer v8 introduces support for Custom URL Rewrites, allowing you to define locale-specific paths that differ from the standard /locale/path structure. This is a powerful feature for improving local SEO and providing a more natural user experience for non-English speakers.

    Key enhancements in v8:

    • Framework Formatters: New nextjsRewrite, svelteKitRewrite, reactRouterRewrite, vueRouterRewrite, solidRouterRewrite, tanstackRouterRewrite, nuxtRewrite, and viteRewrite to provide idiomatic pattern syntax for each router.
    • useRewriteURL Hook: A new client-side hook that silently corrects the address bar to the "pretty" localised URL without triggering router navigations.
    • Automatic SEO Redirects: Built-in proxies now automatically redirect users from manually typed canonical paths (e.g., /fr/about) to their prettier localised versions (e.g., /fr/a-propos).

    Example Configuration:

    intlayer.config.ts
    import { Locales, type IntlayerConfig } from "intlayer";import { nextjsRewrite } from "intlayer/routing";const config: IntlayerConfig = {  internationalization: {    locales: [Locales.ENGLISH, Locales.FRENCH, Locales.SPANISH],    defaultLocale: Locales.ENGLISH,  },  routing: {    mode: "prefix-no-default",    rewrite: nextjsRewrite({      "/[locale]/about": {        fr: "/[locale]/a-propos",        es: "/[locale]/acerca-de",      },      "/[locale]/products/[id]": {        fr: "/[locale]/produits/[id]",        es: "/[locale]/productos/[id]",      },    }),  },};export default config;

    This feature is supported out-of-the-box in Next.js and Vite through the Intlayer proxies, and can be easily integrated into other routers like TanStack Router, React Router, Vue Router, SvelteKit, and Solid Router.

    For more information and integration guides, see the Custom URL Rewrites Documentation.


    Enhanced Insertion Values

    In v8, insertion values can now accept React elements (or Vue nodes) in addition to strings and numbers. This allows you to inject rich, interactive components directly into your insertion templates.

    Intlayer now robustly handles nested React and Preact nodes within insertions, ensuring that complex UI structures are preserved and rendered correctly.

    Example:

    src/example.content.ts
    import { insert } from "intlayer";export default {  key: "my-key",  content: {    myInsertion: insert("Hi {{name}}"),  },};
    tsx
    import { useIntlayer } from "next-intlayer";const { myInsertion } = useIntlayer("my-key");return (  <div>    {myInsertion({      name: 2, // number      // or      name: "John", // string      // or      name: <span>John</span>, // React element    })}  </div>);

    Content Schema Validation

    Intlayer v8 introduces schema validation for dictionaries. You can now define reusable validation schemas in your configuration using Zod and apply them to your content files. This ensures your content always adheres to the expected structure, catching errors at build time.

    1. Define Schemas

    Define your schemas in intlayer.config.ts:

    intlayer.config.ts
    import { z } from "zod";export default {  schemas: {    "seo-metadata": z.object({      title: z.string().min(50).max(60),      description: z.string().min(150).max(160),    }),  },};

    2. Apply Schemas to Dictionaries

    Reference the schema key in your dictionary definition:

    src/example.content.ts
    import { type Dictionary } from "intlayer";const aboutPageMetaContent = {  key: "about-page-meta",  schema: "seo-metadata", // <--  content: {    title: "About Our Company - Learn More About Us",    description: "Discover our company's mission, values, and team.",  },} satisfies Dictionary;export default aboutPageMetaContent;

    If the content doesn't match the schema (e.g., title is too short), the build process will raise an error.


    Enhanced Automatic Content Detection

    In v8, Intlayer intelligently detects Markdown syntax, HTML tags, and variable insertions in your content strings. This means you can often omit helper functions like md(), html(), or insert().

    This behaviour is enabled by default. You can now fine-tune this detection either globally in your intlayer.config.ts or per dictionary.

    Granular Control

    You can enable or disable specific types of transformations:

    intlayer.config.ts
    export default {  dictionary: {    // contentAutoTransformation: false (default)    contentAutoTransformation: {      markdown: true,      html: true,      insertion: false, // Disable automatic insertion detection    },  },};

    v7 behaviour (Manual wrapping):

    src/example.content.ts
    import { md, insert } from "intlayer";export default {  key: "my-key",  content: {    myMarkdown: md("## Hello World"),    myInsertion: insert("Hi {{name}}"),  },};

    v8 behaviour (Automatic detection):

    src/example.content.ts
    export default {  key: "my-key",  contentAutoTransformation: true, // Can also be set by dictionary definition or globally in intlayer.config.ts  content: {    myMarkdown: "## Hello World", // Automatically detected as Markdown    myHTML: "<p>Hello World</p>", // Automatically detected as HTML    myInsertion: "Hi {{name}}", // Automatically detected as Insertion  },};

    The underlying JSON result remains the same, preserving the rich type information needed for rendering:

    json
    {  "key": "my-key",  "content": {    "myMarkdown": {      "nodeType": "markdown",      "markdown": "## Hello World"    },    "myHTML": {      "nodeType": "html",      "html": "<p>Hello World</p>"    },    "myInsertion": {      "nodeType": "insertion",      "insertion": "Hi {{name}}"    }  }}

    Localization: new useIntl hook

    A new useIntl() hook is now available in React, Next.js and Vue. It provides a locale-bound Intl object that automatically uses the current language for formatting numbers, dates, and more, without needing to manually pass the locale.

    tsx
    import { useIntl } from "next-intlayer";const intl = useIntl();const formattedPrice = new intl.NumberFormat({  style: "currency",  currency: "USD",}).format(123.45);

    Tooling: VSCode Extension Enhancements

    The Intlayer VSCode extension receives major updates in v8 to streamline your internationalisation workflow:

    • Starting Time: Performance improvements when opening a project.
    • Caching: Enhanced caching layer for near-instant validation and autocompletion.
    • Unused Keys & Duplicated Keys Detection: New features to automatically detect unused keys and duplicated keys across your dictionaries, helping you keep your content clean and efficient.

    Compiler Optimizations

    Intlayer v8 includes a new caching layer for the Markdown and HTML compiler. This ensures that identical content strings with the same configuration are only parsed once, significantly reducing the overhead during re-renders or when using the same content in multiple places.

    babel.config.js
      const {  intlayerExtractBabelPlugin,  intlayerOptimizeBabelPlugin,  getExtractPluginOptions,  getOptimizePluginOptions,} = require('@intlayer/babel');module.exports = {  presets: ['next/babel'],  plugins: [    // Extract content from components into dictionaries    [intlayerExtractBabelPlugin, getExtractPluginOptions()],    // Optimize imports by replacing useIntlayer with direct dictionary imports    [intlayerOptimizeBabelPlugin, getOptimizePluginOptions()],  ],};

    Flexibility: Unified Import Mode

    The live boolean property has been deprecated in favour of a more comprehensive importMode property. This allows for explicit definition of how dictionaries should be loaded: statically, dynamically, or via live sync.

    Modes

    • static (Default): Dictionary is bundled at build time. Best for performance.
    • dynamic: Dictionary is loaded at runtime (e.g., via JSON fetch or suspense).
    • fetch: Dictionary is fetched from the CMS/Server at runtime and synchronised.

    Migration:

    v7 Config v8 Config
    live: true importMode: 'fetch'
    live: false importMode: 'static' (or 'dynamic')

    Note: In Intlayer v8, the importMode property has been moved from the build configuration to the dictionary configuration in intlayer.config.ts. This allows you to define a default import mode for all your dictionaries while still being able to override it on a per-dictionary basis.

    Global Configuration Example:

    intlayer.config.ts
    export default {  dictionary: {    importMode: "dynamic", // Global default  },  // ...};

    Dictionary Example:

    src/example.content.ts
    export default {    key: 'my-key',    importMode: "fetch", // Overrides global config    content: { ... }}

    Dictionary Location Control

    v8 introduces the location property to explicitly manage where dictionaries live and how they synchronise. This is particularly useful for hybrid workflows involving both local files and remote CMS content.

    Options

    • local: The dictionary exists only locally. It will not be pushed to the remote CMS.
    • remote: The dictionary is managed remotely. Once pushed on the CMS, it will be detached from the local one. The remote dictionary will be pulled from the CMS.
    • local_and_remote: The dictionary exists in both places. Local changes are pushed, and remote changes are pulled (synchronised).

    Example:

    src/example.content.ts
    export default {    key: 'my-key',    location: "local", // Keep this dictionary local-only    content: { ... }}

    System Configuration Separation

    Intlayer v8 separates the configuration of content sources from internal system and output paths. This declutters the content property and makes it clear which settings are intended for user management vs. those that are managed by the Intlayer system.

    The following properties have been moved from content to a new system property in intlayer.config.ts:

    • dictionariesDir
    • moduleAugmentationDir
    • unmergedDictionariesDir
    • typesDir
    • mainDir
    • configDir
    • cacheDir
    • outputFilesPatternWithPath

    v7 behaviour:

    intlayer.config.ts
    export default {  content: {    contentDir: ["src"],    dictionariesDir: ".intlayer/dictionary", // Mixed with source config  },};

    v8 behaviour:

    intlayer.config.ts
    export default {  content: {    contentDir: ["src"],  },  system: {    dictionariesDir: ".intlayer/dictionary", // Clearly separated  },};

    Content and Code Directory Separation

    Intlayer v8 separates the configuration for content definition files from the configuration for code transformation. This allows for more precise watching and scanning, improving build performance.

    Previously, contentDir was used for both watching .content.* files and scanning code for useIntlayer calls. Now:

    • contentDir: Specifically for your content declaration files.
    • codeDir: Specifically for your application code that needs transformation (e.g., pruning, optimisation).

    Migration:

    If you previously had contentDir set, Intlayer v8 will use it as the default for codeDir as well, but will log a warning. You should explicitly define codeDir in your configuration.

    v7 behaviour:

    intlayer.config.ts
    export default {  content: {    contentDir: ["src", "@packages/design-system"], // Used for both content and code  },};

    v8 behaviour:

    intlayer.config.ts
    export default {  content: {    contentDir: ["src/content", "@packages/design-system"], // Only watch for src/content/*.content.* files here and @packages/design-system/dist/*.content.* files    codeDir: ["src", "@packages/design-system"], // Only scan for code transformation here and @packages/design-system/src/*.content.* files  },};

    Framework: Svelte Improvements

    Markdown and HTML content in Svelte now automatically parse to HTML when stringified. This makes it much easier to use with Svelte's {@html} syntax, as you can now simply pass the content node directly.


    Migration notes from v7

    Configuration Changes

    • live property: The live property in dictionaries is removed. Use importMode: 'fetch' instead.
    • importMode: The build.importMode property in configuration has been deprecated. Use dictionary.importMode instead.
    • contentDir and codeDir: contentDir is now specifically for content files. A new codeDir property has been added for code transformation. If codeDir is not set, Intlayer will fallback to contentDir and log a warning.
    • Schema Validation: To use the new schema feature, ensure you have zod installed in your project.