Blocks

A block is a React component plus a schema. The schema tells editkraft which fields are editable; the component renders them — and marks what’s inline editable with data-ek-field.

#Define a block

Fields come from @editkraft/schema (ekText, ekRichText, ekImage, ekSelect, ekLink). A defineBlock bundles type, label and a Zod schema:

import { defineBlock, ekText, ekImage } from "@editkraft/schema";
import { z } from "zod";

export const heroDefinition = defineBlock({
  type: "Hero",
  label: "Hero section",
  schema: z.object({
    headline: ekText({ label: "Headline" }),
    image: ekImage({ label: "Image" }),
  }),
});

#Make it editable: data-ek-field

The Studio edits exclusively inline in the live preview. An element becomes editable by carrying data-ek-field="<fieldName>":

export function Hero({ headline, image }: { headline: string; image: EkImageValue }) {
  return (
    <section>
      <h1 data-ek-field="headline">{headline}</h1>
      <img data-ek-field="image" src={image.url} alt={image.alt ?? ""} />
    </section>
  );
}
  • text/richText become contenteditable (richText gets a formatting toolbar with headings, bold/italic, lists, alignment and links).
  • image opens the media library, link a link popover, select an options popover.

A block without data-ek-field renders fine but can’t be edited — the most common mistake when writing new blocks.

#Registry

The registry connects definitions to components. createRegistry validates that every block type has both:

import { createRegistry } from "@editkraft/react";
import { Hero } from "./Hero";

export const registry = createRegistry([
  { definition: heroDefinition, component: Hero },
]);

#Select fields: ekSelect

ekSelect models a strict choice from fixed values (icon keys, layout variants). In the preview, clicking the element opens an options popover — no free text. Rendering the value stays in the block; always keep a fallback for unknown values.

#Repeating structures

Model card grids or rows as a parent block with slots plus child blocks — not as object arrays. For paragraph lists, a single ekRichText field is usually enough.

#Next steps