diff --git a/README.md b/README.md
index add5ed40..8da6af84 100755
--- a/README.md
+++ b/README.md
@@ -524,9 +524,7 @@ sbBridge.on(['input', 'published', 'change'], (event) => {
## Rendering Rich Text
-> [!WARNING]
-> We have identified issues with richtext and Types on React 19 and Next.js 15. As a temporary measure, we advise you to continue using React 18 and Next.js 14 until we have fully resolved the issues.
-
+### Client-side Rich Text Rendering
You can render rich text fields by using the `StoryblokRichText` component:
```ts
@@ -569,7 +567,61 @@ function App() {
}
```
-For a comprehensive list of options you can provide to the `useStoryblokRichText`, please consult the [Full options](https://github.com/storyblok/richtext?tab=readme-ov-file#options) documentation.
+### Server-side Rich Text Rendering
+
+> [!INFO]
+> Recommended for Next.js 15 and React 19.
+
+You can render rich text fields by using the `StoryblokServerRichText` component:
+
+```ts
+import { StoryblokServerRichText, StoryblokStory } from '@storyblok/react/rsc';
+import { StoryblokClient, ISbStoriesParams } from '@storyblok/react';
+import { getStoryblokApi } from '@/lib/storyblok'; // Remember to import from the local file
+
+async function App() {
+ const { data } = await fetchData();
+
+ if (!data?.story?.content) {
+ return
Loading...
;
+ }
+
+ return (
+
+
+
+ );
+}
+
+export async function fetchData() {
+ let sbParams: ISbStoriesParams = { version: 'draft' };
+
+ const storyblokApi: StoryblokClient = getStoryblokApi();
+ return storyblokApi.get(`cdn/stories/home`, sbParams);
+}
+```
+
+Or you can have more control by using the `useStoryblokServerRichText` hook:
+
+```ts
+import { useStoryblokServerRichText, convertAttributesInElement } from '@storyblok/react';
+import Codeblock from './Codeblock';
+
+function App() {
+ const { render } = useStoryblokServerRichText({
+ // options like resolvers
+ });
+
+ const html = render(doc);
+ const formattedHtml = convertAttributesInElement(html as React.ReactElement); // JSX
+
+ return (
+
+ {formattedHtml}
+
+ );
+}
+```
### Overriding the default resolvers
diff --git a/playground/next15/.gitignore b/playground/next15/.gitignore
index d32cc78b..44fdeff2 100644
--- a/playground/next15/.gitignore
+++ b/playground/next15/.gitignore
@@ -38,3 +38,5 @@ yarn-error.log*
# typescript
*.tsbuildinfo
next-env.d.ts
+
+certificates
\ No newline at end of file
diff --git a/playground/next15/package.json b/playground/next15/package.json
index 4fd666b7..4633038f 100644
--- a/playground/next15/package.json
+++ b/playground/next15/package.json
@@ -3,16 +3,20 @@
"version": "0.1.0",
"private": true,
"scripts": {
- "dev": "next dev",
+ "dev": "next dev --experimental-https",
"build": "next build",
"start": "next start",
"lint": "next lint"
},
"dependencies": {
"@storyblok/react": "workspace:^",
+ "@tailwindcss/postcss": "^4.0.14",
+ "@tailwindcss/typography": "^0.5.16",
"next": "15.3.2",
+ "postcss": "^8.5.3",
"react": "19.1.0",
- "react-dom": "19.1.0"
+ "react-dom": "19.1.0",
+ "tailwindcss": "^4.0.14"
},
"devDependencies": {
"@types/node": "^22",
diff --git a/playground/next15/postcss.config.mjs b/playground/next15/postcss.config.mjs
new file mode 100644
index 00000000..05466d5f
--- /dev/null
+++ b/playground/next15/postcss.config.mjs
@@ -0,0 +1,8 @@
+/** @type {import('tailwindcss').Config} */
+
+const config = {
+ plugins: {
+ '@tailwindcss/postcss': {},
+ },
+};
+export default config;
diff --git a/playground/next15/src/app/globals.css b/playground/next15/src/app/globals.css
index e3734be1..b01bdf43 100644
--- a/playground/next15/src/app/globals.css
+++ b/playground/next15/src/app/globals.css
@@ -1,3 +1,10 @@
+@import 'tailwindcss';
+@plugin '@tailwindcss/typography';
+
+span[data-type='emoji'] img {
+ @apply m-0;
+}
+
:root {
--background: #ffffff;
--foreground: #171717;
@@ -24,10 +31,13 @@ body {
-moz-osx-font-smoothing: grayscale;
}
-* {
- box-sizing: border-box;
- padding: 0;
- margin: 0;
+@layer base {
+ * {
+ margin: 0;
+ padding: 0;
+ box-sizing: border-box;
+ scroll-behavior: smooth;
+ }
}
a {
diff --git a/playground/next15/src/app/layout.tsx b/playground/next15/src/app/layout.tsx
index 923b47a8..adcfa545 100644
--- a/playground/next15/src/app/layout.tsx
+++ b/playground/next15/src/app/layout.tsx
@@ -1,4 +1,5 @@
import StoryblokProvider from '@/components/StoryblokProvider';
+import '../app/globals.css';
export const metadata = {
title: 'Create Next App',
diff --git a/playground/next15/src/app/page.tsx b/playground/next15/src/app/page.tsx
index b726adad..6fba5608 100644
--- a/playground/next15/src/app/page.tsx
+++ b/playground/next15/src/app/page.tsx
@@ -1,105 +1,34 @@
-import type {
- ISbStoriesParams,
- StoryblokClient,
- StoryblokRichTextNode,
-} from '@storyblok/react/rsc';
-import { MarkTypes, StoryblokRichText, StoryblokStory,
-} from '@storyblok/react/rsc';
-import { getStoryblokApi } from '@/lib/storyblok';
+import type { ISbStoriesParams, StoryblokClient } from '@storyblok/react/rsc';
+import { StoryblokStory } from '@storyblok/react/rsc';
+import { getStoryblokApi } from '@/lib/storyblok'; // Remember to import from the local file
import Link from 'next/link';
-import type { ReactElement } from 'react';
export default async function Home() {
const { data } = await fetchData();
- const doc = {
- type: 'doc',
- content: [
- {
- type: 'paragraph',
- content: [
- {
- type: 'text',
- text: 'This is a test of the StoryblokRichText component.',
- },
- ],
- },
- {
- type: 'paragraph',
- content: [
- {
- text: 'Internal Link',
- type: 'text',
- marks: [
- {
- type: 'link',
- attrs: {
- href: '/',
- uuid: '8489bed8-d86f-4fde-965c-e3d748e12147',
- anchor: null,
- target: '_self',
- linktype: 'story',
- },
- },
- ],
- },
- ],
- },
- {
- type: 'paragraph',
- content: [
- {
- text: 'External link',
- type: 'text',
- marks: [
- {
- type: 'link',
- attrs: {
- href: 'https://alvarosaburido.dev',
- uuid: null,
- anchor: null,
- target: '_blank',
- linktype: 'url',
- },
- },
- ],
- },
- ],
- },
- ],
- };
- const resolvers = {
- // custom resolvers
- [MarkTypes.LINK]: (node: StoryblokRichTextNode) => {
- return node.attrs?.linktype === 'story'
- ? (
-
- {node.text}
-
- )
- : (
-
- {node.text}
-
- );
- },
- };
-
return (
-
-
- Story:
- {data.story.id}
-
-
-
-
+
+
+
+ Storyblok Next.js 15 Example
+
+
+
+
+ Go to Rich Text Example
+
+
+
+ {data.story && (
+
+
+
+ )}
+
+
);
}
@@ -107,5 +36,5 @@ export async function fetchData() {
const sbParams: ISbStoriesParams = { version: 'draft' };
const storyblokApi: StoryblokClient = getStoryblokApi();
- return storyblokApi.get(`cdn/stories/home`, sbParams);
+ return storyblokApi.get(`cdn/stories/react`, sbParams);
}
diff --git a/playground/next15/src/app/richtext/page.tsx b/playground/next15/src/app/richtext/page.tsx
new file mode 100644
index 00000000..69b07e50
--- /dev/null
+++ b/playground/next15/src/app/richtext/page.tsx
@@ -0,0 +1,37 @@
+import type { ISbStoriesParams, StoryblokClient } from '@storyblok/react/rsc';
+import { getStoryblokApi } from '@/lib/storyblok';
+import { StoryblokServerRichText } from '@storyblok/react/rsc';
+
+export default async function RichtextPage() {
+ const { data } = await fetchData();
+
+ if (!data.story?.content) {
+ return (
+
+
+ Loading content...
+
+
+ );
+ }
+
+ return (
+
+
Rich Text Example
+ {data.story.content.richText
+ ? (
+
+ )
+ : (
+
No content available
+ )}
+
+ );
+}
+
+export async function fetchData() {
+ const sbParams: ISbStoriesParams = { version: 'draft' };
+
+ const storyblokApi: StoryblokClient = getStoryblokApi();
+ return storyblokApi.get(`cdn/stories/react/test-richtext`, sbParams);
+}
diff --git a/playground/next15/src/components/EmojiRandomizer.tsx b/playground/next15/src/components/EmojiRandomizer.tsx
new file mode 100644
index 00000000..17fd9c61
--- /dev/null
+++ b/playground/next15/src/components/EmojiRandomizer.tsx
@@ -0,0 +1,51 @@
+'use client';
+
+import React, { type FC, useState } from 'react';
+import type { SbBlokData } from '@storyblok/react';
+
+interface EmojiRandomizerProps {
+ blok: SbBlokData & {
+ label?: string;
+ };
+}
+
+/**
+ * A component that displays a label and a random emoji that changes on click
+ */
+const EmojiRandomizer: FC = ({ blok }) => {
+ // List of fun emojis to randomly choose from
+ const emojis = ['😊', '🎉', '🚀', '✨', '🌈', '🎨', '🎸', '🎮', '🍕', '🌺'];
+
+ // State to track current emoji
+ const [currentEmoji, setCurrentEmoji] = useState(() =>
+ emojis[Math.floor(Math.random() * emojis.length)],
+ );
+
+ /**
+ * Generates a new random emoji different from the current one
+ */
+ const randomizeEmoji = () => {
+ let newEmoji;
+ do {
+ newEmoji = emojis[Math.floor(Math.random() * emojis.length)];
+ } while (newEmoji === currentEmoji);
+
+ setCurrentEmoji(newEmoji);
+ };
+
+ return (
+
+
+ {currentEmoji}
+
+
+ {blok.label || 'Randomize Emoji'}
+
+
+ );
+};
+
+export default EmojiRandomizer;
diff --git a/playground/next15/src/lib/storyblok.ts b/playground/next15/src/lib/storyblok.ts
index 378deaa9..a7f120e6 100644
--- a/playground/next15/src/lib/storyblok.ts
+++ b/playground/next15/src/lib/storyblok.ts
@@ -1,3 +1,4 @@
+import EmojiRandomizer from '@/components/EmojiRandomizer';
import Grid from '@/components/Grid';
import IFrameEmbed from '@/components/IFrameEmbed';
import Page from '@/components/Page';
@@ -12,5 +13,6 @@ export const getStoryblokApi = storyblokInit({
'page': Page,
'grid': Grid,
'iframe-embed': IFrameEmbed,
+ 'emoji-randomizer': EmojiRandomizer,
},
});
diff --git a/playground/react/components/emoji-randomizer.tsx b/playground/react/components/emoji-randomizer.tsx
new file mode 100644
index 00000000..8bde99ad
--- /dev/null
+++ b/playground/react/components/emoji-randomizer.tsx
@@ -0,0 +1,49 @@
+import React, { type FC, useState } from 'react';
+import type { SbBlokData } from '@storyblok/react';
+
+interface EmojiRandomizerProps {
+ blok: SbBlokData & {
+ label?: string;
+ };
+}
+
+/**
+ * A component that displays a label and a random emoji that changes on click
+ */
+const EmojiRandomizer: FC = ({ blok }) => {
+ // List of fun emojis to randomly choose from
+ const emojis = ['😊', '🎉', '🚀', '✨', '🌈', '🎨', '🎸', '🎮', '🍕', '🌺'];
+
+ // State to track current emoji
+ const [currentEmoji, setCurrentEmoji] = useState(() =>
+ emojis[Math.floor(Math.random() * emojis.length)],
+ );
+
+ /**
+ * Generates a new random emoji different from the current one
+ */
+ const randomizeEmoji = () => {
+ let newEmoji;
+ do {
+ newEmoji = emojis[Math.floor(Math.random() * emojis.length)];
+ } while (newEmoji === currentEmoji);
+
+ setCurrentEmoji(newEmoji);
+ };
+
+ return (
+
+
+ {currentEmoji}
+
+
+ {blok.label || 'Randomize Emoji'}
+
+
+ );
+};
+
+export default EmojiRandomizer;
diff --git a/playground/react/components/iframe-embed.tsx b/playground/react/components/iframe-embed.tsx
deleted file mode 100644
index 63e53a2f..00000000
--- a/playground/react/components/iframe-embed.tsx
+++ /dev/null
@@ -1,19 +0,0 @@
-import React from 'react';
-import type { SbBlokData } from '@storyblok/react';
-import { storyblokEditable } from '@storyblok/react';
-
-interface IframeEmbedProps {
- blok: SbBlokData;
-}
-
-const IFrameEmbed = ({ blok }: IframeEmbedProps) => {
- return (
-
- );
-};
-
-export default IFrameEmbed;
diff --git a/playground/react/index.html b/playground/react/index.html
index 33feda87..bbe9db01 100644
--- a/playground/react/index.html
+++ b/playground/react/index.html
@@ -4,6 +4,7 @@
React App
+
diff --git a/playground/react/index.tsx b/playground/react/index.tsx
index 36708023..68ef0277 100644
--- a/playground/react/index.tsx
+++ b/playground/react/index.tsx
@@ -8,7 +8,7 @@ import Page from './components/page';
import Feature from './components/feature';
import { apiPlugin, storyblokInit } from '@storyblok/react';
-import IFrameEmbed from './components/iframe-embed';
+import EmojiRandomizer from './components/emoji-randomizer';
// https://app.storyblok.com/#/me/spaces/147897
storyblokInit({
@@ -19,7 +19,7 @@ storyblokInit({
'grid': Grid,
'feature': Feature,
'page': Page,
- 'iframe-embed': IFrameEmbed,
+ 'emoji-randomizer': EmojiRandomizer,
},
});
diff --git a/playground/react/package.json b/playground/react/package.json
index baa49fe6..23c0bfdd 100644
--- a/playground/react/package.json
+++ b/playground/react/package.json
@@ -8,14 +8,20 @@
},
"dependencies": {
"@storyblok/react": "workspace:^",
- "react": "^19.1.0",
- "react-dom": "^19.1.0",
- "react-router": "^7.6.0"
+ "@tailwindcss/vite": "^4.0.14",
+ "react": "^18.3.1",
+ "react-dom": "^18.3.1",
+ "react-router": "^7.1.1",
+ "tailwindcss": "^4.0.14"
},
"devDependencies": {
+ "@tailwindcss/typography": "^0.5.16",
"@types/node": "^22.15.17",
"@types/react": "^19.1.4",
"@vitejs/plugin-basic-ssl": "^2.0.0",
+ "react": "^19.1.0",
+ "react-dom": "^19.1.0",
+ "react-router": "^7.6.0",
"vite": "^6.3.5"
}
}
diff --git a/playground/react/pages/RichtextPage.tsx b/playground/react/pages/RichtextPage.tsx
index f8b68c16..e98daad8 100644
--- a/playground/react/pages/RichtextPage.tsx
+++ b/playground/react/pages/RichtextPage.tsx
@@ -5,11 +5,26 @@ function RichtextPage() {
const story = useStoryblok('react/test-richtext', { version: 'draft' });
if (!story?.content) {
- return Loading...
;
+ return (
+
+
+ Loading content...
+
+
+ );
}
return (
- story.content.richText &&
+
+
Rich Text Example
+ {story.content.richText
+ ? (
+
+ )
+ : (
+
No content available
+ )}
+
);
}
diff --git a/playground/react/styles.css b/playground/react/styles.css
new file mode 100644
index 00000000..70fa7917
--- /dev/null
+++ b/playground/react/styles.css
@@ -0,0 +1,6 @@
+@import 'tailwindcss';
+@plugin '@tailwindcss/typography';
+
+span[data-type='emoji'] img {
+ @apply m-0;
+}
diff --git a/playground/react/vite.config.ts b/playground/react/vite.config.ts
index f5ecbaaa..a3683b71 100644
--- a/playground/react/vite.config.ts
+++ b/playground/react/vite.config.ts
@@ -1,14 +1,15 @@
-import type { PluginOption } from 'vite';
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
import { resolve } from 'node:path';
import basicSsl from '@vitejs/plugin-basic-ssl';
+import tailwindcss from '@tailwindcss/vite';
export default defineConfig({
plugins: [
react(),
basicSsl(),
- ] as PluginOption[],
+ tailwindcss(),
+ ],
resolve: {
alias: {
'@storyblok/react': resolve(__dirname, '../../src/index.ts'),
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 3db08ded..886c830f 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -35,7 +35,7 @@ importers:
version: 6.0.3(cypress@14.3.3)
'@storyblok/eslint-config':
specifier: ^0.3.0
- version: 0.3.0(@typescript-eslint/utils@8.29.1(eslint@9.26.0(jiti@2.4.2))(typescript@5.8.3))(@vue/compiler-sfc@3.5.13)(eslint@9.26.0(jiti@2.4.2))(typescript@5.8.3)(vitest@3.1.3(@types/debug@4.1.12)(@types/node@22.15.17)(jiti@2.4.2)(yaml@2.6.0))
+ version: 0.3.0(@typescript-eslint/utils@8.29.1(eslint@9.26.0(jiti@2.4.2))(typescript@5.8.3))(@vue/compiler-sfc@3.5.13)(eslint@9.26.0(jiti@2.4.2))(typescript@5.8.3)(vitest@3.1.3(@types/debug@4.1.12)(@types/node@22.15.17)(jiti@2.4.2)(lightningcss@1.29.2)(yaml@2.6.0))
'@tsconfig/recommended':
specifier: ^1.0.8
version: 1.0.8
@@ -47,7 +47,7 @@ importers:
version: 19.1.4
'@vitejs/plugin-react':
specifier: ^4.4.1
- version: 4.4.1(vite@6.3.5(@types/node@22.15.17)(jiti@2.4.2)(yaml@2.6.0))
+ version: 4.4.1(vite@6.3.5(@types/node@22.15.17)(jiti@2.4.2)(lightningcss@1.29.2)(yaml@2.6.0))
babel-jest:
specifier: ^29.7.0
version: 29.7.0(@babel/core@7.27.1)
@@ -86,13 +86,13 @@ importers:
version: 5.8.3
vite:
specifier: ^6.3.5
- version: 6.3.5(@types/node@22.15.17)(jiti@2.4.2)(yaml@2.6.0)
+ version: 6.3.5(@types/node@22.15.17)(jiti@2.4.2)(lightningcss@1.29.2)(yaml@2.6.0)
vite-plugin-dts:
specifier: ^4.5.3
- version: 4.5.3(@types/node@22.15.17)(rollup@4.39.0)(typescript@5.8.3)(vite@6.3.5(@types/node@22.15.17)(jiti@2.4.2)(yaml@2.6.0))
+ version: 4.5.3(@types/node@22.15.17)(rollup@4.39.0)(typescript@5.8.3)(vite@6.3.5(@types/node@22.15.17)(jiti@2.4.2)(lightningcss@1.29.2)(yaml@2.6.0))
vitest:
specifier: ^3.1.3
- version: 3.1.3(@types/debug@4.1.12)(@types/node@22.15.17)(jiti@2.4.2)(yaml@2.6.0)
+ version: 3.1.3(@types/debug@4.1.12)(@types/node@22.15.17)(jiti@2.4.2)(lightningcss@1.29.2)(yaml@2.6.0)
playground/next13:
dependencies:
@@ -152,15 +152,27 @@ importers:
'@storyblok/react':
specifier: workspace:^
version: link:../..
+ '@tailwindcss/postcss':
+ specifier: ^4.0.14
+ version: 4.0.14
+ '@tailwindcss/typography':
+ specifier: ^0.5.16
+ version: 0.5.16(tailwindcss@4.0.14)
next:
specifier: 15.3.2
version: 15.3.2(@babel/core@7.27.1)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
+ postcss:
+ specifier: ^8.5.3
+ version: 8.5.3
react:
specifier: 19.1.0
version: 19.1.0
react-dom:
specifier: 19.1.0
version: 19.1.0(react@19.1.0)
+ tailwindcss:
+ specifier: ^4.0.14
+ version: 4.0.14
devDependencies:
'@types/node':
specifier: ^22
@@ -177,16 +189,25 @@ importers:
'@storyblok/react':
specifier: workspace:^
version: link:../..
+ '@tailwindcss/vite':
+ specifier: ^4.0.14
+ version: 4.0.14(vite@6.3.5(@types/node@22.15.17)(jiti@2.4.2)(lightningcss@1.29.2)(yaml@2.6.0))
react:
- specifier: ^19.1.0
- version: 19.1.0
+ specifier: ^18.3.1
+ version: 18.3.1
react-dom:
- specifier: ^19.1.0
- version: 19.1.0(react@19.1.0)
+ specifier: ^18.3.1
+ version: 18.3.1(react@18.3.1)
react-router:
- specifier: ^7.6.0
- version: 7.6.0(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
+ specifier: ^7.1.1
+ version: 7.6.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
+ tailwindcss:
+ specifier: ^4.0.14
+ version: 4.0.14
devDependencies:
+ '@tailwindcss/typography':
+ specifier: ^0.5.16
+ version: 0.5.16(tailwindcss@4.0.14)
'@types/node':
specifier: ^22.15.17
version: 22.15.17
@@ -195,13 +216,17 @@ importers:
version: 19.1.4
'@vitejs/plugin-basic-ssl':
specifier: ^2.0.0
- version: 2.0.0(vite@6.3.5(@types/node@22.15.17)(jiti@2.4.2)(yaml@2.6.0))
+ version: 2.0.0(vite@6.3.5(@types/node@22.15.17)(jiti@2.4.2)(lightningcss@1.29.2)(yaml@2.6.0))
vite:
specifier: ^6.3.5
- version: 6.3.5(@types/node@22.15.17)(jiti@2.4.2)(yaml@2.6.0)
+ version: 6.3.5(@types/node@22.15.17)(jiti@2.4.2)(lightningcss@1.29.2)(yaml@2.6.0)
packages:
+ '@alloc/quick-lru@5.2.0':
+ resolution: {integrity: sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==}
+ engines: {node: '>=10'}
+
'@ampproject/remapping@2.3.0':
resolution: {integrity: sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==}
engines: {node: '>=6.0.0'}
@@ -1838,6 +1863,92 @@ packages:
'@swc/helpers@0.5.2':
resolution: {integrity: sha512-E4KcWTpoLHqwPHLxidpOqQbcrZVgi0rsmmZXUle1jXmJfuIf/UWpczUJ7MZZ5tlxytgJXyp0w4PGkkeLiuIdZw==}
+ '@tailwindcss/node@4.0.14':
+ resolution: {integrity: sha512-Ux9NbFkKWYE4rfUFz6M5JFLs/GEYP6ysxT8uSyPn6aTbh2K3xDE1zz++eVK4Vwx799fzMF8CID9sdHn4j/Ab8w==}
+
+ '@tailwindcss/oxide-android-arm64@4.0.14':
+ resolution: {integrity: sha512-VBFKC2rFyfJ5J8lRwjy6ub3rgpY186kAcYgiUr8ArR8BAZzMruyeKJ6mlsD22Zp5ZLcPW/FXMasJiJBx0WsdQg==}
+ engines: {node: '>= 10'}
+ cpu: [arm64]
+ os: [android]
+
+ '@tailwindcss/oxide-darwin-arm64@4.0.14':
+ resolution: {integrity: sha512-U3XOwLrefGr2YQZ9DXasDSNWGPZBCh8F62+AExBEDMLDfvLLgI/HDzY8Oq8p/JtqkAY38sWPOaNnRwEGKU5Zmg==}
+ engines: {node: '>= 10'}
+ cpu: [arm64]
+ os: [darwin]
+
+ '@tailwindcss/oxide-darwin-x64@4.0.14':
+ resolution: {integrity: sha512-V5AjFuc3ndWGnOi1d379UsODb0TzAS2DYIP/lwEbfvafUaD2aNZIcbwJtYu2DQqO2+s/XBvDVA+w4yUyaewRwg==}
+ engines: {node: '>= 10'}
+ cpu: [x64]
+ os: [darwin]
+
+ '@tailwindcss/oxide-freebsd-x64@4.0.14':
+ resolution: {integrity: sha512-tXvtxbaZfcPfqBwW3f53lTcyH6EDT+1eT7yabwcfcxTs+8yTPqxsDUhrqe9MrnEzpNkd+R/QAjJapfd4tjWdLg==}
+ engines: {node: '>= 10'}
+ cpu: [x64]
+ os: [freebsd]
+
+ '@tailwindcss/oxide-linux-arm-gnueabihf@4.0.14':
+ resolution: {integrity: sha512-cSeLNWWqIWeSTmBntQvyY2/2gcLX8rkPFfDDTQVF8qbRcRMVPLxBvFVJyfSAYRNch6ZyVH2GI6dtgALOBDpdNA==}
+ engines: {node: '>= 10'}
+ cpu: [arm]
+ os: [linux]
+
+ '@tailwindcss/oxide-linux-arm64-gnu@4.0.14':
+ resolution: {integrity: sha512-bwDWLBalXFMDItcSXzFk6y7QKvj6oFlaY9vM+agTlwFL1n1OhDHYLZkSjaYsh6KCeG0VB0r7H8PUJVOM1LRZyg==}
+ engines: {node: '>= 10'}
+ cpu: [arm64]
+ os: [linux]
+
+ '@tailwindcss/oxide-linux-arm64-musl@4.0.14':
+ resolution: {integrity: sha512-gVkJdnR/L6iIcGYXx64HGJRmlme2FGr/aZH0W6u4A3RgPMAb+6ELRLi+UBiH83RXBm9vwCfkIC/q8T51h8vUJQ==}
+ engines: {node: '>= 10'}
+ cpu: [arm64]
+ os: [linux]
+
+ '@tailwindcss/oxide-linux-x64-gnu@4.0.14':
+ resolution: {integrity: sha512-EE+EQ+c6tTpzsg+LGO1uuusjXxYx0Q00JE5ubcIGfsogSKth8n8i2BcS2wYTQe4jXGs+BQs35l78BIPzgwLddw==}
+ engines: {node: '>= 10'}
+ cpu: [x64]
+ os: [linux]
+
+ '@tailwindcss/oxide-linux-x64-musl@4.0.14':
+ resolution: {integrity: sha512-KCCOzo+L6XPT0oUp2Jwh233ETRQ/F6cwUnMnR0FvMUCbkDAzHbcyOgpfuAtRa5HD0WbTbH4pVD+S0pn1EhNfbw==}
+ engines: {node: '>= 10'}
+ cpu: [x64]
+ os: [linux]
+
+ '@tailwindcss/oxide-win32-arm64-msvc@4.0.14':
+ resolution: {integrity: sha512-AHObFiFL9lNYcm3tZSPqa/cHGpM5wOrNmM2uOMoKppp+0Hom5uuyRh0QkOp7jftsHZdrZUpmoz0Mp6vhh2XtUg==}
+ engines: {node: '>= 10'}
+ cpu: [arm64]
+ os: [win32]
+
+ '@tailwindcss/oxide-win32-x64-msvc@4.0.14':
+ resolution: {integrity: sha512-rNXXMDJfCJLw/ZaFTOLOHoGULxyXfh2iXTGiChFiYTSgKBKQHIGEpV0yn5N25WGzJJ+VBnRjHzlmDqRV+d//oQ==}
+ engines: {node: '>= 10'}
+ cpu: [x64]
+ os: [win32]
+
+ '@tailwindcss/oxide@4.0.14':
+ resolution: {integrity: sha512-M8VCNyO/NBi5vJ2cRcI9u8w7Si+i76a7o1vveoGtbbjpEYJZYiyc7f2VGps/DqawO56l3tImIbq2OT/533jcrA==}
+ engines: {node: '>= 10'}
+
+ '@tailwindcss/postcss@4.0.14':
+ resolution: {integrity: sha512-+uIR6KtKhla1XeIanF27KtrfYy+PX+R679v5LxbkmEZlhQe3g8rk+wKj7Xgt++rWGRuFLGMXY80Ek8JNn+kN/g==}
+
+ '@tailwindcss/typography@0.5.16':
+ resolution: {integrity: sha512-0wDLwCVF5V3x3b1SGXPCDcdsbDHMBe+lkFzBRaHeLvNi+nrrnZ1lA18u+OTWO8iSWU2GxUOCvlXtDuqftc1oiA==}
+ peerDependencies:
+ tailwindcss: '>=3.0.0 || insiders || >=4.0.0-alpha.20 || >=4.0.0-beta.1'
+
+ '@tailwindcss/vite@4.0.14':
+ resolution: {integrity: sha512-y69ztPTRFy+13EPS/7dEFVl7q2Goh1pQueVO8IfGeyqSpcx/joNJXFk0lLhMgUbF0VFJotwRSb9ZY7Xoq3r26Q==}
+ peerDependencies:
+ vite: ^5.2.0 || ^6
+
'@tsconfig/recommended@1.0.8':
resolution: {integrity: sha512-TotjFaaXveVUdsrXCdalyF6E5RyG6+7hHHQVZonQtdlk1rJZ1myDIvPUUKPhoYv+JAzThb2lQJh9+9ZfF46hsA==}
@@ -4252,6 +4363,70 @@ packages:
resolution: {integrity: sha512-mET5AIwl7MR2IAKYYoVBBpV0OnkKQ1xGj2IMMeEFIs42QAkEVjRtFZGWmQ28WeU7MP779iAgOaOy93Mn44mn6g==}
hasBin: true
+ lightningcss-darwin-arm64@1.29.2:
+ resolution: {integrity: sha512-cK/eMabSViKn/PG8U/a7aCorpeKLMlK0bQeNHmdb7qUnBkNPnL+oV5DjJUo0kqWsJUapZsM4jCfYItbqBDvlcA==}
+ engines: {node: '>= 12.0.0'}
+ cpu: [arm64]
+ os: [darwin]
+
+ lightningcss-darwin-x64@1.29.2:
+ resolution: {integrity: sha512-j5qYxamyQw4kDXX5hnnCKMf3mLlHvG44f24Qyi2965/Ycz829MYqjrVg2H8BidybHBp9kom4D7DR5VqCKDXS0w==}
+ engines: {node: '>= 12.0.0'}
+ cpu: [x64]
+ os: [darwin]
+
+ lightningcss-freebsd-x64@1.29.2:
+ resolution: {integrity: sha512-wDk7M2tM78Ii8ek9YjnY8MjV5f5JN2qNVO+/0BAGZRvXKtQrBC4/cn4ssQIpKIPP44YXw6gFdpUF+Ps+RGsCwg==}
+ engines: {node: '>= 12.0.0'}
+ cpu: [x64]
+ os: [freebsd]
+
+ lightningcss-linux-arm-gnueabihf@1.29.2:
+ resolution: {integrity: sha512-IRUrOrAF2Z+KExdExe3Rz7NSTuuJ2HvCGlMKoquK5pjvo2JY4Rybr+NrKnq0U0hZnx5AnGsuFHjGnNT14w26sg==}
+ engines: {node: '>= 12.0.0'}
+ cpu: [arm]
+ os: [linux]
+
+ lightningcss-linux-arm64-gnu@1.29.2:
+ resolution: {integrity: sha512-KKCpOlmhdjvUTX/mBuaKemp0oeDIBBLFiU5Fnqxh1/DZ4JPZi4evEH7TKoSBFOSOV3J7iEmmBaw/8dpiUvRKlQ==}
+ engines: {node: '>= 12.0.0'}
+ cpu: [arm64]
+ os: [linux]
+
+ lightningcss-linux-arm64-musl@1.29.2:
+ resolution: {integrity: sha512-Q64eM1bPlOOUgxFmoPUefqzY1yV3ctFPE6d/Vt7WzLW4rKTv7MyYNky+FWxRpLkNASTnKQUaiMJ87zNODIrrKQ==}
+ engines: {node: '>= 12.0.0'}
+ cpu: [arm64]
+ os: [linux]
+
+ lightningcss-linux-x64-gnu@1.29.2:
+ resolution: {integrity: sha512-0v6idDCPG6epLXtBH/RPkHvYx74CVziHo6TMYga8O2EiQApnUPZsbR9nFNrg2cgBzk1AYqEd95TlrsL7nYABQg==}
+ engines: {node: '>= 12.0.0'}
+ cpu: [x64]
+ os: [linux]
+
+ lightningcss-linux-x64-musl@1.29.2:
+ resolution: {integrity: sha512-rMpz2yawkgGT8RULc5S4WiZopVMOFWjiItBT7aSfDX4NQav6M44rhn5hjtkKzB+wMTRlLLqxkeYEtQ3dd9696w==}
+ engines: {node: '>= 12.0.0'}
+ cpu: [x64]
+ os: [linux]
+
+ lightningcss-win32-arm64-msvc@1.29.2:
+ resolution: {integrity: sha512-nL7zRW6evGQqYVu/bKGK+zShyz8OVzsCotFgc7judbt6wnB2KbiKKJwBE4SGoDBQ1O94RjW4asrCjQL4i8Fhbw==}
+ engines: {node: '>= 12.0.0'}
+ cpu: [arm64]
+ os: [win32]
+
+ lightningcss-win32-x64-msvc@1.29.2:
+ resolution: {integrity: sha512-EdIUW3B2vLuHmv7urfzMI/h2fmlnOQBk1xlsDxkN1tCWKjNFjfLhGxYk8C8mzpSfr+A6jFFIi8fU6LbQGsRWjA==}
+ engines: {node: '>= 12.0.0'}
+ cpu: [x64]
+ os: [win32]
+
+ lightningcss@1.29.2:
+ resolution: {integrity: sha512-6b6gd/RUXKaw5keVdSEtqFVdzWnU5jMxTUjA2bVcMNPLwSQ08Sv/UodBVtETLCn7k4S1Ibxwh7k68IwLZPgKaA==}
+ engines: {node: '>= 12.0.0'}
+
lines-and-columns@1.2.4:
resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==}
@@ -4287,6 +4462,9 @@ packages:
lodash.camelcase@4.3.0:
resolution: {integrity: sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==}
+ lodash.castarray@4.4.0:
+ resolution: {integrity: sha512-aVx8ztPv7/2ULbArGJ2Y42bG1mEQ5mGjpdvrbJcJFU3TbYybe+QlLS4pst9zV52ymy2in1KpFPiZnAOATxD4+Q==}
+
lodash.debounce@4.0.8:
resolution: {integrity: sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==}
@@ -4862,6 +5040,10 @@ packages:
resolution: {integrity: sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==}
engines: {node: '>= 0.4'}
+ postcss-selector-parser@6.0.10:
+ resolution: {integrity: sha512-IQ7TZdoaqbT+LCpShg46jnZVlhWD2w6iQYAcYXfHARZ7X1t/UGhhceQDs5X0cGqKvYlHNOuv7Oa1xmb0oQuA3w==}
+ engines: {node: '>=4'}
+
postcss-selector-parser@6.1.2:
resolution: {integrity: sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==}
engines: {node: '>=4'}
@@ -5469,6 +5651,9 @@ packages:
resolution: {integrity: sha512-vrozgXDQwYO72vHjUb/HnFbQx1exDjoKzqx23aXEg2a9VIg2TSFZ8FmeZpTjUCFMYw7mpX4BE2SFu8wI7asYsw==}
engines: {node: ^14.18.0 || >=16.0.0}
+ tailwindcss@4.0.14:
+ resolution: {integrity: sha512-92YT2dpt671tFiHH/e1ok9D987N9fHD5VWoly1CdPD/Cd1HMglvZwP3nx2yTj2lbXDAHt8QssZkxTLCCTNL+xw==}
+
tapable@2.2.1:
resolution: {integrity: sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==}
engines: {node: '>=6'}
@@ -5947,12 +6132,14 @@ packages:
snapshots:
+ '@alloc/quick-lru@5.2.0': {}
+
'@ampproject/remapping@2.3.0':
dependencies:
'@jridgewell/gen-mapping': 0.3.8
'@jridgewell/trace-mapping': 0.3.25
- '@antfu/eslint-config@3.6.2(@typescript-eslint/utils@8.29.1(eslint@9.26.0(jiti@2.4.2))(typescript@5.8.3))(@vue/compiler-sfc@3.5.13)(eslint-plugin-format@0.1.2(eslint@9.26.0(jiti@2.4.2)))(eslint@9.26.0(jiti@2.4.2))(typescript@5.8.3)(vitest@3.1.3(@types/debug@4.1.12)(@types/node@22.15.17)(jiti@2.4.2)(yaml@2.6.0))':
+ '@antfu/eslint-config@3.6.2(@typescript-eslint/utils@8.29.1(eslint@9.26.0(jiti@2.4.2))(typescript@5.8.3))(@vue/compiler-sfc@3.5.13)(eslint-plugin-format@0.1.2(eslint@9.26.0(jiti@2.4.2)))(eslint@9.26.0(jiti@2.4.2))(typescript@5.8.3)(vitest@3.1.3(@types/debug@4.1.12)(@types/node@22.15.17)(jiti@2.4.2)(lightningcss@1.29.2)(yaml@2.6.0))':
dependencies:
'@antfu/install-pkg': 0.4.1
'@clack/prompts': 0.7.0
@@ -5961,7 +6148,7 @@ snapshots:
'@stylistic/eslint-plugin': 2.10.1(eslint@9.26.0(jiti@2.4.2))(typescript@5.8.3)
'@typescript-eslint/eslint-plugin': 8.29.1(@typescript-eslint/parser@8.29.1(eslint@9.26.0(jiti@2.4.2))(typescript@5.8.3))(eslint@9.26.0(jiti@2.4.2))(typescript@5.8.3)
'@typescript-eslint/parser': 8.29.1(eslint@9.26.0(jiti@2.4.2))(typescript@5.8.3)
- '@vitest/eslint-plugin': 1.1.10(@typescript-eslint/utils@8.29.1(eslint@9.26.0(jiti@2.4.2))(typescript@5.8.3))(eslint@9.26.0(jiti@2.4.2))(typescript@5.8.3)(vitest@3.1.3(@types/debug@4.1.12)(@types/node@22.15.17)(jiti@2.4.2)(yaml@2.6.0))
+ '@vitest/eslint-plugin': 1.1.10(@typescript-eslint/utils@8.29.1(eslint@9.26.0(jiti@2.4.2))(typescript@5.8.3))(eslint@9.26.0(jiti@2.4.2))(typescript@5.8.3)(vitest@3.1.3(@types/debug@4.1.12)(@types/node@22.15.17)(jiti@2.4.2)(lightningcss@1.29.2)(yaml@2.6.0))
eslint: 9.26.0(jiti@2.4.2)
eslint-config-flat-gitignore: 0.3.0(eslint@9.26.0(jiti@2.4.2))
eslint-flat-config-utils: 0.4.0
@@ -7750,9 +7937,9 @@ snapshots:
'@sinonjs/commons': 3.0.1
optional: true
- '@storyblok/eslint-config@0.3.0(@typescript-eslint/utils@8.29.1(eslint@9.26.0(jiti@2.4.2))(typescript@5.8.3))(@vue/compiler-sfc@3.5.13)(eslint@9.26.0(jiti@2.4.2))(typescript@5.8.3)(vitest@3.1.3(@types/debug@4.1.12)(@types/node@22.15.17)(jiti@2.4.2)(yaml@2.6.0))':
+ '@storyblok/eslint-config@0.3.0(@typescript-eslint/utils@8.29.1(eslint@9.26.0(jiti@2.4.2))(typescript@5.8.3))(@vue/compiler-sfc@3.5.13)(eslint@9.26.0(jiti@2.4.2))(typescript@5.8.3)(vitest@3.1.3(@types/debug@4.1.12)(@types/node@22.15.17)(jiti@2.4.2)(lightningcss@1.29.2)(yaml@2.6.0))':
dependencies:
- '@antfu/eslint-config': 3.6.2(@typescript-eslint/utils@8.29.1(eslint@9.26.0(jiti@2.4.2))(typescript@5.8.3))(@vue/compiler-sfc@3.5.13)(eslint-plugin-format@0.1.2(eslint@9.26.0(jiti@2.4.2)))(eslint@9.26.0(jiti@2.4.2))(typescript@5.8.3)(vitest@3.1.3(@types/debug@4.1.12)(@types/node@22.15.17)(jiti@2.4.2)(yaml@2.6.0))
+ '@antfu/eslint-config': 3.6.2(@typescript-eslint/utils@8.29.1(eslint@9.26.0(jiti@2.4.2))(typescript@5.8.3))(@vue/compiler-sfc@3.5.13)(eslint-plugin-format@0.1.2(eslint@9.26.0(jiti@2.4.2)))(eslint@9.26.0(jiti@2.4.2))(typescript@5.8.3)(vitest@3.1.3(@types/debug@4.1.12)(@types/node@22.15.17)(jiti@2.4.2)(lightningcss@1.29.2)(yaml@2.6.0))
eslint: 9.26.0(jiti@2.4.2)
eslint-plugin-format: 0.1.2(eslint@9.26.0(jiti@2.4.2))
transitivePeerDependencies:
@@ -7812,6 +7999,84 @@ snapshots:
dependencies:
tslib: 2.8.1
+ '@tailwindcss/node@4.0.14':
+ dependencies:
+ enhanced-resolve: 5.18.1
+ jiti: 2.4.2
+ tailwindcss: 4.0.14
+
+ '@tailwindcss/oxide-android-arm64@4.0.14':
+ optional: true
+
+ '@tailwindcss/oxide-darwin-arm64@4.0.14':
+ optional: true
+
+ '@tailwindcss/oxide-darwin-x64@4.0.14':
+ optional: true
+
+ '@tailwindcss/oxide-freebsd-x64@4.0.14':
+ optional: true
+
+ '@tailwindcss/oxide-linux-arm-gnueabihf@4.0.14':
+ optional: true
+
+ '@tailwindcss/oxide-linux-arm64-gnu@4.0.14':
+ optional: true
+
+ '@tailwindcss/oxide-linux-arm64-musl@4.0.14':
+ optional: true
+
+ '@tailwindcss/oxide-linux-x64-gnu@4.0.14':
+ optional: true
+
+ '@tailwindcss/oxide-linux-x64-musl@4.0.14':
+ optional: true
+
+ '@tailwindcss/oxide-win32-arm64-msvc@4.0.14':
+ optional: true
+
+ '@tailwindcss/oxide-win32-x64-msvc@4.0.14':
+ optional: true
+
+ '@tailwindcss/oxide@4.0.14':
+ optionalDependencies:
+ '@tailwindcss/oxide-android-arm64': 4.0.14
+ '@tailwindcss/oxide-darwin-arm64': 4.0.14
+ '@tailwindcss/oxide-darwin-x64': 4.0.14
+ '@tailwindcss/oxide-freebsd-x64': 4.0.14
+ '@tailwindcss/oxide-linux-arm-gnueabihf': 4.0.14
+ '@tailwindcss/oxide-linux-arm64-gnu': 4.0.14
+ '@tailwindcss/oxide-linux-arm64-musl': 4.0.14
+ '@tailwindcss/oxide-linux-x64-gnu': 4.0.14
+ '@tailwindcss/oxide-linux-x64-musl': 4.0.14
+ '@tailwindcss/oxide-win32-arm64-msvc': 4.0.14
+ '@tailwindcss/oxide-win32-x64-msvc': 4.0.14
+
+ '@tailwindcss/postcss@4.0.14':
+ dependencies:
+ '@alloc/quick-lru': 5.2.0
+ '@tailwindcss/node': 4.0.14
+ '@tailwindcss/oxide': 4.0.14
+ lightningcss: 1.29.2
+ postcss: 8.5.3
+ tailwindcss: 4.0.14
+
+ '@tailwindcss/typography@0.5.16(tailwindcss@4.0.14)':
+ dependencies:
+ lodash.castarray: 4.4.0
+ lodash.isplainobject: 4.0.6
+ lodash.merge: 4.6.2
+ postcss-selector-parser: 6.0.10
+ tailwindcss: 4.0.14
+
+ '@tailwindcss/vite@4.0.14(vite@6.3.5(@types/node@22.15.17)(jiti@2.4.2)(lightningcss@1.29.2)(yaml@2.6.0))':
+ dependencies:
+ '@tailwindcss/node': 4.0.14
+ '@tailwindcss/oxide': 4.0.14
+ lightningcss: 1.29.2
+ tailwindcss: 4.0.14
+ vite: 6.3.5(@types/node@22.15.17)(jiti@2.4.2)(lightningcss@1.29.2)(yaml@2.6.0)
+
'@tsconfig/recommended@1.0.8': {}
'@tybys/wasm-util@0.9.0':
@@ -8146,28 +8411,28 @@ snapshots:
'@unrs/resolver-binding-win32-x64-msvc@1.4.1':
optional: true
- '@vitejs/plugin-basic-ssl@2.0.0(vite@6.3.5(@types/node@22.15.17)(jiti@2.4.2)(yaml@2.6.0))':
+ '@vitejs/plugin-basic-ssl@2.0.0(vite@6.3.5(@types/node@22.15.17)(jiti@2.4.2)(lightningcss@1.29.2)(yaml@2.6.0))':
dependencies:
- vite: 6.3.5(@types/node@22.15.17)(jiti@2.4.2)(yaml@2.6.0)
+ vite: 6.3.5(@types/node@22.15.17)(jiti@2.4.2)(lightningcss@1.29.2)(yaml@2.6.0)
- '@vitejs/plugin-react@4.4.1(vite@6.3.5(@types/node@22.15.17)(jiti@2.4.2)(yaml@2.6.0))':
+ '@vitejs/plugin-react@4.4.1(vite@6.3.5(@types/node@22.15.17)(jiti@2.4.2)(lightningcss@1.29.2)(yaml@2.6.0))':
dependencies:
'@babel/core': 7.27.1
'@babel/plugin-transform-react-jsx-self': 7.25.9(@babel/core@7.27.1)
'@babel/plugin-transform-react-jsx-source': 7.25.9(@babel/core@7.27.1)
'@types/babel__core': 7.20.5
react-refresh: 0.17.0
- vite: 6.3.5(@types/node@22.15.17)(jiti@2.4.2)(yaml@2.6.0)
+ vite: 6.3.5(@types/node@22.15.17)(jiti@2.4.2)(lightningcss@1.29.2)(yaml@2.6.0)
transitivePeerDependencies:
- supports-color
- '@vitest/eslint-plugin@1.1.10(@typescript-eslint/utils@8.29.1(eslint@9.26.0(jiti@2.4.2))(typescript@5.8.3))(eslint@9.26.0(jiti@2.4.2))(typescript@5.8.3)(vitest@3.1.3(@types/debug@4.1.12)(@types/node@22.15.17)(jiti@2.4.2)(yaml@2.6.0))':
+ '@vitest/eslint-plugin@1.1.10(@typescript-eslint/utils@8.29.1(eslint@9.26.0(jiti@2.4.2))(typescript@5.8.3))(eslint@9.26.0(jiti@2.4.2))(typescript@5.8.3)(vitest@3.1.3(@types/debug@4.1.12)(@types/node@22.15.17)(jiti@2.4.2)(lightningcss@1.29.2)(yaml@2.6.0))':
dependencies:
'@typescript-eslint/utils': 8.29.1(eslint@9.26.0(jiti@2.4.2))(typescript@5.8.3)
eslint: 9.26.0(jiti@2.4.2)
optionalDependencies:
typescript: 5.8.3
- vitest: 3.1.3(@types/debug@4.1.12)(@types/node@22.15.17)(jiti@2.4.2)(yaml@2.6.0)
+ vitest: 3.1.3(@types/debug@4.1.12)(@types/node@22.15.17)(jiti@2.4.2)(lightningcss@1.29.2)(yaml@2.6.0)
'@vitest/expect@3.1.3':
dependencies:
@@ -8176,13 +8441,13 @@ snapshots:
chai: 5.2.0
tinyrainbow: 2.0.0
- '@vitest/mocker@3.1.3(vite@6.3.5(@types/node@22.15.17)(jiti@2.4.2)(yaml@2.6.0))':
+ '@vitest/mocker@3.1.3(vite@6.3.5(@types/node@22.15.17)(jiti@2.4.2)(lightningcss@1.29.2)(yaml@2.6.0))':
dependencies:
'@vitest/spy': 3.1.3
estree-walker: 3.0.3
magic-string: 0.30.17
optionalDependencies:
- vite: 6.3.5(@types/node@22.15.17)(jiti@2.4.2)(yaml@2.6.0)
+ vite: 6.3.5(@types/node@22.15.17)(jiti@2.4.2)(lightningcss@1.29.2)(yaml@2.6.0)
'@vitest/pretty-format@3.1.3':
dependencies:
@@ -9029,8 +9294,7 @@ snapshots:
dequal@2.0.3: {}
- detect-libc@2.0.3:
- optional: true
+ detect-libc@2.0.3: {}
detect-newline@3.1.0:
optional: true
@@ -10933,6 +11197,51 @@ snapshots:
transitivePeerDependencies:
- supports-color
+ lightningcss-darwin-arm64@1.29.2:
+ optional: true
+
+ lightningcss-darwin-x64@1.29.2:
+ optional: true
+
+ lightningcss-freebsd-x64@1.29.2:
+ optional: true
+
+ lightningcss-linux-arm-gnueabihf@1.29.2:
+ optional: true
+
+ lightningcss-linux-arm64-gnu@1.29.2:
+ optional: true
+
+ lightningcss-linux-arm64-musl@1.29.2:
+ optional: true
+
+ lightningcss-linux-x64-gnu@1.29.2:
+ optional: true
+
+ lightningcss-linux-x64-musl@1.29.2:
+ optional: true
+
+ lightningcss-win32-arm64-msvc@1.29.2:
+ optional: true
+
+ lightningcss-win32-x64-msvc@1.29.2:
+ optional: true
+
+ lightningcss@1.29.2:
+ dependencies:
+ detect-libc: 2.0.3
+ optionalDependencies:
+ lightningcss-darwin-arm64: 1.29.2
+ lightningcss-darwin-x64: 1.29.2
+ lightningcss-freebsd-x64: 1.29.2
+ lightningcss-linux-arm-gnueabihf: 1.29.2
+ lightningcss-linux-arm64-gnu: 1.29.2
+ lightningcss-linux-arm64-musl: 1.29.2
+ lightningcss-linux-x64-gnu: 1.29.2
+ lightningcss-linux-x64-musl: 1.29.2
+ lightningcss-win32-arm64-msvc: 1.29.2
+ lightningcss-win32-x64-msvc: 1.29.2
+
lines-and-columns@1.2.4: {}
listr2@3.14.0(enquirer@2.4.1):
@@ -10973,6 +11282,8 @@ snapshots:
lodash.camelcase@4.3.0: {}
+ lodash.castarray@4.4.0: {}
+
lodash.debounce@4.0.8: {}
lodash.isplainobject@4.0.6: {}
@@ -11719,6 +12030,11 @@ snapshots:
possible-typed-array-names@1.1.0: {}
+ postcss-selector-parser@6.0.10:
+ dependencies:
+ cssesc: 3.0.0
+ util-deprecate: 1.0.2
+
postcss-selector-parser@6.1.2:
dependencies:
cssesc: 3.0.0
@@ -11825,13 +12141,13 @@ snapshots:
react-refresh@0.17.0: {}
- react-router@7.6.0(react-dom@19.1.0(react@19.1.0))(react@19.1.0):
+ react-router@7.6.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1):
dependencies:
cookie: 1.0.2
- react: 19.1.0
+ react: 18.3.1
set-cookie-parser: 2.7.1
optionalDependencies:
- react-dom: 19.1.0(react@19.1.0)
+ react-dom: 18.3.1(react@18.3.1)
react@18.3.1:
dependencies:
@@ -12461,6 +12777,8 @@ snapshots:
'@pkgr/core': 0.1.1
tslib: 2.8.1
+ tailwindcss@4.0.14: {}
+
tapable@2.2.1: {}
test-exclude@6.0.0:
@@ -12723,13 +13041,13 @@ snapshots:
core-util-is: 1.0.2
extsprintf: 1.3.0
- vite-node@3.1.3(@types/node@22.15.17)(jiti@2.4.2)(yaml@2.6.0):
+ vite-node@3.1.3(@types/node@22.15.17)(jiti@2.4.2)(lightningcss@1.29.2)(yaml@2.6.0):
dependencies:
cac: 6.7.14
debug: 4.4.0(supports-color@8.1.1)
es-module-lexer: 1.7.0
pathe: 2.0.3
- vite: 6.3.5(@types/node@22.15.17)(jiti@2.4.2)(yaml@2.6.0)
+ vite: 6.3.5(@types/node@22.15.17)(jiti@2.4.2)(lightningcss@1.29.2)(yaml@2.6.0)
transitivePeerDependencies:
- '@types/node'
- jiti
@@ -12744,7 +13062,7 @@ snapshots:
- tsx
- yaml
- vite-plugin-dts@4.5.3(@types/node@22.15.17)(rollup@4.39.0)(typescript@5.8.3)(vite@6.3.5(@types/node@22.15.17)(jiti@2.4.2)(yaml@2.6.0)):
+ vite-plugin-dts@4.5.3(@types/node@22.15.17)(rollup@4.39.0)(typescript@5.8.3)(vite@6.3.5(@types/node@22.15.17)(jiti@2.4.2)(lightningcss@1.29.2)(yaml@2.6.0)):
dependencies:
'@microsoft/api-extractor': 7.51.1(@types/node@22.15.17)
'@rollup/pluginutils': 5.1.4(rollup@4.39.0)
@@ -12757,13 +13075,13 @@ snapshots:
magic-string: 0.30.17
typescript: 5.8.3
optionalDependencies:
- vite: 6.3.5(@types/node@22.15.17)(jiti@2.4.2)(yaml@2.6.0)
+ vite: 6.3.5(@types/node@22.15.17)(jiti@2.4.2)(lightningcss@1.29.2)(yaml@2.6.0)
transitivePeerDependencies:
- '@types/node'
- rollup
- supports-color
- vite@6.3.5(@types/node@22.15.17)(jiti@2.4.2)(yaml@2.6.0):
+ vite@6.3.5(@types/node@22.15.17)(jiti@2.4.2)(lightningcss@1.29.2)(yaml@2.6.0):
dependencies:
esbuild: 0.25.4
fdir: 6.4.4(picomatch@4.0.2)
@@ -12775,12 +13093,13 @@ snapshots:
'@types/node': 22.15.17
fsevents: 2.3.3
jiti: 2.4.2
+ lightningcss: 1.29.2
yaml: 2.6.0
- vitest@3.1.3(@types/debug@4.1.12)(@types/node@22.15.17)(jiti@2.4.2)(yaml@2.6.0):
+ vitest@3.1.3(@types/debug@4.1.12)(@types/node@22.15.17)(jiti@2.4.2)(lightningcss@1.29.2)(yaml@2.6.0):
dependencies:
'@vitest/expect': 3.1.3
- '@vitest/mocker': 3.1.3(vite@6.3.5(@types/node@22.15.17)(jiti@2.4.2)(yaml@2.6.0))
+ '@vitest/mocker': 3.1.3(vite@6.3.5(@types/node@22.15.17)(jiti@2.4.2)(lightningcss@1.29.2)(yaml@2.6.0))
'@vitest/pretty-format': 3.1.3
'@vitest/runner': 3.1.3
'@vitest/snapshot': 3.1.3
@@ -12797,8 +13116,8 @@ snapshots:
tinyglobby: 0.2.13
tinypool: 1.0.2
tinyrainbow: 2.0.0
- vite: 6.3.5(@types/node@22.15.17)(jiti@2.4.2)(yaml@2.6.0)
- vite-node: 3.1.3(@types/node@22.15.17)(jiti@2.4.2)(yaml@2.6.0)
+ vite: 6.3.5(@types/node@22.15.17)(jiti@2.4.2)(lightningcss@1.29.2)(yaml@2.6.0)
+ vite-node: 3.1.3(@types/node@22.15.17)(jiti@2.4.2)(lightningcss@1.29.2)(yaml@2.6.0)
why-is-node-running: 2.3.0
optionalDependencies:
'@types/debug': 4.1.12
diff --git a/src/rsc/index.ts b/src/rsc/index.ts
index a2b5b9dd..9e683f7c 100644
--- a/src/rsc/index.ts
+++ b/src/rsc/index.ts
@@ -1,13 +1,7 @@
-import { useStoryblokRichText } from '../richtext';
-
-// Export the main function
-export { useStoryblokRichText };
-
-/** @deprecated Use useStoryblokRichText instead */
-export const useStoryblokRichTextResolver = useStoryblokRichText;
-
-export { default as StoryblokRichText } from '../storyblok-rich-text';
export * from './common';
+export { useStoryblokServerRichText } from './server-richtext';
+
+export { default as StoryblokServerRichText } from './server-storyblok-rich-text';
export { default as StoryblokStory } from './story';
export {
BlockTypes,
diff --git a/src/rsc/server-richtext.ts b/src/rsc/server-richtext.ts
new file mode 100644
index 00000000..17f59883
--- /dev/null
+++ b/src/rsc/server-richtext.ts
@@ -0,0 +1,29 @@
+import React from 'react';
+import { StoryblokServerComponent } from './common';
+import type { StoryblokRichTextNode, StoryblokRichTextOptions } from '@storyblok/js';
+import { BlockTypes, richTextResolver } from '@storyblok/js';
+
+export function componentResolver(node: StoryblokRichTextNode): React.ReactElement {
+ const body = node?.attrs?.body;
+ return React.createElement(StoryblokServerComponent, {
+ blok: Array.isArray(body) ? body[0] : undefined,
+ key: node.attrs?.id,
+ });
+}
+
+export function useStoryblokServerRichText(
+ options: StoryblokRichTextOptions,
+) {
+ const mergedOptions = {
+ renderFn: React.createElement,
+ textFn: (text: string) => React.createElement(React.Fragment, {
+ key: Math.random().toString(36).substring(2, 15),
+ }, text),
+ resolvers: {
+ [BlockTypes.COMPONENT]: componentResolver,
+ ...options.resolvers,
+ },
+ keyedResolvers: true,
+ };
+ return richTextResolver(mergedOptions);
+}
diff --git a/src/rsc/server-storyblok-rich-text.tsx b/src/rsc/server-storyblok-rich-text.tsx
new file mode 100644
index 00000000..f1982faf
--- /dev/null
+++ b/src/rsc/server-storyblok-rich-text.tsx
@@ -0,0 +1,24 @@
+import React, { forwardRef } from 'react';
+
+import { convertAttributesInElement } from '../utils';
+import type { StoryblokRichTextProps } from '../types';
+import { useStoryblokServerRichText } from './server-richtext';
+
+const StoryblokRichText = forwardRef(
+ ({ doc, resolvers }, ref) => {
+ const { render } = useStoryblokServerRichText({
+ resolvers,
+ });
+
+ const html = render(doc);
+ const formattedHtml = convertAttributesInElement(html as React.ReactElement);
+
+ return (
+
+ {formattedHtml}
+
+ );
+ },
+);
+
+export default StoryblokRichText;