Skip to content

docs(ui): kind:'react' / kind:'html' page authoring has no guide, and the Page Properties table omits kind and source entirely #10210

Description

@os-elon

Part of #10206

The gap

The react tier is real, shipped, and gated:

  • packages/spec/src/ui/react-blocks.ts — the curated block index (ADR-0081), with the overlay-shadow ledger and the record:* exclusion ledger;
  • @object-ui/react-runtime in the objectui repo — Sucrase transpile + eval, the trusted execution tier;
  • os validate step 3d (validateReactPageProps) parses each react page's real JSX and checks block usage against the generated contract;
  • docs/audits/2026-06-react-tier-authoring-dogfood.md proves the authoring loop end-to-end on examples/app-showcase/src/pages/renewals-pipeline.page.ts.

On the docs site, a developer can find only:

wherewhat
references/ui/page.mdx L87 / L89two generated schema rows describing kind and source
ui/pages.mdx L147one bullet, phrased negatively: record:* blocks are rejected on a react page
deployment/validating-metadata.mdx §10b and neighbourshow it fails validation

And critically, the Page Properties table in content/docs/ui/pages.mdx lists neither kind nor source (it stops at name/label/description/type/object/template/regions/variables/isDefault/assignedProfiles). A developer reading the Page guide end to end concludes that regions + components is the only way to author a page. kind:'html' — the constrained, parsed-never-executed tier served by @objectstack/sdui-parser — is equally invisible.

The material exists, in two places a customer does not read: objectui/content/docs/guide/react-pages.md (a different docs site; @object-ui/react-runtime's README links it by a relative path that does not resolve from this repo) and skills/objectstack-ui/references/react-blocks.md (generated, AI-facing).

File surface

  • content/docs/ui/pages.mdx — edit
  • content/docs/ui/react-pages.mdx — new
  • content/docs/ui/meta.json — one added entry

⛔ Do not touch any other file under content/docs/ui/ — sibling cards in this epic own reports.mdx, theming.mdx and doc-pages.mdx, and this card holds the serial position on ui/meta.json.

What to write

1. ui/pages.mdx

  • Add kind and source rows to the Page Properties table. Take the wording from the schema, not from this issue: read PageSchema in packages/spec/src/ui/page.zod.ts and mirror what references/ui/page.mdx L87/L89 already publishes.
  • Add an Authoring modes section placed before ## Regions, contrasting the three tiers in one table — full / slotted (structured authoring, the existing content), html (constrained JSX/HTML + Tailwind, parsed and compiled at save time, never executed — the tier for untrusted authors; jsx is the deprecated alias), react (real React executed at render, the trusted tier, gated by a host capability that defaults ON and is disabled server-side with OS_PAGE_REACT=off).
  • Say plainly that source is authoritative over regions when both are present.
  • Cross-link the new page and deployment/validating-metadata.mdx.

2. ui/react-pages.mdx (new)

objectui/content/docs/guide/react-pages.md is the starting draft — but port it, do not copy it. Every claim must be re-verified against this repo's sources (packages/spec/src/ui/react-blocks.ts, page.zod.ts, the os validate implementation) and every cross-link must resolve inside this site. Cover:

  • Choosing between react and html — the security boundary stated once and plainly: react source is new Function(...)-evaluated with full access to the page's React tree, no sandbox, first-party trust only; html is parsed into a schema tree and never executed.
  • The security gate — the host capability and the OS_PAGE_REACT=off server-side kill switch.
  • What is in scope — how the runtime builds the injected scope, and that blocks are referenced by their PascalCase tag. Link the generated contract (skills/objectstack-ui/references/react-blocks.md) as the authority for per-block props rather than restating prop tables that would drift.
  • Blocks take flat props, and the <Block> escape hatch.
  • Live dataconst adapter = useAdapter(); adapter.find/findOne/create/update.
  • Styling (ADR-0065) — a page's source is runtime metadata, so the console's build-time Tailwind never scans it and utility class names silently produce no CSS. Inline style={{}} with hsl(var(--token)) theme colors; use <ObjectForm formType='drawer'|'modal'> for overlays instead of hand-rolling a backdrop. This is the single highest-value paragraph on the page — an author who misses it produces a page that renders unstyled with no error.
  • Accepted source shapes and what happens when something throws.
  • Page state — the parent record on a react page is ordinary React state.
  • The record:* exclusion — why the record-context family is not in the react tier (they read a shared record context a type:'record' page mounts once; a react page mounts none), what os validate reports (react-block-needs-record-context), and what to write instead. REACT_RECORD_BLOCK_ALTERNATIVES in react-blocks.ts is the authority for the alternatives.
  • How you check your workos validate step 3d, with the real error shapes (missing required prop, did-you-mean on a typo'd callback) taken from docs/audits/2026-06-react-tier-authoring-dogfood.md.

3. ui/meta.json

Add react-pages immediately after pages.

⛔ Non-goals

  • Do not restate per-block prop tables in prose. They are generated from REACT_BLOCKS by packages/spec/scripts/build-react-blocks-contract.ts precisely so they cannot drift; a hand-copied table in content/docs/** is a new drift surface and will be rejected in review.
  • Do not touch packages/spec/**, skills/**, or the objectui repo.
  • If porting turns up a claim in the objectui guide that this repo's sources contradict, report it — do not silently write the version you believe. That is a finding worth filing, not a paragraph to smooth over.

Gates (derived at dispatch time via node scripts/pm/dispatch-gates.mjs, not recalled)

check:doc-anchors, check:docs-audit-scope, check:docs-redirects, check:published-readme-links, check:role-word, check:cross-package-test-inputs, and the spec liveness set (check:empty-state, check:liveness, check:strictness-ledger, check:variant-docs). Re-derive against your actual diff. Note that content/docs/** carries an {/* os:check */} convention for verified code samples — follow what ui/pages.mdx already does for its regions example.

Acceptance

  • A developer who opens /docs/ui/pages learns that three authoring modes exist and can reach the react guide in one click.
  • The Page Properties table is complete against PageSchema.
  • Every code sample on the new page is valid against the real contract, and the styling rule is stated where an author will hit it.
  • Named gates green.

Metadata

Metadata

Assignees

Labels

Type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions