Skip to content
artificia11y Amplifying everyone

Generative UI and component libraries

Generative UI is an approach where a model does not write the markup for an interface. Instead it returns a structured description, usually JSON, that names which components to show and how to configure them, and your application renders its own components from that description. This is a different lever from generated content, where the model authors the artifact and you make the result accessible after the fact. Here the model never authors markup at all, so the accessibility of what appears on screen is decided by the component library it is allowed to choose from.

  • Generative UI lets a model assemble an interface from a fixed set of approved components rather than write markup, so accessibility is decided by the library and not the model.
  • If every component in the catalog is accessible, everything the model assembles from it inherits that accessibility.
  • Build accessibility into the component definitions, so a requested component carries its roles, labels, and keyboard behavior automatically.
  • Reusing a vetted component is cheaper, more consistent, and safer for accessibility than regenerating a similar element each time.
  • A generative UI system is only as accessible as its catalog, so keep the catalog complete and audit it.

Select components instead of generating markup

Section titled “Select components instead of generating markup”

General-purpose models produce interfaces that look right but are broken underneath by default, with missing landmarks, wrong roles, and controls that a keyboard cannot reach. One engineer who tested several tools found this pattern was pervasive rather than occasional. Guidance in the prompt helps and does not hold, because the model decides again on every response. Even under the best measured conditions, where a model told to produce accessible markup passes automated checks 86 percent of the time, about one output in seven still fails. Per-response correctness is not something to lean on.

The stronger move is to stop asking the model for markup and give it a menu instead. The model returns a description that selects from a set of components you have already built and tested, and your application renders those components. Because the model can only reach for parts that already meet the bar, every interface it assembles meets the bar too. Adrian Roselli reaches the same conclusion from the failure side, that a deterministic, vetted pattern library beats trusting generated code.

The component library is the accessibility boundary

Section titled “The component library is the accessibility boundary”

Once the model can only pick from your catalog, the catalog is where accessibility is won or lost. Anna E. Cook calls the guarantees that must never break the protected invariants of a system, such as keyboard operability, semantic structure, programmatic labels, and focus order. A component library is where you can hold those invariants, because you build them into each part once and reuse them everywhere.

So put the accessibility into the component definitions rather than the prompt. When you define the catalog, each component can carry its role, its labelling rules, and its keyboard behavior, so a request for that component brings them along on its own. This moves the guarantee out of something you hope the model writes and into something the system supplies every time. The same idea runs through Cook’s argument that AI depends on accessible systems rather than repairing broken ones, so the parts it draws from have to be sound first.

Two emerging projects put this pattern into a shared format. A2UI is a framework-agnostic protocol where an agent describes an interface as declarative JSON and any client renders it with its own components, and the agent can only choose from a catalog the client has approved in advance. json-render does the same for React, with the catalog defined as schemas, so the model’s output is bounded to the components and props you allow.

Both exist to make model-driven interfaces safe and predictable, and both leave accessibility to the implementer, because neither one requires the catalog components to be accessible. That is the gap to close. The pattern gives you a place to guarantee accessibility, and you still have to build accessible components to put in it. This is also why generative UI is not the shortcut some framing suggests. Jakob Nielsen has argued that per-user generative interfaces could replace accessibility work, but a generated interface is only as accessible as the parts it is built from, so the work moves into the catalog rather than disappearing.

There is a pull toward regenerating an element every time it is needed, because the model can. Reusing a component you already have is better on three counts. It costs less, because you are not spending tokens and time producing markup you already own. It stays consistent, because a model builds each screen from its own prompt and does not carry visual language or component behavior from one screen to the next. And it protects accessibility, because a component that was audited once keeps its guarantees, while a freshly generated element is a new roll of the dice. Regeneration turns a solved accessibility problem back into an open one on every render.

Audience: Engineer

Treat the model’s response as a description to validate, not markup to trust. Parse it, check each node against your catalog schema, and render real components from your own library. Keep a strict allowlist so an unknown component type is rejected rather than passed through as raw HTML. Because the components are yours, the roles, labels, and keyboard handling live in the component once and the model cannot route around them.

// The model returns a description, not markup
{ "component": "Alert", "props": { "tone": "warning", "title": "Low balance" } }
// Your app renders a vetted, accessible component from the description.
// An unknown component type is rejected, never rendered as HTML.
const entry = catalog[node.component];
if (!entry) throw new Error(`Unknown component: ${node.component}`);
return <entry.Component {...validate(entry.schema, node.props)} />;
Audience: Accessibility Specialist

The audit target changes shape. Instead of checking every generated output, you audit the catalog once, because every interface is assembled from it, which is a large cut in surface area. It is not zero though. Check that the catalog is complete, because a missing component pushes the model to improvise something outside the library. And sample real compositions, because accessible parts can still be arranged into an inaccessible whole, such as a broken heading order or a focus trap between two components.

Audience: Designer

A generative UI is only as expressive as its catalog. Every state and pattern the product needs has to have a matching accessible component, or the model will reach for something that only looks right. Treat the catalog as a design deliverable in its own right. The gaps in it are where accessibility breaks, because a gap is an invitation for the model to improvise.

Audience: Self-advocate

An interface assembled from the same tested parts behaves the same way every time, so what you learn on one screen carries to the next. That consistency is not a small thing. When a tool regenerates its controls on every screen, the labels and shortcuts you rely on can shift under you, and you end up relearning the same interface again and again. Reused, tested components keep the ground steady.