API
The Pro SDK ships as chaipro with purpose-specific entry points. The rule
that keeps imports correct: client APIs come from the root and /registry; server APIs
come from /nextjs/server, /nextjs/render, /payload, and /db/*. Importing the root
package in a server context throws by design.
Entry points
| Entry point | Side | Purpose |
|---|---|---|
chaipro |
client | Builder component + UI extension APIs (panels, slots, hooks, flags) |
chaipro/registry |
client | Block registration - registerChaiBlock, props schemas, fonts |
chaipro/web-blocks |
client | loadWebBlocks() - the built-in block library |
chaipro/types |
shared | All public types |
chaipro/utils |
shared | HTML-to-blocks conversion, design token helpers |
chaipro/nextjs |
server | withChaiBuilder next.config wrapper |
chaipro/nextjs/server |
server | Config, context, actions, permissions |
chaipro/nextjs/render |
server | RenderChaiBlocks, ChaiPageCSS, styles utilities |
chaipro/nextjs/render-client |
client | PreviewBanner, ChaiAnimationProvider |
chaipro/db/* |
server | Database adapter factories (postgres, node-pg, libsql, d1, better-sqlite3) |
chaipro/payload |
server | Payload integration - plugin, schema hook, createPayloadChaiBuilder |
chaipro/plugins/<name>/server and /client |
per plugin | Feature plugins (redirects, revisions, trash, roles, media, ...) |
chaipro/ui/* |
client | shadcn-based UI primitives for custom panels |
Client: extending the builder
All registration APIs are client-only and must run at module level before the builder mounts - centralize them in one setup file. The main families:
| API | What it does |
|---|---|
registerChaiBlock(component, config) |
add a custom block (from /registry) |
registerChaiBlockProps(schema) |
define a block's editable props schema |
registerChaiSidebarPanel(id, options) |
add a sidebar panel |
registerChaiSlot(CHAI_SLOT_IDS.X, Component) |
inject UI into a named slot |
registerChaiHook(CHAI_HOOKS.X, fn) |
intercept lifecycle events (before/after save) |
registerChaiFeatureFlag(key, options) |
user-toggleable flags |
registerChaiLibrary(id, options) |
add a browsable block library |
registerChaiAddBlockTab(id, options) |
add a tab to the Add Block drawer |
Hooks for reading builder state from custom UI include useChaiAuth,
useUserPermissions, useActivePage, useSavePage, useChaiFeatureFlag, and
useWebsitePages. Full walkthroughs: Extending the UI
and Custom Blocks.
Server: the request-scoped instance
getChaiBuilder (exported from your chaibuilder.server.ts, see
Configuration) returns a request-scoped instance with all
server APIs bound to the caller's context:
const cb = await getChaiBuilder(props) // pages/layouts
const cb = await getChaiBuilder(props, request) // route handlers
| Method | Returns |
|---|---|
cb.getPagePayload(slug) |
full render payload - { page, pageData, settings, pageProps } |
cb.generateMetaData(...) |
a Next.js Metadata object for the page |
cb.getPageData(slug) |
resolved data-provider output for a page |
cb.getSiteSettings() |
site-wide settings |
cb.getSiteGlobalData() |
output of your globalDataProvider |
cb.getPages() |
all pages |
cb.handleHttpAction(body) |
dispatch a builder action from a route handler |
The instance is request-scoped - never cache it or share it across requests.
The action endpoint
All client-to-server builder communication goes through one route, which the starter already includes:
// app/(builder)/api/chai/route.ts
export async function POST(request: NextRequest, props: ChaiBuilderRouteProps) {
const cb = await getChaiBuilder(props, request)
return cb.handleHttpAction(await request.json())
}
The body is { action: "ACTION_NAME", data: {...} }. Authentication comes from the Payload
auth cookie (builder UI) or an Authorization: Bearer header (programmatic calls), and
each action enforces its required permission before executing. After an action runs, any
cache tags and paths it reports are revalidated automatically - see
Caching & Revalidation.
Custom actions
Add your own actions by extending ChaiBaseAction:
import { ChaiBaseAction } from 'chaipro/nextjs/server'
import { z } from 'zod'
export class GetInventoryAction extends ChaiBaseAction {
protected getValidationSchema() {
return z.object({ productId: z.string() })
}
async execute(data: { productId: string }) {
const { appId, userId } = this.context! // set before execute()
return fetchInventory(appId, data.productId)
}
}
Register it in chaibuilder.config.ts under actions: { GET_INVENTORY: new GetInventoryAction() }, then call it from the builder UI through the action endpoint, or
server-side with dispatchChaiAction("GET_INVENTORY", data) (throws on error) or
tryChaiAction(...) (returns { ok, data | error }).
Rendering
Server components for rendering published pages, from /nextjs/render:
RenderChaiBlocks- renders a page payload; accepts optionallinkComponent,imageComponent,buttonComponentoverrides.ChaiPageCSS- page CSS (Tailwind output, theme variables, fonts); must be in<head>.ChaiPageJSONLD- structured data for SEO.getStylesForBlocks,getChaiThemeCssVariables- lower-level utilities for custom render pipelines.
Permissions
The permission catalog is CHAI_PERMISSIONS (core keys) plus keys registered by active
plugins - getPermissionCatalog() returns the runtime set. Check access in custom code
with hasPermission, and resolve a user's effective set with resolvePermissions.
Effective permissions are the user's grants intersected with any credential-level
delegatedPermissions ceiling. See
Roles & Permissions.
Related
- Configuration - the config files these APIs plug into.
- Custom Blocks - the block registration API in depth.
- Extending the UI - panels, slots, and hooks in depth.

