Markdoc Components
12/27/2025

Support for keystatic

Highly recommend using keystatic as your article editor. Once configured, it enables zero-code publishing of articles!

Support for keystatic

Deploy according to the documentation

You'll get this.

image

keystatic.config.ts #

This project uses the keystatic config and has added components.

import { config, fields, collection } from "@keystatic/core";
import { networkImage } from "./keystatic_components/networkImage";
import { horizontalGallery } from "./keystatic_components/horizontalGallery";

export default config({
  storage: {
    kind: "local",
    // kind: 'github',
    // repo: {
    //    owner: REPO_OWNER,
    //    name: REPO_NAME
    // }
  },
  ui: {
    brand: { name: "Markdoc Components" },
  },
  collections: {
    Posts: collection({
      label: "Posts",
      slugField: "title",
      path: "src/content/*",
      entryLayout: "content",
      format: { contentField: "content" },
      columns: ["title", "date", "description"],
      schema: {
        date: fields.date({ label: "Event date" }),
        title: fields.slug({
          name: { label: "Title" },
          slug: {
            label: "slug",
            description: "For Local Src Path 'Aa-Zz,0-9,-,_,.,'are prohibited",
          },
        }),
        description: fields.text({
          label: "description",
          multiline: true,
        }),
        // tags: fields.array(fields.text({ label: "Tag" }), {
        //   label: "Tag",
        //   itemLabel: (props) => props.value,
        // }),
        //Local image

        image: fields.image({
          label: "Image",
          directory: "src/assets/images",
          publicPath: "@/assets/images/",
        }),

        //image Url

        // image: fields.url({
        //   label: 'URL',
        //   description: 'The website URL'
        // }),
        content: fields.markdoc({
          label: "Content",
          options: {
            image: {
              directory: "src/assets/images",
              publicPath: "@/assets/images/",
            },
          },
          components: {
            networkImage,
            horizontalGallery,
          },
        }),
      },
    }),
  },
});

networkImage #

//keystatic_components\networkImage.ts
import { fields } from "@keystatic/core";
import { block } from "@keystatic/core/content-components";
import { createElement } from "react";

export const networkImage = block({
  label: "Image For URL",
  ContentView: ({ value }) =>
    createElement("div", { style: { textAlign: "center" } }, [
      createElement("img", {
        key: "img",
        src: value.src || "",
        style: { width: "50%", display: "inline-block" },
      }),
    ]),
  schema: {
    src: fields.url({
      label: "Image URL",
      validation: { isRequired: true },
    }),
    alt: fields.text({ label: "Image Alt" }),
  },
});

horizontalGallery #

//keystatic_components\horizontalGallery.ts

import { fields } from "@keystatic/core";
import { block } from "@keystatic/core/content-components";
import { createElement } from "react";

export const horizontalGallery = block({
  label: "Horizontal Gallery",
  ContentView: ({ value }) =>
    createElement(
      "div",
      {
        style: {
          display: "flex",
          overflowX: "auto",
          gap: "10px",
          paddingBottom: "10px",
        },
      },
      value.images.map((image, index) =>
        createElement("img", {
          key: index,
          src: image.src || "",
          alt: image.alt,
          style: { height: "150px", borderRadius: "8px" },
        })
      )
    ),
  schema: {
    images: fields.array(
      fields.object({
        src: fields.url({
          label: "Image URL",
          validation: { isRequired: true },
        }),
        alt: fields.text({ label: "Image Alt" }),
      }),
      {
        label: "Images",
        itemLabel: (props) => props.fields.alt.value || "Image",
      }
    ),
  },
});