A lightweight A2UI renderer and catalog for agent-driven web interfaces.
npm install a2ui-oata2ui-oat is an open-source community renderer that pairs Google's A2UI protocol with Kailash Nadh's Oat CSS library and its companion micro-libraries. It fills a gap in the A2UI renderer ecosystem: every maintained renderer today targets a framework (React, Angular, Lit, or Flutter). There is no lightweight, framework-free option for web-only deployments.
a2ui-oat provides two artifacts:
- Oat Catalog -- A custom A2UI catalog JSON schema exposing 37 UI components and 22 registered functions.
- Oat Renderer -- A minimal JavaScript renderer built on
@a2ui/web-libthat converts A2UI JSON messages into semantic HTML styled automatically by Oat.
The project supports a dual-mode architecture: A2UI Mode for structured, catalog-constrained rendering with full security guarantees, and Direct Mode for trusted-agent scenarios where the LLM emits semantic HTML and Oat styles it with zero intermediary. Both modes share the same CSS, JS, and companion libraries. The choice between them is a security architecture decision.
- 37 components -- Full Basic Catalog (16) plus 21 Oat-native components: tables, pagination, progress, meter, skeleton loaders, toasts, tooltips, alerts, breadcrumbs, sidebars, accordions, switches, badges, avatars, video, audio, grid, dropdown, and more.
- 22 registered functions -- Data (
fetchPage,fetchAndAppend,subscribeSSE,subscribeWebSocket), navigation (openUrl,navigateTo), UI (showToast,debounce), formatting (formatDate,formatNumber,formatString,formatCurrency,pluralize), logic (and,or,not), and validation (required,regex,length,numeric,email). - ~13KB total client footprint -- Oat CSS + JS + companion libraries, minified and gzipped.
- Zero framework dependencies -- No React, Angular, Lit, or build tooling required. Deploy via CDN include.
- Dual-mode architecture -- A2UI Mode (structured, validated) and Direct Mode (HTML-native, zero intermediary).
- A2A integration -- Catalog negotiation, inline catalog support, multi-agent surface ownership.
- Autonomous surfaces -- Agents build a surface once; registered functions keep it running (pagination, streaming, routing) after the agent disconnects.
import { createOatRenderer, registerWithWebLib } from 'a2ui-oat';
const { renderer, functions } = createOatRenderer();
registerWithWebLib(webLib); // one-call setup for @a2ui/web-libInclude Oat CSS, @a2ui/web-lib, and the Oat Renderer. The agent emits A2UI JSON against the Oat Catalog schema. The protocol engine parses the stream, manages state, and delegates to the renderer for HTML output.
<!-- Oat CSS + JS -->
<link rel="stylesheet" href="https://unpkg.com/@knadh/oat/oat.min.css">
<script src="https://unpkg.com/@knadh/oat/oat.min.js"></script>
<!-- A2UI Protocol Engine -->
<script src="https://unpkg.com/@a2ui/web-lib"></script>
<!-- Oat Renderer -->
<script type="module">
import { createOatRenderer } from 'https://unpkg.com/a2ui-oat/renderer/index.js';
const { renderer, functions } = createOatRenderer();
// Connect to your agent transport (A2A, MCP, WebSocket, SSE)
</script>Include only Oat CSS + JS. The agent emits semantic HTML directly. Oat styles it automatically.
<link rel="stylesheet" href="https://unpkg.com/@knadh/oat/oat.min.css">
<script src="https://unpkg.com/@knadh/oat/oat.min.js"></script>
<div id="agent-output"></div>
<script>
// Agent sends semantic HTML; insert it into the DOM.
// Oat CSS styles it automatically based on element semantics.
// For untrusted input, run through the sanitizer first: see direct/sanitizer.js
document.getElementById("agent-output").innerHTML = agentHtml;
</script>See docs/when-to-use-which.md for guidance on choosing between modes.
| Component | HTML Output | Source |
|---|---|---|
| Row | <div class="row"> |
Basic Catalog + Oat Grid |
| Column | <div class="col-{n}"> |
Basic Catalog + Oat Grid |
| Grid | <div class="container"><div class="row"> |
Oat 12-column grid |
| List | <ul> / <ol> |
Basic Catalog |
| Sidebar | <aside> |
Oat Sidebar |
| Component | HTML Output | Source |
|---|---|---|
| Text | <h1>--<h6>, <p> |
Basic Catalog |
| Image | <img> |
Basic Catalog |
| Icon | <span> (icon class) |
Basic Catalog |
| Divider | <hr> |
Basic Catalog |
| Badge | <span data-badge> |
Oat Badge |
| Avatar | <img class="avatar"> |
Oat Avatar |
| Spinner | <div class="spinner"> |
Oat Spinner |
| Skeleton | <div class="skeleton"> |
Oat Skeleton |
| Progress | <progress> |
Oat Progress |
| Meter | <meter> |
Oat Meter |
| Video | <video> |
Oat / HTML5 |
| AudioPlayer | <audio> |
Oat / HTML5 |
| Component | HTML Output | Source |
|---|---|---|
| Button | <button> |
Basic Catalog |
| TextField | <input> / <textarea> |
Basic Catalog |
| CheckBox | <input type="checkbox"> |
Basic Catalog |
| Switch | <input type="checkbox" role="switch"> |
Oat Switch |
| Slider | <input type="range"> |
Basic Catalog |
| DateTimeInput | <input type="date/time"> |
Basic Catalog |
| ChoicePicker | <select> / radio group |
Basic Catalog |
| Autocomplete | <input> + floatype.js |
Oat + floatype.js |
| Component | HTML Output | Source |
|---|---|---|
| Card | <article> / <section> |
Basic Catalog |
| Modal | <dialog> |
Basic Catalog + Oat Dialog |
| Tabs | <oat-tabs> |
Basic Catalog + Oat Tabs WC |
| Accordion | <details><summary> |
Oat Accordion |
| Tooltip | <span data-tooltip> |
Oat Tooltip |
| Dropdown | <ot-dropdown> |
Oat Dropdown WC |
| Component | HTML Output | Source |
|---|---|---|
| Table | <table> |
Oat Table |
| Pagination | <nav> (pagination) |
Oat Pagination |
| Alert | <div role="alert"> |
Oat Alert |
| Toast | <oat-toast> |
Oat Toast WC |
| Breadcrumb | <nav aria-label="breadcrumb"> |
Oat Breadcrumb |
Total: 37 components. The Basic Catalog's 16 are fully included. The additional 21 are native Oat primitives.
| Function | Backed By | Purpose |
|---|---|---|
| fetchPage | fetch API | Client-side cursor pagination. Fetches next page and writes to data model. |
| fetchAndAppend | fetch API | Infinite scroll. Appends results to existing array in data model. |
| subscribeSSE | EventSource | Opens SSE stream. Writes each event to data model path. |
| subscribeWebSocket | WebSocket | Opens WebSocket. Writes parsed messages to data model. |
| Function | Backed By | Purpose |
|---|---|---|
| openUrl | window.open | Opens URL in browser (target: "_blank" or "_self"). |
| navigateTo | tinyrouter.js | Client-side SPA routing via window.history. |
| Function | Backed By | Purpose |
|---|---|---|
| showToast | Oat Toast WC | Triggers a toast notification from any action. |
| debounce | vanilla JS | Wraps another action with a debounce delay. |
| Function | Backed By | Purpose |
|---|---|---|
| formatDate | Intl.DateTimeFormat | Locale-aware date formatting. |
| formatNumber | Intl.NumberFormat | Locale-aware number formatting. |
| formatString | vanilla JS | String interpolation using ${/data/model/path} placeholders. |
| formatCurrency | Intl.NumberFormat | Currency formatting with ISO 4217 currency codes. |
| pluralize | vanilla JS | Returns singular or plural form based on count. |
| Function | Backed By | Purpose |
|---|---|---|
| and | vanilla JS | Returns true if all conditions in an array are truthy. |
| or | vanilla JS | Returns true if any condition in an array is truthy. |
| not | vanilla JS | Returns the boolean negation of a value. |
| Function | Backed By | Purpose |
|---|---|---|
| required | vanilla JS | Validates that a value is non-null and non-empty. |
| regex | vanilla JS | Validates a value against a regular expression pattern. |
| length | vanilla JS | Validates that a string's length falls within min/max bounds. |
| numeric | vanilla JS | Validates that a value is a valid number. |
| vanilla JS | Validates that a value matches a basic email address pattern. |
Agent (LLM)
| Generates A2UI JSON against Oat Catalog schema
v
Transport (A2A / MCP / WebSocket / SSE)
| Delivers DataParts with mimeType: application/json+a2ui
v
@a2ui/web-lib (Protocol Engine)
| Parses JSONL stream, manages surfaces, resolves data bindings
v
Oat Renderer
| Maps component types -> semantic HTML elements
v
Oat CSS + JS (~8KB)
| Styles semantic HTML automatically
v
Browser DOM
| Layer | Artifact | Author | Role |
|---|---|---|---|
| Oat Catalog | oat-catalog.json | a2ui-oat project | Defines 37 components and 22 registered functions as A2UI-compliant JSON Schema |
| Oat Renderer | oat-renderer.js | a2ui-oat project | Maps catalog components to semantic HTML elements |
| Protocol Engine | @a2ui/web-lib | Google (existing) | Stream parsing, state management, data binding, validation |
| Styling | Oat CSS + JS + companions | Kailash Nadh (existing) | Automatic semantic styling, Web Components for dynamic elements |
For the full architecture document, see docs/architecture.md.
| Library | Size | Role |
|---|---|---|
| Oat CSS + JS | ~8KB | Automatic semantic styling, Web Components for Tabs, Dropdown, Toast |
| tinyrouter.js | ~950B | Client-side SPA routing via window.history |
| floatype.js | ~1.2KB | Floating autocomplete/autosuggestion for text inputs |
| dragmove.js | ~500B | Make DOM elements draggable and movable |
| indexed-cache.js | ~2.1KB | IndexedDB caching for Oat assets across sessions |
Total shared footprint: ~13KB minified and gzipped. This is the complete client-side runtime for both modes, excluding @a2ui/web-lib which is only required for A2UI Mode.
a2ui-oat provides three security tiers. The choice depends on the trust level of the agent producing UI.
| Tier | Mode | Trust Level | Validation | Use Case |
|---|---|---|---|---|
| 1 (Strictest) | A2UI + Oat Catalog | Untrusted | Catalog allowlist + schema validation + sandboxed functions | Multi-vendor A2A, Agentspace, regulated environments |
| 2 (Moderate) | A2UI + OatHTML component | Semi-trusted | Catalog allowlist + HTML sanitizer | Internal agents needing layout flexibility |
| 3 (Open) | Direct Mode | Fully trusted | Optional sanitizer | Internal tools, prototypes, single-agent |
A2UI Mode provides defense in depth: the catalog acts as an allowlist, two-phase validation catches hallucinated properties, registered functions are sandboxed, and data model isolation protects multi-agent scenarios.
Direct Mode has no catalog, no schema validation, and no sandboxing. It is appropriate only for trusted, internally-controlled agents.
OatHTML accepts sanitized HTML within A2UI's security model, bridging the gap between strict catalog constraints and full HTML freedom.
See docs/when-to-use-which.md for a detailed decision guide.
| Attribute | a2ui-oat | Lit Renderer | React Renderer | Angular Renderer |
|---|---|---|---|---|
| Client footprint | ~13KB | ~15KB+ | ~45KB+ | ~60KB+ |
| Components | 37 | 16 | 16 | 16 |
| Registered functions | 22 | Basic set | Basic set | Basic set |
| Framework dependency | None | Lit | React | Angular |
| Build tooling required | No | Yes | Yes | Yes |
| Client-side pagination | Native | Custom | Custom | Custom |
| Real-time streaming | Native | Custom | Custom | Custom |
| Direct HTML mode | Yes | No | No | No |
| Zero-build deployment | Yes (CDN) | No | No | No |
- A2UI Specification (v0.9)
- A2UI Component Gallery
- Oat UI
- Oat GitHub
- Architecture Document
- Mode Decision Guide
- Oat Catalog
MIT