Owned primitives

Add ready-made features, such as comments, as code you own and can edit.

Content2 min read

You want comments on your blog. You could write the collection, the functions to post and moderate, and the access rules yourself, or you could start from one that already works.

Graft ships ready-made features like this as source code you copy into your project, the way shadcn/ui ships components. We call them primitives. There is no plugin system and no package to keep updated: once added, the code is yours to read and change.

See what is available

graft add
PrimitiveWhat you getNeeds
commentsA comments collection, plus functions to post, list, moderate, and deletePostgres
commerceProducts as content, orders as runtime data, plus functions to place, list, update, and cancel ordersPostgres
scoped-accessNamed permission scopes and helpers for access rulesEither engine
seoSEO fields to add to any collectionEither engine
faqStructured FAQ fields and a block to render themEither engine
calloutA callout content block and its MDX componentEither engine

Agents can browse the same list with the list_registry and describe_item MCP tools.

Add one

Preview what it will create, then add it:

graft add comments --dry-run   # show the files, change nothing
graft add comments

This copies the primitive's TypeScript into your graft/ folder, along with anything it depends on, such as scoped-access. It also updates graft/index.ts, a file that re-exports everything in graft/.

Connect it to your config

Your config combines your own collections with everything in graft/, using mergePrimitives. Projects created with graft init already do this:

graft.config.ts
import { mergePrimitives } from "@usegraft/core";
import * as primitives from "./graft";

export const { collections, functions } = mergePrimitives([
  { collections: { pages }, functions: {} },
  primitives,
]);

Run graft compile, and the new collection and functions are live. If a primitive's name clashes with one of yours, the config stops loading with CONFIG_INVALID instead of silently replacing either.

Change it

The files in graft/ are yours. Rename fields, change the moderation rules, or delete functions you do not need. Graft does not overwrite them: running graft add again refuses to replace a file you changed, unless you pass --overwrite. If you need to undo your changes, Git has the original.

Where primitives come from

Primitives ship inside the @usegraft/registry package, versioned with the rest of Graft and tested with it. Each one is a small manifest: a content block, a set of fields, access rules, or a bundle of those. Third-party registries are not supported yet. The format is designed so they can be added later.

Next steps