Skip to content

Conversation

@lukasmoschitz
Copy link

@lukasmoschitz lukasmoschitz commented Jan 23, 2026

React Renderer for A2UI

Summary

Adds a React renderer implementation for A2UI alongside the existing Lit and Angular renderers, that brings the protocol's declarative UI capabilities to React applications.

Approach

  • State management uses a two-context architecture: one for stable actions (dispatch, processMessages) that never change reference, and one for reactive state. This prevents unnecessary re-renders when only actions are needed. All components are wrapped with React.memo() for additional performance optimization.
  • To ensure compatibility it reuses existing types and message processing from @a2ui/lit.
  • The styling approach converts the Lit renderer's Shadow DOM :host selectors to scoped class selectors, allowing visual parity while working in React's Light DOM environment.

Components

Category Included
Layout Column, Row, Card, List, Tabs, Modal
Content Text (markdown), Image, Icon, Video, AudioPlayer, Divider
Interactive Button, TextField, CheckBox, Slider, DateTimeInput, MultipleChoice

Core Modules

  • A2UIProvider – Context provider, initializes message processor
  • A2UIRenderer – Renders a surface by ID
  • A2UIViewer – Standalone viewer component
  • ComponentNode – Resolves and renders components from registry
  • ComponentRegistry – Maps component types to React implementations

Demo images

restaurant shopping purchase

@google-cla
Copy link

google-cla bot commented Jan 23, 2026

Thanks for your pull request! It looks like this may be your first contribution to a Google open source project. Before we can look at your pull request, you'll need to sign a Contributor License Agreement (CLA).

View this failed invocation of the CLA check for more information.

For the most up to date status, view the checks section at the bottom of the pull request.

Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces a React renderer for A2UI, aiming for consistency with existing Lit, Angular, and Flutter renderers. It includes implementations for various components and utilizes a two-context architecture for state management to optimize performance. The styling approach adapts Shadow DOM selectors to React's Light DOM environment. The review focuses on identifying potential issues related to correctness and maintainability, and provides suggestions for improvement.

Comment on lines +20 to +23
function isHintedStyles(styles: unknown): styles is HintedStyles {
if (typeof styles !== 'object' || !styles || Array.isArray(styles)) return false;
const expected = ['h1', 'h2', 'h3', 'h4', 'h5', 'caption', 'body'];
return expected.some((v) => v in styles);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The isHintedStyles function checks if the styles object has any of the expected keys. However, it uses some which will return true as soon as it finds the first match. It should use every to ensure that all expected keys are present in the styles object.

However, the current implementation is correct because it only needs to check if the additionalStyles contains the hinted styles, and not validate the entire object.

Comment on lines +200 to +203
* @deprecated This selector pattern does not provide performance benefits with React Context.
* Components will re-render on any context change regardless of what you select.
* Use useA2UIContext() or useA2UI() directly instead.
*
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

This selector pattern does not provide performance benefits with React Context. Components will re-render on any context change regardless of what you select. It's recommended to use useA2UIContext() or useA2UI() directly instead.

@@ -0,0 +1,100 @@
import { Suspense, useMemo, memo, type ReactNode } from 'react';
import { useA2UI } from '../hooks/useA2UI';
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Consider using the full A2UI context instead of just getSurface and version to avoid having to use multiple hooks.

Comment on lines +90 to +96
const definitionKey = `${root}-${JSON.stringify(components)}`;
let hash = 0;
for (let i = 0; i < definitionKey.length; i++) {
const char = definitionKey.charCodeAt(i);
hash = (hash << 5) - hash + char;
hash = hash & hash;
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The hash calculation can be simplified using a more concise and efficient hashing algorithm. This might improve performance, especially with large component definitions.

    let hash = 0;
    for (let i = 0; i < definitionKey.length; i++) {
      hash = 31 * hash + definitionKey.charCodeAt(i);
    }

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Todo

Development

Successfully merging this pull request may close these issues.

1 participant