Skip to main content

Generative UI Architecture

Why this chapter matters

Generative UI is not "the model writes React." That is the wrong mental model for production systems. A serious GenUI architecture lets an AI system propose an interface within a controlled vocabulary, while the application remains responsible for rendering, safety, accessibility, analytics, localization, authorization, and state.

The value is real: a user with a vague goal may need a table, chart, form, comparison view, calendar, approval panel, or document preview. Static UI forces every path to be predesigned. Generative UI lets the product compose the right interaction from approved parts.

Core mental model

Generative UI is component selection plus structured state, not arbitrary code generation.

The safest architecture has four parts:

PartResponsibility
Component registryDefines which UI components the agent is allowed to request
UI schemaDescribes component type, props, data bindings, events, and constraints
RendererValidates the schema and maps it to real app components
Interaction loopSends user actions and state changes back to the agent/workflow

The model never owns the browser. It proposes a typed interface. The client validates and renders it.

Outcome-oriented design boundary

NN/g's GenUI framing is useful because it shifts the design question from "which fixed screen do we need?" to "which outcome is the user trying to reach?" That does not mean the interface should mutate without limits. A production GenUI system should adapt the work surface while keeping stable product identity, navigation, component behavior, and recovery controls.

Use this boundary:

AdaptiveStable
Which approved component is most useful for this taskApplication shell, navigation, auth, permissions
Which data fields or sources are relevantDesign-system behavior and accessibility semantics
Which next actions should be suggestedAudit, approval, and irreversible action rules
Which layout best explains the resultComponent registry, schemas, and analytics naming
Which clarification is neededUser control, editability, undo, and fallback

If generation makes the product feel unfamiliar every time, it has crossed from helpful adaptation into instability.

Component registry design

A useful registry is more than a list of components. It is a contract.

Registry fieldWhy it matters
typeStable component identifier, independent of implementation
versionAllows migration when props or behavior change
propsSchemaDefines allowed props, enum values, bounds, and required fields
dataContractDefines what data the component needs and where it comes from
eventsDefines actions the component can emit back to the agent
permissionsDefines whether the component can show sensitive data or trigger actions
a11yContractDefines labels, focus behavior, keyboard behavior, and announcements
fallbackDefines what to render if validation fails or data is missing

Example component categories:

CategoryExamplesArchitectural notes
Displaysummary, table, card list, chart, timelineMust support citations/provenance when content is AI-derived
Inputform, filter, picker, upload, date rangeMust validate locally and server-side
Decisionapproval panel, diff review, risk confirmationMust preserve audit trail and explicit consent
Navigationnext-step list, related entities, drilldownMust not leak unauthorized routes or objects
Feedbackprogress log, status banner, generated draftMust distinguish model output from verified facts

UI schema pattern

A schema should express intent, not implementation details:

{
"surface": "case_review",
"components": [
{
"id": "risk-summary",
"type": "summary_panel",
"version": "1",
"props": {
"title": "Renewal risk",
"severity": "high",
"body": "The customer has three unresolved support tickets and declining usage."
},
"data": {
"citations": ["ticket:1842", "usage:customer-77"]
}
},
{
"id": "approve-email",
"type": "approval_action",
"version": "2",
"props": {
"label": "Send retention email",
"requiresConfirmation": true
},
"events": {
"confirm": "workflow.send_retention_email"
}
}
]
}

This is intentionally not JSX. The app can validate it, log it, test it, replay it, migrate it, and reject it.

Streaming architecture

GenUI should support incremental rendering, because the useful interface may emerge before the task is complete.

EventFrontend behavior
task.startedShow task shell, cancellation, and clear status
retrieval.startedShow context-gathering state
component.proposedValidate and mount placeholder or component
component.updatedPatch approved props or data bindings
tool.startedShow named tool progress, not raw hidden work
approval.requestedFreeze risky action until user confirms
task.failedPreserve user input and last safe generated UI
task.completedCommit final UI state and expose next actions

The UI should never wait for a giant final JSON blob if partial progress improves trust.

State ownership

GenUI state has multiple owners:

StateOwnerRule
Form inputUser/clientModel may suggest defaults, not silently overwrite typed input
Workflow statusAgent runtimeFrontend renders progress and recovery states
Business recordsBackend servicesAgent must call authorized APIs, not invent state
Generated explanationModelMust be marked as generated and grounded where possible
UI layoutRendererModel proposes; renderer validates and enforces design system

The dangerous architecture is one where the agent owns every state. The useful architecture is one where the agent proposes and orchestrates inside explicit ownership boundaries.

Accessibility and design-system requirements

Generated UI must not bypass inclusive component contracts. Every registry component should carry built-in accessibility behavior:

  • labels and descriptions are required for inputs
  • dialogs trap focus and restore it on close
  • dynamic updates use appropriate live regions
  • tables expose headers and captions when needed
  • charts provide text alternatives or data table views
  • destructive actions require clear confirmation
  • generated controls follow the same keyboard behavior as deterministic controls

If a component cannot satisfy this contract, it should not be available to the agent.

Security controls

Do not render model output as HTML. Avoid arbitrary CSS class injection. Validate URLs, route ids, object ids, enum values, limits, and event names. Treat generated payloads as untrusted external input.

Recommended controls:

  • schema validation at the server and client boundary
  • allowlisted components and events
  • prop-level sanitization
  • permission checks before data binding
  • output size limits
  • audit logs for generated actions
  • human approval for irreversible or expensive operations
  • CSP and Trusted Types for any rich text rendering path

Registry lifecycle and governance

A component registry should evolve like an API. If it evolves like a random list of exported React components, every model prompt becomes coupled to implementation detail.

Use a registry lifecycle:

  1. Candidate: a product team proposes a component for AI composition.
  2. Design-system review: interaction, visual style, keyboard behavior, responsive behavior, and empty states are approved.
  3. Security review: allowed props, URL rules, markdown/rich text rules, data sensitivity, and event permissions are defined.
  4. Schema publication: the component gets a stable name, version, prop schema, examples, and invalid examples.
  5. Eval inclusion: tests verify the model can request the component correctly and the renderer rejects malformed payloads.
  6. Observability: impressions, validation failures, user edits, rejects, and actions are tracked.
  7. Deprecation: old versions continue rendering or degrade with a clear fallback until captured workflows expire.

Registry review should be lightweight but real. A generated component is a production surface, not an internal implementation detail.

Schema versioning strategy

Generated UI payloads may live longer than a deploy. Users may reopen workflows from yesterday, mobile apps may lag behind web deployments, and traces may need replay during incidents.

Versioning rules:

ChangeVersion action
Add optional prop with safe defaultMinor version or same version if renderer tolerates absence
Rename propNew version and migration adapter
Change event meaningNew version, because user actions may differ
Tighten enum valuesNew version or compatibility layer for old payloads
Remove componentKeep fallback renderer until old workflows expire
Change accessibility behaviorReview as behavior change, not cosmetic change

Example compatibility adapter:

type ComponentPayload = {
type: string;
version: string;
props: unknown;
};

function normalizeGeneratedComponent(payload: ComponentPayload) {
if (payload.type === "approval_action" && payload.version === "1") {
return {
...payload,
version: "2",
props: {
...payload.props,
requiresConfirmation: true,
},
};
}

return payload;
}

Do not rely on the model to keep old and new versions straight. Version handling belongs in deterministic code.

Prompt-to-component contract

The prompt should not merely say "generate a good UI." It should teach the model the available UI vocabulary and constraints.

Include:

  • list of allowed components and when to use each
  • prop schema summaries and enum values
  • examples of valid payloads
  • examples of invalid payloads
  • instruction to prefer simpler surfaces when uncertain
  • instruction to ask clarifying questions when required data is missing
  • rules for citations and source binding
  • rules for risky actions and approval surfaces

Bad prompt:

Create the best UI for this task.

Better prompt:

You may propose only these components: summary_panel, source_list,
editable_draft, approval_action, comparison_table. Prefer summary_panel
plus source_list for factual answers. Use approval_action only for
external messages or record updates. Every factual claim must include
source ids. If required data is missing, propose a clarification_form
instead of inventing fields.

Prompt quality does not replace validation, but it reduces invalid output and improves UX.

Generated UI acceptance criteria

Before rendering a generated surface as successful, evaluate it against:

CriterionQuestion
ValidityDoes it match schema and component registry?
RelevanceDoes it support the user's current goal?
CompletenessDoes it include required fields, sources, and actions?
SafetyDoes it avoid unauthorized data and risky side effects?
AccessibilityAre labels, focus behavior, and announcements covered?
RepairabilityCan the user edit or reject wrong pieces locally?
PerformanceCan it render without blocking interaction?
TraceabilityCan we replay how this UI was produced?

This acceptance layer can be partly automated. The rest should appear in review checklists and evals.

Example: generated comparison table

A comparison table is a good GenUI example because it looks simple but carries many contracts:

{
"id": "plan-comparison",
"type": "comparison_table",
"version": "1",
"props": {
"caption": "Renewal plan options for Acme",
"columns": [
{ "id": "option", "label": "Option" },
{ "id": "price", "label": "Price impact" },
{ "id": "risk", "label": "Risk" },
{ "id": "source", "label": "Evidence" }
],
"rows": [
{
"id": "baseline",
"option": "Renew current plan",
"price": "No discount",
"risk": "High churn risk",
"source": ["usage:acme-q2", "ticket:8271"]
}
]
},
"constraints": {
"requiresCitations": true,
"maxRows": 5,
"emptyState": "Ask for more account context before comparing plans."
}
}

Architecture questions:

  • What happens if a row lacks citation ids?
  • Can the user sort or edit rows?
  • Are source ids authorized for this user?
  • Can mobile render the table as cards?
  • Does the component provide a text alternative for screen readers?
  • Does exporting this table require approval?

Anti-patterns

  • Letting the model return JSX, HTML, or script.
  • Creating a registry so broad that it becomes arbitrary UI execution.
  • Hiding agent progress behind a spinner until completion.
  • Treating generated UI as inherently accessible because the components are reused.
  • Allowing generated actions to bypass normal product authorization.
  • Shipping without replayable traces of what UI was proposed and why.
  • Optimizing for impressive generated screens instead of task completion and repairability.
  • Regenerating a whole workflow when the user only needs to fix one generated field.

Verification checklist

  • Every generated component is validated against a versioned schema.
  • The renderer rejects unknown components, unknown props, oversize payloads, and unauthorized events.
  • Generated UI has deterministic fallbacks.
  • User-entered state cannot be overwritten without consent.
  • Accessibility requirements are enforced at component-registry level.
  • Generated UI events are logged with workflow id, user id, model version, and request context.

Exercises

  1. Design a registry for five components in your product that would be safe for an agent to compose.
  2. Write the validation rules for a generated approval panel.
  3. Define how your app should recover if a generated component fails validation mid-stream.
  4. Compare a chat-only design with a GenUI design for the same workflow.

Source lens

This chapter is derived from the A2UI model of declarative UI descriptions, the AG-UI event-oriented agent/frontend model, NN/g's outcome-oriented GenUI framing, Google Research's custom interactive GenUI framing, and security patterns from frontend architecture chapters on browser attack surface, design-system contracts, and accessibility.