This guide creates a small but production-minded foundation for generative UI development. The example stack uses TypeScript, Next.js, React, a schema validator, and Playwright. The same boundaries apply if your preferred frontend is Vue, Svelte, Angular, or a web-components architecture.
1. Install the prerequisites
Install Git, a current supported Node.js LTS release, and an editor such as Visual Studio Code. Verify the tools:
git --version
node --version
npm --versionUse an LTS Node.js line for predictable dependency support. Never commit model-provider credentials to source control.
2. Bootstrap the application
npx create-next-app@latest generative-ui-lab --typescript --eslint --app --src-dir
cd generative-ui-lab
npm install zod
npm install -D @playwright/test
npx playwright installCreate .env.local for secrets and confirm it is excluded by .gitignore.
3. Establish clear boundaries
src/
app/
api/generate-ui/route.ts
page.tsx
components/generative/
registry.tsx
ComparisonTable.tsx
ConfirmationCard.tsx
lib/
ui-schema.ts
policy.ts
telemetry.tsThe API route may call a model, but the browser should receive validated data rather than arbitrary executable code. The component registry is the security and design-system boundary.
4. Define a UI schema
import { z } from "zod";
export const uiNodeSchema = z.discriminatedUnion("component", [
z.object({
component: z.literal("Message"),
props: z.object({ text: z.string().max(4000) })
}),
z.object({
component: z.literal("ConfirmationCard"),
props: z.object({
title: z.string().max(120),
actionId: z.string().regex(/^[a-z0-9-]+$/)
})
})
]);Keep schemas narrow. Limit string length, enumerations, nesting depth, URLs, row counts, and action identifiers. Validation is not only for type safety; it controls cost, layout stability, and attack surface.
5. Render through a registry
const registry = {
Message,
ConfirmationCard
} as const;
export function RenderNode({ node }: { node: UiNode }) {
const Component = registry[node.component];
return <Component {...node.props} />;
}Do not evaluate generated JavaScript. Do not insert untrusted markup with dangerouslySetInnerHTML. Keep navigation targets and external resources on allowlists.
6. Add progressive rendering
Generative experiences benefit from streaming, but partial data must remain valid. Stream complete typed events such as status, node, and error, rather than incomplete JSON fragments. Use an accessible status region for progress messages:
<p role="status" aria-live="polite">
{generationStatus}
</p>7. Add quality gates
npm run lint
npm run build
npx playwright testYour tests should cover schema rejection, keyboard navigation, focus restoration, loading and error states, action confirmation, mobile layouts, and model timeouts. Mock model output in deterministic UI tests.
8. Recommended daily workflow
- Develop against recorded or mocked model responses.
- Validate every response at the server boundary.
- Review generated interfaces using keyboard-only navigation.
- Log schema failures and unknown component requests.
- Run automated tests before enabling a new component in the registry.
Environment checklist
- Supported Node.js LTS and locked dependencies
- Typed UI protocol with runtime validation
- Secrets stored outside the repository
- Finite component and action registries
- Unit, integration, accessibility, and browser tests
- Telemetry without sensitive prompt or user-data leakage
References
Node.js downloads · Next.js installation · W3C status messages