\n\n \n \n \n ```\n\n \n \n\n **1. Using the Component:**\n\n ```svelte\n \n\n \n ```\n\n **2. Using the Hook:**\n\n ```svelte\n \n\n {@html render(\"# My Title\")}\n ```\n\n **3. Using the Utility Function:**\n\n ```svelte\n \n\n {@html renderMarkdown(\"# My Title\")}\n ```\n\n \n \n\n **1. Using the Service:**\n\n ```typescript\n import { Component } from \"@angular/core\";\n import { IntlayerMarkdownService } from \"angular-intlayer\";\n\n @Component({ ... })\n export class MyComponent {\n constructor(private markdownService: IntlayerMarkdownService) {}\n\n render(markdown: string) {\n return this.markdownService.renderMarkdown(markdown);\n }\n }\n ```\n\n \n \n\n **1. Using the Component:**\n\n ```tsx\n import { MarkdownRenderer } from \"solid-intlayer/markdown\";\n\n \n {\"# My Title\"}\n \n ```\n\n **2. Using the Hook:**\n\n ```tsx\n import { useMarkdownRenderer } from \"solid-intlayer/markdown\";\n\n const render = useMarkdownRenderer();\n\n return
{render(\"# My Title\")}
;\n ```\n\n **3. Using the Utility Function:**\n\n ```tsx\n import { renderMarkdown } from \"solid-intlayer/markdown\";\n\n return
{renderMarkdown(\"# My Title\")}
;\n ```\n\n \n \n\n **1. Using the Component:**\n\n ```tsx\n import { MarkdownRenderer } from \"preact-intlayer/markdown\";\n\n \n {\"# My Title\"}\n \n ```\n\n **2. Using the Hook:**\n\n ```tsx\n import { useMarkdownRenderer } from \"preact-intlayer/markdown\";\n\n const render = useMarkdownRenderer();\n\n return
{render(\"# My Title\")}
;\n ```\n\n **3. Using the Utility Function:**\n\n ```tsx\n import { renderMarkdown } from \"preact-intlayer/markdown\";\n\n return
{renderMarkdown(\"# My Title\")}
;\n ```\n\n \n\n\n#### Examples: HTML Rendering Tools\n\n\n \n\n **1. Using the Component:**\n\n ```tsx\n import { HTMLRenderer } from \"react-intlayer/html\";\n\n
{children}
\n }}\n >\n {\"
Hello World
\"}\n \n ```\n\n **2. Using the Hook:**\n\n ```tsx\n import { useHTMLRenderer } from \"react-intlayer/html\";\n\n const renderHTML = useHTMLRenderer({\n components: {\n strong: ({ children }) => {children}\n }\n });\n\n return
{renderHTML(\"
Hello World
\")}
;\n ```\n\n **3. Using the Utility Function:**\n\n ```tsx\n import { renderHTML } from \"react-intlayer/html\";\n\n const html = renderHTML(\"
Hello World
\");\n ```\n\n \n \n\n **1. Using the Component:**\n\n ```vue\n \n\n \n Hello World\" />\n \n ```\n\n \n \n\n **1. Using the Component:**\n\n ```svelte\n \n\n Hello World\" />\n ```\n\n **2. Using the Hook:**\n\n ```svelte\n \n\n {@html render(\"
Hello World
\")}\n ```\n\n **3. Using the Utility Function:**\n\n ```svelte\n \n\n {@html renderHTML(\"
Hello World
\")}\n ```\n\n \n \n\n **1. Direct Usage:**\n\n In Angular, you can use the standard `[innerHTML]` binding.\n\n ```html\n
Hello World'\">
\n ```\n\n \n \n\n **1. Using the Component:**\n\n ```tsx\n import { HTMLRenderer } from \"solid-intlayer/html\";\n\n \n {\"
Hello World
\"}\n \n ```\n\n **2. Using the Hook:**\n\n ```tsx\n import { useHTMLRenderer } from \"solid-intlayer/html\";\n\n const render = useHTMLRenderer();\n\n return
{render(\"
Hello World
\")}
;\n ```\n\n **3. Using the Utility Function:**\n\n ```tsx\n import { renderHTML } from \"solid-intlayer/html\";\n\n return
{renderHTML(\"
Hello World
\")}
;\n ```\n\n \n \n\n **1. Using the Component:**\n\n ```tsx\n import { HTMLRenderer } from \"preact-intlayer/html\";\n\n \n {\"
Hello World
\"}\n \n ```\n\n **2. Using the Hook:**\n\n ```tsx\n import { useHTMLRenderer } from \"preact-intlayer/html\";\n\n const render = useHTMLRenderer();\n\n return
{render(\"
Hello World
\")}
;\n ```\n\n **3. Using the Utility Function:**\n\n ```tsx\n import { renderHTML } from \"preact-intlayer/html\";\n\n return
{renderHTML(\"
Hello World
\")}
;\n ```\n\n \n\n\nFor more details, see the [HTML Content Documentation](/en-GB/doc/concept/content/html) and [Markdown Documentation](/en-GB/doc/concept/content/markdown).\n\n---\n\n## Custom URL Rewrites\n\nIntlayer 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.\n\n**Key enhancements in v8:**\n\n- **Framework Formatters**: New `nextjsRewrite`, `svelteKitRewrite`, `reactRouterRewrite`, `vueRouterRewrite`, `solidRouterRewrite`, `tanstackRouterRewrite`, `nuxtRewrite`, and `viteRewrite` to provide idiomatic pattern syntax for each router.\n- **`useRewriteURL` Hook**: A new client-side hook that silently corrects the address bar to the \"pretty\" localised URL without triggering router navigations.\n- **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`).\n\n**Example Configuration:**\n\n\n \n\n ```typescript fileName=\"intlayer.config.ts\"\n import { Locales, type IntlayerConfig } from \"intlayer\";\n import { nextjsRewrite } from \"intlayer/routing\";\n\n const config: IntlayerConfig = {\n internationalization: {\n locales: [Locales.ENGLISH, Locales.FRENCH, Locales.SPANISH],\n defaultLocale: Locales.ENGLISH,\n },\n routing: {\n mode: \"prefix-no-default\",\n rewrite: nextjsRewrite({\n \"/[locale]/about\": {\n fr: \"/[locale]/a-propos\",\n es: \"/[locale]/acerca-de\",\n },\n \"/[locale]/products/[id]\": {\n fr: \"/[locale]/produits/[id]\",\n es: \"/[locale]/productos/[id]\",\n },\n }),\n },\n };\n\n export default config;\n ```\n\n \n \n\n ```typescript fileName=\"intlayer.config.ts\"\n import { Locales, type IntlayerConfig } from \"intlayer\";\n import { reactRouterRewrite } from \"intlayer/routing\";\n\n const config: IntlayerConfig = {\n internationalization: {\n locales: [Locales.ENGLISH, Locales.FRENCH, Locales.SPANISH],\n defaultLocale: Locales.ENGLISH,\n },\n routing: {\n mode: \"prefix-all\",\n rewrite: reactRouterRewrite({\n \"/:locale/about\": {\n fr: \"/:locale/a-propos\",\n es: \"/:locale/acerca-de\",\n },\n \"/:locale/products/:id\": {\n fr: \"/:locale/produits/:id\",\n es: \"/:locale/productos/:id\",\n },\n }),\n },\n };\n\n export default config;\n ```\n\n \n \n\n ```typescript fileName=\"intlayer.config.ts\"\n import { Locales, type IntlayerConfig } from \"intlayer\";\n import { viteRewrite } from \"intlayer/routing\";\n\n const config: IntlayerConfig = {\n internationalization: {\n locales: [Locales.ENGLISH, Locales.FRENCH, Locales.SPANISH],\n defaultLocale: Locales.ENGLISH,\n },\n routing: {\n mode: \"prefix-all\",\n rewrite: viteRewrite({\n \"/:locale/about\": {\n fr: \"/:locale/a-propos\",\n es: \"/:locale/acerca-de\",\n },\n \"/:locale/products/:id\": {\n fr: \"/:locale/produits/:id\",\n es: \"/:locale/productos/:id\",\n },\n }),\n },\n };\n\n export default config;\n ```\n\n \n \n\n ```typescript fileName=\"intlayer.config.ts\"\n import { Locales, type IntlayerConfig } from \"intlayer\";\n import { nuxtRewrite } from \"intlayer/routing\";\n\n const config: IntlayerConfig = {\n internationalization: {\n locales: [Locales.ENGLISH, Locales.FRENCH, Locales.SPANISH],\n defaultLocale: Locales.ENGLISH,\n },\n routing: {\n mode: \"prefix-all\",\n rewrite: nuxtRewrite({\n \"/[locale]/about\": {\n fr: \"/[locale]/a-propos\",\n es: \"/[locale]/acerca-de\",\n },\n \"/[locale]/products/[id]\": {\n fr: \"/[locale]/produits/[id]\",\n es: \"/[locale]/productos/[id]\",\n },\n }),\n },\n };\n\n export default config;\n ```\n\n \n \n\n ```typescript fileName=\"intlayer.config.ts\"\n import { Locales, type IntlayerConfig } from \"intlayer\";\n import { svelteKitRewrite } from \"intlayer/routing\";\n\n const config: IntlayerConfig = {\n internationalization: {\n locales: [Locales.ENGLISH, Locales.FRENCH, Locales.SPANISH],\n defaultLocale: Locales.ENGLISH,\n },\n routing: {\n mode: \"prefix-all\",\n rewrite: svelteKitRewrite({\n \"/[locale]/about\": {\n fr: \"/[locale]/a-propos\",\n es: \"/[locale]/acerca-de\",\n },\n \"/[locale]/products/[id]\": {\n fr: \"/[locale]/produits/[id]\",\n es: \"/[locale]/productos/[id]\",\n },\n }),\n },\n };\n\n export default config;\n ```\n\n \n\n\nThis 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**.\n\nFor more information and integration guides, see the [Custom URL Rewrites Documentation](/en-GB/doc/concept/custom_url_rewrites).\n\n---\n\n### Enhanced Insertion Values\n\nIn 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.\n\nIntlayer now robustly handles nested React and Preact nodes within insertions, ensuring that complex UI structures are preserved and rendered correctly.\n\n**Example:**\n\n```typescript fileName=\"src/example.content.ts\"\nimport { insert } from \"intlayer\";\n\nexport default {\n key: \"my-key\",\n content: {\n myInsertion: insert(\"Hi {{name}}\"),\n },\n};\n```\n\n\n \n\n ```tsx\n import { useIntlayer } from \"next-intlayer\";\n\n const { myInsertion } = useIntlayer(\"my-key\");\n\n return (\n
\n `,\n })\n export class InsertionExampleComponent {\n content = useIntlayer(\"my-key\");\n }\n ```\n\n \n\n\n## Content Schema Validation\n\nIntlayer 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.\n\n### 1. Define Schemas\n\nDefine your schemas in `intlayer.config.ts`:\n\n```typescript fileName=\"intlayer.config.ts\"\nimport { z } from \"zod\";\n\nexport default {\n schemas: {\n \"seo-metadata\": z.object({\n title: z.string().min(50).max(60),\n description: z.string().min(150).max(160),\n }),\n },\n};\n```\n\n### 2. Apply Schemas to Dictionaries\n\nReference the schema key in your dictionary definition:\n\n```typescript fileName=\"src/example.content.ts\"\nimport { type Dictionary } from \"intlayer\";\n\nconst aboutPageMetaContent = {\n key: \"about-page-meta\",\n schema: \"seo-metadata\", // <--\n content: {\n title: \"About Our Company - Learn More About Us\",\n description: \"Discover our company's mission, values, and team.\",\n },\n} satisfies Dictionary;\n\nexport default aboutPageMetaContent;\n```\n\nIf the content doesn't match the schema (e.g., title is too short), the build process will raise an error.\n\n---\n\n### Enhanced Automatic Content Detection\n\nIn 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()`.\n\nThis behaviour is enabled by default. You can now fine-tune this detection either globally in your `intlayer.config.ts` or per dictionary.\n\n#### Granular Control\n\nYou can enable or disable specific types of transformations:\n\n```typescript fileName=\"intlayer.config.ts\"\nexport default {\n dictionary: {\n // contentAutoTransformation: false (default)\n contentAutoTransformation: {\n markdown: true,\n html: true,\n insertion: false, // Disable automatic insertion detection\n },\n },\n};\n```\n\n**v7 behaviour (Manual wrapping):**\n\n```typescript fileName=\"src/example.content.ts\"\nimport { md, insert } from \"intlayer\";\n\nexport default {\n key: \"my-key\",\n content: {\n myMarkdown: md(\"## Hello World\"),\n myInsertion: insert(\"Hi {{name}}\"),\n },\n};\n```\n\n**v8 behaviour (Automatic detection):**\n\n```typescript fileName=\"src/example.content.ts\"\nexport default {\n key: \"my-key\",\n contentAutoTransformation: true, // Can also be set by dictionary definition or globally in intlayer.config.ts\n content: {\n myMarkdown: \"## Hello World\", // Automatically detected as Markdown\n myHTML: \"
Hello World
\", // Automatically detected as HTML\n myInsertion: \"Hi {{name}}\", // Automatically detected as Insertion\n },\n};\n```\n\nThe underlying JSON result remains the same, preserving the rich type information needed for rendering:\n\n```json\n{\n \"key\": \"my-key\",\n \"content\": {\n \"myMarkdown\": {\n \"nodeType\": \"markdown\",\n \"markdown\": \"## Hello World\"\n },\n \"myHTML\": {\n \"nodeType\": \"html\",\n \"html\": \"
Hello World
\"\n },\n \"myInsertion\": {\n \"nodeType\": \"insertion\",\n \"insertion\": \"Hi {{name}}\"\n }\n }\n}\n```\n\n---\n\n## Localization: new `useIntl` hook\n\nA 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.\n\n\n \n\n ```tsx\n import { useIntl } from \"next-intlayer\";\n\n const intl = useIntl();\n\n const formattedPrice = new intl.NumberFormat({\n style: \"currency\",\n currency: \"USD\",\n }).format(123.45);\n ```\n\n \n \n\n ```tsx\n import { useIntl } from \"react-intlayer\";\n\n const intl = useIntl();\n\n const formattedPrice = new intl.NumberFormat({\n style: \"currency\",\n currency: \"USD\",\n }).format(123.45);\n ```\n\n \n \n\n ```vue\n \n ```\n\n \n \n\n ```tsx\n import { useIntl } from \"preact-intlayer\";\n\n const intl = useIntl();\n\n const formattedPrice = new intl.NumberFormat({\n style: \"currency\",\n currency: \"USD\",\n }).format(123.45);\n ```\n\n \n \n\n ```tsx\n import { useIntl } from \"solid-intlayer\";\n\n const intl = useIntl();\n\n const formattedPrice = new intl.NumberFormat({\n style: \"currency\",\n currency: \"USD\",\n }).format(123.45);\n ```\n\n \n \n\n ```svelte\n \n ```\n\n \n \n\n ```typescript\n import { Component, computed } from \"@angular/core\";\n import { useIntl } from \"angular-intlayer\";\n\n @Component({\n selector: \"app-intl-example\",\n template: `
{{ formattedPrice() }}
`,\n })\n export class IntlExampleComponent {\n intl = useIntl();\n\n formattedPrice = computed(() =>\n new (this.intl().NumberFormat)({\n style: \"currency\",\n currency: \"USD\",\n }).format(123.45)\n );\n }\n ```\n\n \n\n\n---\n\n## Tooling: VSCode Extension Enhancements\n\nThe Intlayer VSCode extension receives major updates in v8 to streamline your internationalisation workflow:\n\n- **Starting Time**: Performance improvements when opening a project.\n- **Caching**: Enhanced caching layer for near-instant validation and autocompletion.\n- **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.\n\n---\n\n## Compiler Optimizations\n\nIntlayer 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.\n\n\n \n \n ```typescript fileName=\"babel.config.js\"\n const {\n intlayerExtractBabelPlugin,\n intlayerOptimizeBabelPlugin,\n getExtractPluginOptions,\n getOptimizePluginOptions,\n } = require('@intlayer/babel');\n\n module.exports = {\n presets: ['next/babel'],\n plugins: [\n // Extract content from components into dictionaries\n [intlayerExtractBabelPlugin, getExtractPluginOptions()],\n // Optimize imports by replacing useIntlayer with direct dictionary imports\n [intlayerOptimizeBabelPlugin, getOptimizePluginOptions()],\n ],\n };\n ```\n\n \n \n \n ```typescript fileName=\"vite.config.js\"\n import { defineConfig } from 'vite';\n import { intlayer, intlayerCompiler } from \"vite-intlayer\";\n\n export default defineConfig({\n plugins: [intlayer(), intlayerCompiler()],\n });\n ```\n\n> For vue / svelte you will need to install the appropriate compiler package:\n>\n> ```bash\n> # For Vue\n> npm install @intlayer/vue-compiler\n> ```\n>\n> ```bash\n> # For Svelte\n> npm install @intlayer/svelte-compiler\n> ```\n\n \n\n\n---\n\n## Flexibility: Unified Import Mode\n\nThe `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.\n\n### Modes\n\n- **`static`** (Default): Dictionary is bundled at build time. Best for performance.\n- **`dynamic`**: Dictionary is loaded at runtime (e.g., via JSON fetch or suspense).\n- **`fetch`**: Dictionary is fetched from the CMS/Server at runtime and synchronised.\n\n**Migration:**\n\n| v7 Config | v8 Config |\n| :------------ | :------------------------------------ |\n| `live: true` | `importMode: 'fetch'` |\n| `live: false` | `importMode: 'static'` (or 'dynamic') |\n\nNote: 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.\n\n**Global Configuration Example:**\n\n```typescript fileName=\"intlayer.config.ts\"\nexport default {\n dictionary: {\n importMode: \"dynamic\", // Global default\n },\n // ...\n};\n```\n\n**Dictionary Example:**\n\n```typescript fileName=\"src/example.content.ts\"\nexport default {\n key: 'my-key',\n importMode: \"fetch\", // Overrides global config\n content: { ... }\n}\n```\n\n---\n\n## Dictionary Location Control\n\nv8 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.\n\n### Options\n\n- **`local`**: The dictionary exists only locally. It will not be pushed to the remote CMS.\n- **`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.\n- **`local_and_remote`**: The dictionary exists in both places. Local changes are pushed, and remote changes are pulled (synchronised).\n\n**Example:**\n\n```typescript fileName=\"src/example.content.ts\"\nexport default {\n key: 'my-key',\n location: \"local\", // Keep this dictionary local-only\n content: { ... }\n}\n```\n\n---\n\n## System Configuration Separation\n\nIntlayer 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.\n\nThe following properties have been moved from `content` to a new `system` property in `intlayer.config.ts`:\n\n- `dictionariesDir`\n- `moduleAugmentationDir`\n- `unmergedDictionariesDir`\n- `typesDir`\n- `mainDir`\n- `configDir`\n- `cacheDir`\n- `outputFilesPatternWithPath`\n\n**v7 behaviour:**\n\n```typescript fileName=\"intlayer.config.ts\"\nexport default {\n content: {\n contentDir: [\"src\"],\n dictionariesDir: \".intlayer/dictionary\", // Mixed with source config\n },\n};\n```\n\n**v8 behaviour:**\n\n```typescript fileName=\"intlayer.config.ts\"\nexport default {\n content: {\n contentDir: [\"src\"],\n },\n system: {\n dictionariesDir: \".intlayer/dictionary\", // Clearly separated\n },\n};\n```\n\n---\n\n## Content and Code Directory Separation\n\nIntlayer 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.\n\nPreviously, `contentDir` was used for both watching `.content.*` files and scanning code for `useIntlayer` calls. Now:\n\n- **`contentDir`**: Specifically for your content declaration files.\n- **`codeDir`**: Specifically for your application code that needs transformation (e.g., pruning, optimisation).\n\n**Migration:**\n\nIf 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.\n\n**v7 behaviour:**\n\n```typescript fileName=\"intlayer.config.ts\"\nexport default {\n content: {\n contentDir: [\"src\", \"@packages/design-system\"], // Used for both content and code\n },\n};\n```\n\n**v8 behaviour:**\n\n```typescript fileName=\"intlayer.config.ts\"\nexport default {\n content: {\n contentDir: [\"src/content\", \"@packages/design-system\"], // Only watch for src/content/*.content.* files here and @packages/design-system/dist/*.content.* files\n codeDir: [\"src\", \"@packages/design-system\"], // Only scan for code transformation here and @packages/design-system/src/*.content.* files\n },\n};\n```\n\n---\n\n## Framework: Svelte Improvements\n\nMarkdown 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.\n\n---\n\n## Migration notes from v7\n\n### Configuration Changes\n\n- **`live` property**: The `live` property in dictionaries is removed. Use `importMode: 'fetch'` instead.\n- **importMode**: The `build.importMode` property in configuration has been deprecated. Use `dictionary.importMode` instead.\n- **`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.\n- **Schema Validation**: To use the new `schema` feature, ensure you have `zod` installed in your project.\n\n---\n\n## Useful links\n\n- [Configuration Reference](/en-GB/doc/concept/configuration)\n- [Content File Documentation](/en-GB/doc/concept/content)\n- [HTML Content Documentation](/en-GB/doc/concept/content/html)\n- [Markdown Content Documentation](/en-GB/doc/concept/content/markdown)\n- [Custom URL Rewrites Documentation](/en-GB/doc/concept/custom_url_rewrites)\n","about":"Discover what's new in Intlayer v8. Major improvements in developer experience, content validation, and dictionary management.","url":"https://intlayer.org/en-GB/doc/releases/v8","datePublished":"22-09-2025","dateModified":"26-01-2026","keywords":"Intlayer, CMS, Developer Experience, Features, React, Next.js, JavaScript, TypeScript","license":"https://raw.githubusercontent.com/aymericzip/intlayer/refs/heads/main/LICENSE","audience":{"@type":"Audience","audienceType":"Developers, Content Managers"}}
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.
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).
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).
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.
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.
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>);
tsx
Copy code
Copy the code to the clipboard
import { useIntlayer } from "react-intlayer";const { myInsertion } = useIntlayer("my-key");return ( <div> {myInsertion({ name: 2, // number // or name: "John", // string // or name: <span>John</span>, // React element })} </div>);
vue
Copy code
Copy the code to the clipboard
<script setup>import { h } from "vue";import { useIntlayer } from "vue-intlayer";const { myInsertion } = useIntlayer("my-key");</script><template> <div> <component :is="myInsertion({ name: 2, // or name: 'John', // or name: h('span', 'John'), })" /> </div></template>
tsx
Copy code
Copy the code to the clipboard
import { useIntlayer } from "preact-intlayer";const { myInsertion } = useIntlayer("my-key");return ( <div> {myInsertion({ name: 2, // number // or name: "John", // string // or name: <span>John</span>, // Preact element })} </div>);
tsx
Copy code
Copy the code to the clipboard
import { useIntlayer } from "solid-intlayer";const { myInsertion } = useIntlayer("my-key");return ( <div> {myInsertion({ name: 2, // number // or name: "John", // string // or name: <span>John</span>, // Solid element })} </div>);
svelte
Copy code
Copy the code to the clipboard
<script> import { useIntlayer } from "svelte-intlayer"; const { myInsertion } = useIntlayer("my-key");</script><div> {myInsertion({ name: 2, // number // or name: "John", // string })}</div>
typescript
Copy code
Copy the code to the clipboard
import { Component } from "@angular/core";import { useIntlayer } from "angular-intlayer";@Component({ selector: "app-insertion-example", template: ` <div> {{ content().myInsertion({ name: 'John' }) }} </div> `,})export class InsertionExampleComponent { content = useIntlayer("my-key");}
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
Copy code
Copy the code to the clipboard
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
Copy code
Copy the code to the clipboard
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:
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:
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
Copy code
Copy the code to the clipboard
import { useIntl } from "next-intlayer";const intl = useIntl();const formattedPrice = new intl.NumberFormat({ style: "currency", currency: "USD",}).format(123.45);
tsx
Copy code
Copy the code to the clipboard
import { useIntl } from "react-intlayer";const intl = useIntl();const formattedPrice = new intl.NumberFormat({ style: "currency", currency: "USD",}).format(123.45);
vue
Copy code
Copy the code to the clipboard
<script setup>import { useIntl } from "vue-intlayer";const intl = useIntl();const formattedPrice = new intl.NumberFormat({ style: "currency", currency: "USD",}).format(123.45);</script>
tsx
Copy code
Copy the code to the clipboard
import { useIntl } from "preact-intlayer";const intl = useIntl();const formattedPrice = new intl.NumberFormat({ style: "currency", currency: "USD",}).format(123.45);
tsx
Copy code
Copy the code to the clipboard
import { useIntl } from "solid-intlayer";const intl = useIntl();const formattedPrice = new intl.NumberFormat({ style: "currency", currency: "USD",}).format(123.45);
import { Component, computed } from "@angular/core";import { useIntl } from "angular-intlayer";@Component({ selector: "app-intl-example", template: `<div>{{ formattedPrice() }}</div>`,})export class IntlExampleComponent { intl = useIntl(); formattedPrice = computed(() => new (this.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
Copy code
Copy the code to the clipboard
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()], ],};
vite.config.js
Copy code
Copy the code to the clipboard
import { defineConfig } from 'vite'; import { intlayer, intlayerCompiler } from "vite-intlayer"; export default defineConfig({ plugins: [intlayer(), intlayerCompiler()], });
For vue / svelte you will need to install the appropriate compiler package:
bash
Copy code
Copy the code to the clipboard
# For Vuenpm install @intlayer/vue-compiler
bash
Copy code
Copy the code to the clipboard
# For Sveltenpm install @intlayer/svelte-compiler
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:
Show all table content
Open the table in a modal to view all data content clearly
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.
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).
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:
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
Copy code
Copy the code to the clipboard
export default { content: { contentDir: ["src", "@packages/design-system"], // Used for both content and code },};
v8 behaviour:
intlayer.config.ts
Copy code
Copy the code to the clipboard
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.
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 },};
export default { content: { contentDir: ["src", "@packages/design-system"], // Used for both content and code },};
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 },};