Forkshop has four user-facing primitives. Most of the API surface is either an instance of one of these or a function over them.
Node
A Node is a positioned thing on the canvas. It carries an id,
a kind, an x/y/width/height, and an optional label. Three
kinds ship in the engine:
inline-react— renders a React element directly in the host context. No iframe.iframe-route— renders a route of your app inside an iframe.routePath: "/pricing"becomes<iframe src="/pricing">.iframe-component— renders a single component preview inside an iframe via a generated preview route.
Nodes are plain data. Boards return them; the engine positions and renders them.
NodeType
A NodeType is a plugin object that tells the engine how to render a kind of Node. It satisfies this interface:
type NodeType<T extends AnyNode> = {
id: string
match: (node: AnyNode) => node is T
render: (props: RenderProps<T>) => ReactNode
agentMatch?: (node: T, activity: AgentActivitySnapshot) => AgentMatchResult
}
match is a TypeScript type guard that decides whether a NodeType
handles a given Node. render returns the React tree for it.
agentMatch is optional — it tells the engine whether agent
activity on a file should highlight this Node.
There is no defineNodeType() factory. A NodeType is just an
object satisfying the interface. The engine ships three built-ins
(inlineReactNodeType, iframeRouteNodeType,
iframeComponentNodeType) collected in BUILTIN_NODE_TYPES.
Layout
A Layout arranges Nodes on the canvas. The engine ships two:
Gallery— flat grid or stack. Auto-flows entries when they omitrow/column; honors explicit coordinates when set.Tree— hierarchical view derived from URL paths.
Boards reference a built-in by string id (layout: "gallery" or
layout: "tree") inside defineBoard(). Custom Layouts are
authored with defineLayout() and registered in
forkshopConfig.layouts; a Board then references the Layout
object directly (layout: chartsLayout).
Layouts decide where Nodes sit. NodeTypes decide what each tile renders. The two are independent.
Board
A Board is one entry in your sidebar. Create one with
defineBoard() — a typed config that becomes a React component the
engine mounts inside <BoardRegistry>. The engine owns canvas,
sidebar, selection routing, and positions wiring.
import { defineBoard, forkshopIcons } from "@forkshop/engine"
export default defineBoard({
id: "charts",
label: "Charts",
icon: forkshopIcons.flows,
match: (s) => s.kind === "section" && s.sectionId === "charts",
layout: "gallery",
useEntries: () => [/* … */],
})
Boards live in app/forkshop/ in your project. Drop them into the
boards prop on <BoardRegistry>; no switch statement, no canvas
wiring. See Boards for the full anatomy.