diff --git a/packages/cms/keystatic/package.json b/packages/cms/keystatic/package.json
index 2b29cec1d..2ab8dc6c4 100644
--- a/packages/cms/keystatic/package.json
+++ b/packages/cms/keystatic/package.json
@@ -16,7 +16,8 @@
},
"dependencies": {
"@keystatic/core": "0.5.14",
- "@keystatic/next": "5.0.0"
+ "@keystatic/next": "5.0.0",
+ "@markdoc/markdoc": "^0.4.0"
},
"devDependencies": {
"@kit/cms": "workspace:^",
@@ -25,6 +26,8 @@
"@kit/tsconfig": "workspace:*",
"@kit/ui": "workspace:^",
"@types/node": "^20.12.8",
+ "@types/react": "^18.3.1",
+ "react": "18.3.1",
"zod": "^3.23.6"
},
"eslintConfig": {
diff --git a/packages/cms/keystatic/src/content-renderer.tsx b/packages/cms/keystatic/src/content-renderer.tsx
index 5dfc181ec..40040611c 100644
--- a/packages/cms/keystatic/src/content-renderer.tsx
+++ b/packages/cms/keystatic/src/content-renderer.tsx
@@ -1,6 +1,7 @@
-import { DocumentElement } from '@keystatic/core';
-import { DocumentRenderer } from '@keystatic/core/renderer';
+import * as React from 'react';
-export function KeystaticDocumentRenderer(props: { content: unknown }) {
- return ;
+import Markdoc from '@markdoc/markdoc';
+
+export function KeystaticDocumentRenderer({ content }: { content: unknown }) {
+ return Markdoc.renderers.react(content as string, React);
}
diff --git a/packages/cms/keystatic/src/keystatic-client.ts b/packages/cms/keystatic/src/keystatic-client.ts
index 8c1bf416e..ffb9ab7fa 100644
--- a/packages/cms/keystatic/src/keystatic-client.ts
+++ b/packages/cms/keystatic/src/keystatic-client.ts
@@ -1,3 +1,5 @@
+import Markdoc from '@markdoc/markdoc';
+
import { Cms, CmsClient } from '@kit/cms';
import { createKeystaticReader } from './create-reader';
@@ -138,7 +140,8 @@ class KeystaticClient implements CmsClient {
? new Date(item.entry.publishedAt)
: new Date();
- const content = await item.entry.content();
+ const markdoc = await item.entry.content();
+ const content = Markdoc.transform(markdoc.node);
return {
id: item.slug,
diff --git a/packages/cms/keystatic/src/keystatic.config.ts b/packages/cms/keystatic/src/keystatic.config.ts
index 19efb7da0..c3141bfdb 100644
--- a/packages/cms/keystatic/src/keystatic.config.ts
+++ b/packages/cms/keystatic/src/keystatic.config.ts
@@ -1,15 +1,24 @@
-import { collection, config, fields } from '@keystatic/core';
+import {
+ CloudConfig,
+ GitHubConfig,
+ LocalConfig,
+ collection,
+ config,
+ fields,
+} from '@keystatic/core';
import { Entry } from '@keystatic/core/reader';
import { z } from 'zod';
+type ZodOutputFor = z.ZodType;
+
const local = z.object({
kind: z.literal('local'),
-});
+}) satisfies ZodOutputFor;
const cloud = z.object({
kind: z.literal('cloud'),
project: z.string(),
-});
+}) satisfies ZodOutputFor;
const github = z.object({
kind: z.literal('github'),
@@ -19,7 +28,7 @@ const github = z.object({
githubToken: z.string({
required_error: 'Please provide a GitHub token',
}),
-});
+}) satisfies ZodOutputFor;
const storage = z.union([local, cloud, github]).parse({
kind: process.env.KEYSTATIC_STORAGE_KIND ?? 'local',
@@ -34,6 +43,34 @@ const keyStaticConfig = createKeyStaticConfig();
export default keyStaticConfig;
+function getContentField() {
+ return fields.markdoc({
+ label: 'Content',
+ options: {
+ link: true,
+ blockquote: true,
+ bold: true,
+ divider: true,
+ orderedList: true,
+ unorderedList: true,
+ strikethrough: true,
+ heading: true,
+ code: true,
+ italic: true,
+ image: {
+ directory: 'public/site/images',
+ publicPath: '/site/images',
+ schema: {
+ title: fields.text({
+ label: 'Caption',
+ description: 'The text to display under the image in a caption.',
+ }),
+ },
+ },
+ },
+ });
+}
+
export type PostEntryProps = Entry<
(typeof keyStaticConfig)['collections']['posts']
>;
@@ -64,23 +101,7 @@ function createKeyStaticConfig() {
}),
language: fields.text({ label: 'Language' }),
order: fields.number({ label: 'Order' }),
- content: fields.document({
- label: 'Content',
- formatting: true,
- dividers: true,
- links: true,
- images: {
- directory: 'public/site/images',
- publicPath: '/site/images',
- schema: {
- title: fields.text({
- label: 'Caption',
- description:
- 'The text to display under the image in a caption.',
- }),
- },
- },
- }),
+ content: getContentField(),
},
}),
documentation: collection({
@@ -90,23 +111,7 @@ function createKeyStaticConfig() {
format: { contentField: 'content' },
schema: {
title: fields.slug({ name: { label: 'Title' } }),
- content: fields.document({
- label: 'Content',
- formatting: true,
- dividers: true,
- links: true,
- images: {
- directory: 'public/site/images',
- publicPath: '/site/images',
- schema: {
- title: fields.text({
- label: 'Caption',
- description:
- 'The text to display under the image in a caption.',
- }),
- },
- },
- }),
+ content: getContentField(),
image: fields.image({
label: 'Image',
directory: 'public/site/images',
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index c41715b61..fca3ac2bc 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -426,6 +426,9 @@ importers:
'@keystatic/next':
specifier: 5.0.0
version: 5.0.0(@keystatic/core@0.5.14)(next@14.2.3)(react-dom@18.3.1)(react@18.3.1)
+ '@markdoc/markdoc':
+ specifier: ^0.4.0
+ version: 0.4.0(@types/react@18.3.1)(react@18.3.1)
devDependencies:
'@kit/cms':
specifier: workspace:^
@@ -445,6 +448,12 @@ importers:
'@types/node':
specifier: ^20.12.8
version: 20.12.10
+ '@types/react':
+ specifier: ^18.3.1
+ version: 18.3.1
+ react:
+ specifier: 18.3.1
+ version: 18.3.1
zod:
specifier: ^3.23.6
version: 3.23.6