Builder Guide
Professional Template Foundation
Build polished, responsive SaaS pages from registry-backed components, copyable compositions, token-safe themes and server-scoped data bindings.
The Professional Template Foundation is a registry-backed starting point for polished BuildWithHQ applications. It adds nine presentation components, three complete page compositions, and three theme presets without creating a second renderer, a second permission system, or a client-side data layer.
The page compositions work as immediate static previews. Live values must use a registered server-side binding. Existing SaaS databases need the reviewed component-registry release migration before these blocks appear in Puck; builders must never insert registry or theme rows directly.
Download the Builder Developer Kit
Download Professional Foundation Developer Kit 1.0.0 (46.8 KiB). Verify SHA-256 80bed0dc00e3e19ca65e3e7566fffedc5ac35b89d3102e6e582f86731f4b7b51, or download the adjacent checksum file.
A builder working entirely in BuildWithHQ does not need the repository or its maintainer test commands. Install the exact published Template Library version, open its pages in Puck, and customize them. Download the kit when you want standalone styled and unstyled React, canonical page and theme JSON, the public capability catalog, OpenAPI, the server-side TypeScript SDK, or MCP/CLI setup material.
| Surface | Use | Identity boundary |
|---|---|---|
| Builder and Puck | Install, edit, validate and publish template pages. | Short-lived Builder identity and account ownership. |
| Developer API and MCP | Operate on one SaaS app's authorized data and integrations. | App-bound credential with mcp.use plus each tool's scope. |
| Headless React | Paint already-authorized results in a custom interface. | Your server holds the API credential; the browser never does. |
The app-scoped MCP endpoint is https://api.buildwithhq.com/v1/apps/{saasAppId}/mcp. Tool discovery is authoritative, and sensitive mutation tools remain absent unless server policy explicitly enables them.
Copy the MCP connection descriptor
{
"schemaVersion": 1,
"name": "BuildWithHQ",
"transport": "streamable-http",
"urlTemplate": "https://api.buildwithhq.com/v1/apps/{saasAppId}/mcp",
"authentication": {
"type": "bearer",
"credentialSource": {
"kind": "environment",
"name": "BWHQ_API_CREDENTIAL"
}
},
"requiredConfiguration": {
"saasAppIdEnvironment": "BWHQ_SAAS_APP_ID",
"minimumScopes": ["mcp.use"],
"toolScopesAreAdditional": true
},
"security": {
"credentialBoundToExactSaasApp": true,
"routeAppIsAuthorization": false,
"physicalRoutingExposed": false,
"sensitiveMutationsDefaultDenied": true
}
}Copy the CLI profile setup
# Install bwhq 0.1.0 from your approved BuildWithHQ package channel first.
# Secrets stay in your environment or secret manager, not in the profile.
bwhq auth profile add `
--name my-saas `
--saas-app-id <your-saas-app-id> `
--base-url https://api.buildwithhq.com `
--credential-env BWHQ_API_CREDENTIAL `
--control-plane-base-url https://hello.buildwithhq.com `
--builder-token-env BWHQ_BUILDER_TOKEN `
--set-current
bwhq doctor --profile my-saas
bwhq status --profile my-saasThe ZIP contains CLI instructions, not a signed bwhq executable. Publish the CLI through an approved package channel as a separate release. Never ask a builder to compile the repository, and never put BWHQ_API_CREDENTIAL or BWHQ_BUILDER_TOKEN in page JSON, React source, a ZIP, or a prompt.
1. How the foundation works
| Layer | What it owns | What it does not own |
|---|---|---|
| Component Registry | Stable keys, display names, default props, renderer keys and versions. | Tenant identity, data scope or SQL. |
| Canonical page JSON | Stable block IDs, layout, component properties, children and approved binding keys. | Authorization or physical database routing. |
| SaaS app theme | Light/dark/system mode and allowlisted --runtime-* design tokens. | Arbitrary executable CSS, script or permissions. |
| Runtime binding resolver | Maps a registered bindingKey to a reviewed, identity-scoped server operation before JSON is returned. | Ad-hoc SQL or browser-selected tenant/account IDs. |
| Template Library | Owned page harvest, validation, immutable page/component publication and exact-version installation. | Silently changing an already installed tenant copy. |
At runtime the server resolves the signed-in SaaS app, account, user, DataRoles and Locations. SQL returns only the permitted result. React receives that already-scoped result and paints it. A component never downloads a broad table and filters it in the browser.
2. Use the professional component vocabulary
| Component key | Use it for | Main properties |
|---|---|---|
core.page-header | Page title, lead and breadcrumb. | title, lead, breadcrumb[] |
core.stat-tile | One KPI or bounded scalar. | label, value, delta, direction, tone |
core.card | A titled content group. | title, description, tone, child blocks |
core.chart | An accessible bar or line summary. | title, variant, series[], labels[], tone |
core.data-table | A bounded server-scoped list. | caption, columns[], optional static rows[] |
core.list-row | Compact recent activity or person/work summaries. | primary, secondary, trailing, initials, tone |
core.badge | Status and category labels. | label, tone |
core.section-label | A compact section eyebrow. | label |
core.empty-state | An explicit zero-result or missing-proof state. | title, body |
Use the existing core.container with layout: "stack", "row" or "grid" to compose them. The professional stylesheet makes the grid responsive and keeps the content left-aligned inside the expandable center column.
3. Start from a complete page composition
Each source below is canonical BuildWithHQ page content. The dashboard demonstrates KPIs, a chart and activity. The queue demonstrates status, a bounded table and the correct empty state. The record page is a base for Files, Conversations, Checklists, Workflow, Record Activity and other first-class blocks.
Professional dashboard - copy the complete page JSON
{
"blocks": [
{
"_id": "professional-dashboard-header",
"_type": "core.page-header",
"props": {
"title": "Operations overview",
"lead": "A focused view of the work, service level, and team activity that need attention today.",
"breadcrumb": ["Operations", "Overview"]
}
},
{
"_id": "professional-dashboard-stats",
"_type": "core.container",
"props": { "layout": "grid" },
"children": [
{
"_id": "professional-dashboard-stat-open",
"_type": "core.stat-tile",
"props": { "label": "Open work", "value": "128", "delta": "12 this week", "direction": "up", "tone": "primary" }
},
{
"_id": "professional-dashboard-stat-due",
"_type": "core.stat-tile",
"props": { "label": "Due today", "value": "17", "delta": "4 at risk", "direction": "down", "tone": "warning" }
},
{
"_id": "professional-dashboard-stat-sla",
"_type": "core.stat-tile",
"props": { "label": "Within service level", "value": "96%", "delta": "2.4%", "direction": "up", "tone": "success" }
},
{
"_id": "professional-dashboard-stat-waiting",
"_type": "core.stat-tile",
"props": { "label": "Waiting on customer", "value": "23", "tone": "neutral" }
}
]
},
{
"_id": "professional-dashboard-detail-grid",
"_type": "core.container",
"props": { "layout": "grid" },
"children": [
{
"_id": "professional-dashboard-trend-card",
"_type": "core.card",
"props": { "title": "Work completed", "description": "Six-week throughput from the authorized page scope.", "tone": "primary" },
"children": [
{
"_id": "professional-dashboard-trend-chart",
"_type": "core.chart",
"props": {
"title": "Completed work by week",
"variant": "line",
"tone": "primary",
"series": [38, 45, 42, 56, 61, 73],
"labels": ["W1", "W2", "W3", "W4", "W5", "W6"]
}
}
]
},
{
"_id": "professional-dashboard-team-card",
"_type": "core.card",
"props": { "title": "Recent team activity", "description": "The latest visible changes across this workspace.", "tone": "success" },
"children": [
{ "_id": "professional-dashboard-team-label", "_type": "core.section-label", "props": { "label": "Recently updated" } },
{ "_id": "professional-dashboard-team-1", "_type": "core.list-row", "props": { "primary": "Preventive maintenance", "secondary": "North facility · Updated 8 minutes ago", "trailing": "Ready", "initials": "PM", "tone": "success" } },
{ "_id": "professional-dashboard-team-2", "_type": "core.list-row", "props": { "primary": "Customer onboarding", "secondary": "West region · Updated 21 minutes ago", "trailing": "Review", "initials": "CO", "tone": "warning" } },
{ "_id": "professional-dashboard-team-3", "_type": "core.list-row", "props": { "primary": "Quarterly inspection", "secondary": "Central office · Updated 46 minutes ago", "trailing": "Open", "initials": "QI", "tone": "primary" } }
]
}
]
}
]
}Professional work queue - copy the complete page JSON
{
"blocks": [
{
"_id": "professional-queue-header",
"_type": "core.page-header",
"props": {
"title": "Work queue",
"lead": "A bounded, server-scoped table designed for fast triage instead of fetching an entire record graph.",
"breadcrumb": ["Operations", "Work queue"]
}
},
{
"_id": "professional-queue-statuses",
"_type": "core.container",
"props": { "layout": "row" },
"children": [
{ "_id": "professional-queue-status-all", "_type": "core.badge", "props": { "label": "All 128", "tone": "primary" } },
{ "_id": "professional-queue-status-progress", "_type": "core.badge", "props": { "label": "In progress 46", "tone": "success" } },
{ "_id": "professional-queue-status-risk", "_type": "core.badge", "props": { "label": "At risk 4", "tone": "warning" } },
{ "_id": "professional-queue-status-blocked", "_type": "core.badge", "props": { "label": "Blocked 3", "tone": "danger" } }
]
},
{
"_id": "professional-queue-card",
"_type": "core.card",
"props": { "title": "Priority work", "description": "Preview rows; bind this block to an approved list operation for live tenant data.", "tone": "primary" },
"children": [
{
"_id": "professional-queue-table",
"_type": "core.data-table",
"props": {
"caption": "Priority work queue",
"columns": [
{ "key": "reference", "label": "Reference", "align": "left" },
{ "key": "summary", "label": "Summary", "align": "left" },
{ "key": "owner", "label": "Owner", "align": "left" },
{ "key": "due", "label": "Due", "align": "left" },
{ "key": "status", "label": "Status", "align": "right" }
],
"rows": [
{ "reference": "WO-1048", "summary": "Inspect cooling system", "owner": "Morgan Lee", "due": "Today", "status": "In progress" },
{ "reference": "TK-318", "summary": "Confirm access window", "owner": "Avery Patel", "due": "Today", "status": "Waiting" },
{ "reference": "WO-1051", "summary": "Replace safety signage", "owner": "Jordan Kim", "due": "Tomorrow", "status": "Ready" },
{ "reference": "AP-092", "summary": "Approve completion proof", "owner": "Riley Chen", "due": "Tomorrow", "status": "Review" }
]
}
}
]
},
{
"_id": "professional-queue-empty",
"_type": "core.empty-state",
"props": {
"title": "No work matches this view",
"body": "Use this state when an approved query returns zero visible rows; do not substitute an unscoped fallback query."
}
}
]
}Professional record detail - copy the complete page JSON
{
"blocks": [
{
"_id": "professional-record-header",
"_type": "core.page-header",
"props": {
"title": "WO-1048 · Inspect cooling system",
"lead": "A composed record view that can sit beside Files, Conversations, Checklists, Workflow, and Record Activity.",
"breadcrumb": ["Operations", "Work orders", "WO-1048"]
}
},
{
"_id": "professional-record-statuses",
"_type": "core.container",
"props": { "layout": "row" },
"children": [
{ "_id": "professional-record-status", "_type": "core.badge", "props": { "label": "In progress", "tone": "success" } },
{ "_id": "professional-record-priority", "_type": "core.badge", "props": { "label": "High priority", "tone": "warning" } },
{ "_id": "professional-record-location", "_type": "core.badge", "props": { "label": "North facility", "tone": "neutral" } }
]
},
{
"_id": "professional-record-grid",
"_type": "core.container",
"props": { "layout": "grid" },
"children": [
{
"_id": "professional-record-summary",
"_type": "core.card",
"props": { "title": "Work summary", "description": "Use nested blocks to compose an editable page without bypassing the page contract.", "tone": "primary" },
"children": [
{ "_id": "professional-record-summary-label", "_type": "core.section-label", "props": { "label": "Scope" } },
{ "_id": "professional-record-summary-text", "_type": "core.text", "props": { "text": "Inspect the rooftop cooling system, capture proof, and submit the completed checklist for approval.", "variant": "body", "align": "left" } },
{ "_id": "professional-record-owner", "_type": "core.list-row", "props": { "primary": "Morgan Lee", "secondary": "Assigned technician", "trailing": "Today", "initials": "ML", "tone": "primary" } }
]
},
{
"_id": "professional-record-proof",
"_type": "core.card",
"props": { "title": "Completion proof", "description": "Files and photos remain secured native records; the presentation block only explains the state.", "tone": "success" },
"children": [
{ "_id": "professional-record-proof-empty", "_type": "core.empty-state", "props": { "title": "No proof attached yet", "body": "Attach photos or documents to checklist items before submitting this work for approval." } }
]
}
]
}
]
}Keep every _id stable while editing. Template upgrades use block identity to compare and merge changes. Give a copied block a new unique ID instead of duplicating an existing one.
4. Edit the pages in Puck
- Open an account-owned workshop SaaS and create or open a page in Design mode.
- Drag a component from BuildWithHQ components, or load one of the complete page sources above through the supported page authoring flow.
- Select the block and edit Component properties (JSON). The field must contain a JSON object; malformed JSON, arrays and scalar values are rejected.
- Preview desktop and mobile layouts, save a draft, then publish through the normal page validator.
For example, this is the complete properties object for a stat tile:
{
"label": "Open work",
"value": "128",
"delta": "12 this week",
"direction": "up",
"tone": "primary"
}
The editor round-trips these properties into block.props. It preserves canonical block identity, parent/child structure, responsive settings, event bindings, security metadata, bindingKey and dataKey.
5. Choose a token-safe theme
The presets use the existing SaaS app theme contract: themeJson selects light, dark or system; cssVariables contains allowlisted runtime tokens; and designTokensJson sets base spacing and radius. The runtime parses the document fail-closed, stamps the document element, and applies the tokens to painted components.
BuildWithHQ Indigo Light - copy the complete theme JSON
{
"schemaVersion": 1,
"themeKey": "buildwithhq-indigo-light",
"themeJson": { "schemaVersion": 1, "name": "BuildWithHQ Indigo Light", "mode": "light" },
"cssVariables": {
"--runtime-primary": "#4b45e8",
"--runtime-primary-strong": "#302b8e",
"--runtime-primary-tint": "rgba(75,69,232,.13)",
"--runtime-page": "#f4f6fb",
"--runtime-surface": "#ffffff",
"--runtime-surface-raised": "#f8f9fc",
"--runtime-ink": "#17202f",
"--runtime-ink-muted": "#667085",
"--runtime-line": "#dfe3ed"
},
"designTokensJson": { "spacingBase": 8, "radiusBase": 8 }
}Executive Slate Dark - copy the complete theme JSON
{
"schemaVersion": 1,
"themeKey": "executive-slate-dark",
"themeJson": { "schemaVersion": 1, "name": "Executive Slate Dark", "mode": "dark" },
"cssVariables": {
"--runtime-primary": "#9ea8ff",
"--runtime-primary-strong": "#c7ccff",
"--runtime-primary-tint": "rgba(158,168,255,.18)",
"--runtime-page": "#0e1420",
"--runtime-surface": "#171e2c",
"--runtime-surface-raised": "#20293a",
"--runtime-ink": "#edf1f7",
"--runtime-ink-muted": "#a8b2c4",
"--runtime-line": "#303b50"
},
"designTokensJson": { "spacingBase": 8, "radiusBase": 10 }
}Service Emerald Light - copy the complete theme JSON
{
"schemaVersion": 1,
"themeKey": "service-emerald-light",
"themeJson": { "schemaVersion": 1, "name": "Service Emerald Light", "mode": "light" },
"cssVariables": {
"--runtime-primary": "#16705a",
"--runtime-primary-strong": "#0d4f40",
"--runtime-primary-tint": "rgba(22,112,90,.14)",
"--runtime-page": "#f2f7f5",
"--runtime-surface": "#ffffff",
"--runtime-surface-raised": "#f6faf8",
"--runtime-ink": "#172521",
"--runtime-ink-muted": "#61716b",
"--runtime-line": "#d7e5df"
},
"designTokensJson": { "spacingBase": 8, "radiusBase": 8 }
}Do not paste theme JSON into a tenant table or add an arbitrary CSS/script field. Theme writes must go through the supported version-fenced builder contract. Template-pack artifact schema v2 pins pages and effective components, not the optional legacy live ThemeKey lookup; these presets are instead versioned by the developer-kit package lock and ZIP SHA-256. The public builder theme switcher is still a separate delivery step.
6. Replace preview values with live, scoped data
Static values are useful while designing. For live data, register a reviewed binding and put only its stable key on the block. dataKey may select a bounded property from the returned JSON; it is not a query language.
{
"_id": "visible-work-table",
"_type": "core.data-table",
"bindingKey": "work-orders.current-page",
"dataKey": "items",
"props": {
"caption": "Visible work orders",
"columns": [
{ "key": "reference", "label": "Reference", "align": "left" },
{ "key": "summary", "label": "Summary", "align": "left" },
{ "key": "status", "label": "Status", "align": "right" }
]
}
}
The approved server operation may return shapes like these:
{
"stat": { "value": 128, "delta": "12 this week", "direction": "up" },
"chart": {
"title": "Completed work",
"series": [38, 45, 42, 56, 61, 73],
"labels": ["W1", "W2", "W3", "W4", "W5", "W6"]
},
"table": {
"items": [
{ "reference": "WO-1048", "summary": "Inspect cooling system", "status": "In progress" }
]
}
}
- A stat accepts a scalar or an object with
value,deltaanddirection. - A chart accepts finite numeric
seriesplus short labels. - A table accepts an array or an object containing
items; columns explicitly select display fields. - A failed bound table never falls back to its static preview rows. It shows a loading, error or no-visible-data state.
Page JSON must never contain a database name, connection string, SaaSAppId, AppAccountId, UserId, DataRoleId or LocationId as authorization evidence. The server derives identity and scope from the verified session.
7. Turn the workshop pages into a reusable template
- Customize the pages in an account-owned workshop SaaS.
- Save the selected theme through the supported version-fenced authoring contract when that builder surface is available.
- Harvest each owned Page with
POST /api/apps/{saasAppId}/template-harvests. - Attach the harvested artifacts to a draft pack and run the existing pack validator.
- Publish an immutable Template Library version.
- Install that exact version into a different disposable SaaS and run real auth, permission, binding, layout and failure tests before offering it broadly.
Copy the source-kit manifest
{
"schemaVersion": 1,
"templateKey": "professional-foundation",
"templateName": "BuildWithHQ Professional Foundation",
"version": "1.0.0",
"description": "Clean-room professional page compositions and theme presets built from the canonical BuildWithHQ component registry.",
"delivery": "authoring-source-for-exact-version-template-pack",
"installRequiresExactVersion": true,
"runtimeDataAccess": "approved-server-scoped-bindings-only",
"publicDistribution": {
"kind": "builder-developer-kit",
"apiBaseUrl": "https://api.buildwithhq.com",
"mcpEndpointTemplate": "https://api.buildwithhq.com/v1/apps/{saasAppId}/mcp",
"archiveName": "BuildWithHQ-Professional-Foundation-Developer-Kit-1.0.0.zip"
},
"requiredComponents": [
"core.badge",
"core.card",
"core.chart",
"core.container",
"core.data-table",
"core.empty-state",
"core.list-row",
"core.page-header",
"core.section-label",
"core.stat-tile",
"core.text"
],
"pages": [
{
"pageKey": "ProfessionalDashboard",
"name": "Professional dashboard",
"path": "pages/professional-dashboard.json"
},
{
"pageKey": "ProfessionalQueue",
"name": "Professional work queue",
"path": "pages/professional-queue.json"
},
{
"pageKey": "ProfessionalRecord",
"name": "Professional record detail",
"path": "pages/professional-record.json"
}
],
"themes": [
{
"themeKey": "buildwithhq-indigo-light",
"name": "BuildWithHQ Indigo Light",
"path": "themes/buildwithhq-indigo-light.json"
},
{
"themeKey": "executive-slate-dark",
"name": "Executive Slate Dark",
"path": "themes/executive-slate-dark.json"
},
{
"themeKey": "service-emerald-light",
"name": "Service Emerald Light",
"path": "themes/service-emerald-light.json"
}
],
"publication": {
"harvestEndpoint": "POST /api/apps/{saasAppId}/template-harvests",
"artifactTypes": ["Page"],
"ownership": "verified-account-owner",
"destination": "BuildWithHQ_TemplateLibrary",
"snapshot": "published-immutable-exact-version",
"exactVersionContents": ["PackSettings", "Page", "Component"],
"themePresetDelivery": "checksummed-developer-kit-and-version-fenced-builder-theme-write"
},
"security": {
"identitySource": "verified-server-session",
"pageJsonCarriesAuthority": false,
"dataBindings": "tenant+datarole+location scoped before serialization"
}
}Template installation is snapshot isolation. A later upstream page or component edit can become a new pack version, but it must not silently mutate a tenant that installed an earlier one. Apply a preset theme separately through the version-fenced theme write; do not claim a live theme catalog lookup is part of the immutable pack.
8. Know the fail-closed limits and verify your template
- Tables render at most 500 rows and 24 declared columns. Keep pagination and filtering on the server.
- Charts render at most 60 finite points. Use a summarized binding rather than sending raw event history.
dataKeyis at most eight safe path segments. Missing or invalid paths return no selected data.- Text, labels, breadcrumbs and cell values are length-bounded and rendered as React text, not executable HTML.
- No CDN, arbitrary runtime script, direct SQL connection or physical tenant database selector is introduced.
Repository maintainers run the source, renderer, editor and catalog checks below before releasing a new foundation version. Builders installing an approved exact version do not need these commands:
python -m pytest templates\professional-foundation\tests -q
npm --prefix apps\tenant-runtime test -- src\ProfessionalPrimitives.test.tsx src\PageRenderer.test.tsx src\PuckPageDesignerAdapter.test.ts
npm --prefix apps\tenant-runtime run build
# Regenerate the implementation catalog while retaining runtime observations.
python catalog-generator\generate_catalog.py --observations artifacts\catalog-observations\tenant-runtime-2026-09-03.jsonl
python catalog-generator\generate_catalog.py --check
Then run an authenticated browser journey through the real auth, SQL, API and runtime paths. Check the selected theme, all expected blocks, empty/loading/error states, mobile overflow, keyboard access, cross-tenant rejection and exact result cardinality.
Continue with the complete React component catalog, page authoring guide, and template versioning guide.