Skip to main content

Block components

Block components are React components that receive BlockProps from the renderer and render a single block. The same components are used for preview and inside the editor canvas (the editor wraps them with chrome like edit/remove).

BlockProps​

From @genetik/renderer-react, each component receives:

  • config — the block's config object (typed per block)
  • slots — rendered slot content, e.g. slots.children (React nodes)
  • Optional slot/block metadata as needed

Example components​

Text: render config.content.

import type { BlockProps } from "@genetik/renderer-react";

export function TextBlock({ config }: BlockProps) {
const content = (config as { content?: string }).content ?? "";
return <span>{content}</span>;
}

Card: title + a children slot.

export function CardBlock({ config, slots }: BlockProps) {
const title = (config as { title?: string }).title ?? "";
return (
<div className="playground-card">
{title ? <div className="playground-card__title">{title}</div> : null}
<div className="playground-card__children">{slots.children ?? []}</div>
</div>
);
}

Row / column: layout only; pass through slots.children.

export function RowBlock({ config, slots }: BlockProps) {
const gap = (config as { gap?: string }).gap ?? "normal";
const gapClass = gap === "tight" ? "gap-2" : gap === "wide" ? "gap-6" : "gap-4";
return <div className={`flex flex-row ${gapClass}`}>{slots.children ?? []}</div>;
}

export function ColumnBlock({ config, slots }: BlockProps) {
return (
<div className="flex flex-col gap-2 flex-1 min-w-0 playground-card__children min-h-6">
{slots.children ?? []}
</div>
);
}

Button (context-aware): use usePageRuntime() and getContextValue / updateContext from @genetik/context-events to read and toggle a boolean in page context.

import { usePageRuntime } from "@genetik/renderer-react";
import { getContextValue } from "@genetik/context-events";

export function ButtonBlock({ config }: BlockProps) {
const runtime = usePageRuntime();
const { contextPath = "customContextBoolean", label = "Toggle" } = (config ?? {}) as { contextPath?: string; label?: string };
const value = getContextValue(runtime?.context, contextPath) as boolean | undefined;
const next = !(value === true);
return (
<button type="button" onClick={() => runtime?.updateContext(contextPath, next)}>
{label}: {String(value ?? false)} → {String(next)}
</button>
);
}

Component map​

Build a map from block id to component and pass it to both the editor and the renderer:

const componentMap: ComponentMap = {
page: PageBlock,
text: TextBlock,
card: CardBlock,
row: RowBlock,
column: ColumnBlock,
image: ImageBlock,
button: ButtonBlock,
};

Next: Wire up the editor with EditorProvider, BlockPalette, and EditorCanvas.