Composer: Replace Lit renderer with React, add v0.9 support#992
Composer: Replace Lit renderer with React, add v0.9 support#992lukasmoschitz wants to merge 14 commits intogoogle:mainfrom
Conversation
- Swap dependency from @copilotkit/a2ui-renderer to @a2ui/react@^0.8.0 - Add adapter layer (src/lib/a2ui.tsx) as single import point for all renderer consumers, enabling future v0.8/v0.9 version switching - Add composerTheme extending litTheme with additionalStyles to fix button text color (--n-10/--n-35 remap to white inside buttons) - Add A2UI color palettes (--p-*, --n-*, --s-*) in globals.css to replace the palette that Lit's themed-a2ui-surface provided via Shadow DOM - Fix nested <button> hydration error in gallery-widget by changing outer wrapper from <button> to <div role="button"> - Update all 9 import sites to use adapter instead of package directly
…ng margins - Add viewerTheme.ts: exact port of the production viewer-theme used by A2UIViewer on a2ui-composer.ag-ui.com (from private_a2ui_demo repo) - Add appTheme.ts: the app-level theme used for chat view (for future use) - Switch adapter to use viewerTheme instead of composerTheme - Fix card background in gallery: define --a2ui-card-bg at :root level and reference it via --p-100 so the gallery wrapper can override it to transparent (matching Lit's themed-a2ui-surface behavior) - Fix heading margins: add layout-m-0 to markdown.h4 and markdown.h5 to prevent browser default margins on h4/h5 elements
- Add specVersion field ('0.8' | '0.9') to Widget type
- Add all existing gallery widgets as specVersion: '0.8'
- Create V09Viewer wrapper that builds SurfaceModel from v0.9
component definitions and renders via A2uiSurface
- Update adapter (a2ui.tsx) to switch between v0.8 A2UIViewer
and v0.9 V09Viewer based on specVersion prop
- Pass specVersion through preview-pane and gallery-widget
- Switch @a2ui/react to local file dependency for v0.9 exports
- Add @a2ui/web_core dependency for MessageProcessor
- Add SpecVersionContext with localStorage persistence - Add VersionSelector toggle (v0.8/v0.9) in sidebar - Reorganize gallery data into v08/ and v09/ subdirectories - Gallery page switches widget set based on selected version - Fix v0.8 gallery data: action string shorthand → object format, invalid 'baseline' alignment → 'center' - Add v0.9 Contact Card sample widget - Fix SSR error: dynamically import V09Viewer (client-only) - Pass specVersion through widget-preview-modal
Convert all v0.8 gallery widgets to v0.9 format:
- Flat component structure (component: 'TypeName' discriminator)
- Property renames (usageHint→variant, alignment→align, distribution→justify)
- Value unwrapping (literalString→string, explicitList→array)
- Action format (action: { event: { name, context } })
- Native JSON data (no ValueMap arrays)
All samples render via V09Viewer → MessageProcessor → A2uiSurface.
Gallery switches between v0.8 and v0.9 sets via the sidebar toggle.
- Create page uses specVersion context for default components and widget creation (v0.8 or v0.9 format based on sidebar toggle) - AI prompt rewritten to document both v0.8 and v0.9 formats - User messages prefixed with [A2UI v0.8] or [A2UI v0.9] tag so the AI generates the correct component format - Start Blank also respects the selected version
…n-agnostic
Two agents:
- v08 agent with actual v0.8 catalog spec from specification/v0_8/
- v09 agent with actual v0.9 catalog spec + rules from specification/v0_9/
- Client selects agent via useAgent({ agentId }) based on specVersion
- Editor chat uses widget.specVersion to pick the right agent
- No version mixing possible — each agent only knows its format
Version-agnostic component type:
- Replace v0.8-specific ComponentInstance with A2UIComponent
(Record<string, unknown> & { id: string }) in Widget type
- Editor, preview, create all use the agnostic type
- Only the adapter interprets component structure for routing
- Remove as any[] casts from v0.9 gallery widgets
Also:
- Editor header shows version badge (v0.8/v0.9)
- Delete old combined a2ui-prompt.ts
- Components page switches docs/previews between v0.8 and v0.9 based on global spec version toggle - Add components-data-v09.ts with v0.9 property names, usage examples, and preview components for all 16 component types - Add error boundary around preview pane — invalid components show fallback instead of crashing the editor - Remove unused composerTheme.ts and appTheme.ts - Disable ChoicePicker preview (requires live data binding)
- Icons page renders with correct renderer based on spec version toggle - Add .material-symbols-outlined CSS class in globals.css — the v0.9 Icon component uses this class but Google Fonts only provides @font-face rules, not the class definition - Includes overflow: hidden to prevent icon name text from overflowing during font load
- TextField: rename 'text' prop to 'value', fix variant enum (remove 'date', add 'obscured', default 'shortText') - ChoicePicker: fix variant enum to 'multipleSelection'/'mutuallyExclusive', add displayStyle and filterable props - Image: fix 'scale-down' to 'scaleDown' in fit enum - Row/Column: add 'stretch' to justify enum - Button: add 'default' to variant enum - List: add missing 'align' prop - DateTimeInput: add missing label, min, max props - Slider: add missing label prop, add default for min - Divider: remove non-spec color/thickness props
- Add variant: 'primary' to main CTA buttons in 9 v0.9 gallery widgets - Change default spec version to v0.9 - Move version selector above navigation in sidebar - Fix music player: replace non-existent ProgressBar with Slider (max: 262, values in seconds)
- Add web_core and react renderer build steps before composer install - Add renderer paths to CI trigger so composer rebuilds when renderers change (local file dependency)
There was a problem hiding this comment.
Code Review
This pull request introduces support for A2UI protocol version 0.9 alongside the existing 0.8 version. It adds a global spec version context to manage the active version across the composer, updates the renderer adapter to handle both versions, and includes new gallery widgets for v0.9. My feedback highlights a potential hydration mismatch in the version context, suggests improving type safety for default components, and recommends refining the accessibility attributes for the gallery widget.
jacobsimionato
left a comment
There was a problem hiding this comment.
Hey thanks so much! I tried this locally and it worked really well!
There was a problem hiding this comment.
I'd love it if we could deduplicate this data with the JSON files here (which I actually pulled from your code!) https://github.com/google/A2UI/tree/main/specification/v0_8/json/catalogs/basic/examples
In other examples, we have used some tricky imports and build scripts to pull from the central location e.g. https://github.com/google/A2UI/blob/main/renderers/react/a2ui_explorer/src/examples.ts
This is non-blocking though, let's merge this a deduplicate as a 'nice to have'.
There was a problem hiding this comment.
Also we can deduplicate v0.9 with https://github.com/google/A2UI/tree/main/specification/v0_9/json/catalogs/basic/examples
This is slightly higher priority perhaps, because I've updated these examples to use v0.9-specific functionality and will continue to add them probably!
There was a problem hiding this comment.
Makes sense, especially since you're actively updating the spec examples. Let's tackle this in a follow-up PR so the gallery stays in sync.
|
Can you please update the open source license headers then ask someone at Google to merge? Thank you!! |
- Fix hydration mismatch: use useEffect + isLoaded flag to defer
version-dependent rendering until client has loaded localStorage
- Fix type: replace any[] with A2UIComponent[] for v0.9 defaults
- Fix accessibility: make role/tabIndex conditional on onClick
- Add license header to pnpm-lock.yaml for CI check
Summary
Swaps the Lit-based renderer in the composer for
@a2ui/reactand adds v0.9 support with a version switcher in the sidebar.Renderer swap
The composer previously used
@copilotkit/a2ui-rendererwhich wrapped Lit web components in Shadow DOM. This PR replaces it with the native@a2ui/reactrenderer (Light DOM). The production viewer-theme is ported over, and the necessary CSS palettes are provided inglobals.css.v0.9 rendering
A
V09Viewerwrapper creates aSurfaceModelviaMessageProcessorand renders throughA2uiSurface. The adapter layer (a2ui.tsx) is the single point that interprets component format — everything else treats components as opaqueA2UIComponent[]arrays.Version switching
A global context (persisted to localStorage) controls which version the composer uses. The sidebar has a v0.8/v0.9 toggle that affects:
v08/andv09/subdirectories)specVersion, isolated from the global toggle. Version badge shown in header.AI integration
Two separate CopilotKit agents on the server, each with only their version's actual JSON spec from the
specification/directory. The client selects the agent based onspecVersion. The editor chat uses the widget's stored version to pick the right agent.Known v0.9 renderer limitations
These are in
@a2ui/reactv0.9 basic catalog, not fixable in the composer:captionvariant renders as<caption>HTML element (hydration warning).material-symbols-outlinedCSS class definition (added inglobals.css)Test plan