npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@a2ui/react

v0.9.0-alpha.0

Published

React renderer for A2UI (Agent-to-User Interface)

Readme

@a2ui/react

React renderer for A2UI (Agent-to-User Interface) - enables AI agents to generate rich, interactive user interfaces through declarative JSON.

Table of Contents

Installation

npm install @a2ui/react

Peer Dependencies: - React 18.x or 19.x - React DOM 18.x or 19.x

Quick Start

import { A2UIProvider, A2UIRenderer, injectStyles } from '@a2ui/react';
import { useA2UI } from '@a2ui/react';

// Inject A2UI styles once at app startup
injectStyles();

function App() {
  const { processMessages } = useA2UI();

  // Process A2UI messages from your AI agent
  const handleAgentResponse = (messages) => {
    processMessages(messages);
  };

  return (
    <A2UIProvider onAction={handleAction}>
      <A2UIRenderer surfaceId="main" />
    </A2UIProvider>
  );
}

// Handle user interactions
function handleAction(message) {
  console.log('User action:', message);
  // Send to your AI agent backend
}

Standalone Viewer

For simpler use cases, use the all-in-one A2UIViewer:

import { A2UIViewer, injectStyles } from '@a2ui/react';

injectStyles();

function App() {
  const messages = [...]; // A2UI messages from agent

  return (
    <A2UIViewer
      messages={messages}
      onAction={(msg) => console.log('Action:', msg)}
    />
  );
}

Architecture

Versioned Structure

To support the evolution of the A2UI protocol, the @a2ui/react package is organized into versioned subdirectories (e.g., v0_8/, and soon v0_9/).

renderers/react/
├── src/
│   ├── v0_8/           # Implementation of the v0.8 protocol
│   │   ├── components/
│   │   ├── core/
│   │   └── ...
│   ├── index.ts        # Proxy export for backward compatibility -> ./v0_8/index
│   ├── types.ts        # Proxy export -> ./v0_8/types
│   └── styles/         # Proxy export -> ../v0_8/styles/index

Backward Compatibility & Future Roadmap

The top-level exports in package.json and the proxy files in the src/ root currently default to the v0_8 implementation. This guarantees that existing applications relying on import { A2UIProvider } from '@a2ui/react' will continue to work without modification.

Applications can also explicitly import from a specific version path if desired (e.g., import { A2UIProvider } from '@a2ui/react/v0_8').

As the v0_9 protocol implementation is added to the v0_9/ subdirectory, the top-level exports will be updated to point to the newest stable version.

Note: This side-by-side versioned directory structure is a temporary transition phase. Eventually, the v0_8 renderer will be deprecated and removed. The package will then revert to a single, unified codebase that natively supports multiple versions of the A2UI specification simultaneously.

Two-Context Pattern

The React renderer uses a two-context architecture for optimal performance:

┌─────────────────────────────────────────────────────────┐
│                     A2UIProvider                         │
├─────────────────────────────────────────────────────────┤
│  ┌─────────────────────┐  ┌─────────────────────────┐   │
│  │  A2UIActionsContext │  │   A2UIStateContext      │   │
│  │  (stable reference) │  │   (triggers re-renders) │   │
│  │                     │  │                         │   │
│  │  • processMessages  │  │   • version             │   │
│  │  • setData          │  │                         │   │
│  │  • dispatch         │  │                         │   │
│  │  • getData          │  │                         │   │
│  │  • getSurface       │  │                         │   │
│  └─────────────────────┘  └─────────────────────────┘   │
│                                                          │
│  ┌────────────────────────────────────────────────────┐ │
│  │                   ThemeProvider                     │ │
│  │                                                     │ │
│  │   ┌─────────────┐    ┌─────────────────────────┐   │ │
│  │   │ A2UIRenderer│───▶│     ComponentNode       │   │ │
│  │   │ (surfaceId) │    │  (recursive rendering)  │   │ │
│  │   └─────────────┘    └─────────────────────────┘   │ │
│  └────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────┘

Why two contexts?

  • A2UIActionsContext: Contains stable action callbacks that never change reference. Components using useA2UIActions() won't re-render when data changes.
  • A2UIStateContext: Contains a version counter that increments on state changes. Only components that need to react to data changes subscribe to this.

This separation prevents unnecessary re-renders and provides fine-grained control over component updates.

Data Flow

Agent Server                    React App
     │                              │
     │  ServerToClientMessage[]     │
     ├─────────────────────────────▶│ processMessages()
     │                              │
     │                              ▼
     │                     ┌────────────────┐
     │                     │ MessageProcessor│
     │                     │   (surfaces)   │
     │                     └───────┬────────┘
     │                             │
     │                             ▼
     │                     ┌────────────────┐
     │                     │  A2UIRenderer  │
     │                     │  (per surface) │
     │                     └───────┬────────┘
     │                             │
     │                             ▼
     │                     ┌────────────────┐
     │                     │ ComponentNode  │
     │                     │  (recursive)   │
     │                     └───────┬────────┘
     │                             │
     │  A2UIClientEventMessage     │ User interaction
     │◀────────────────────────────┤ dispatch()
     │                              │

Components

All components are wrapped with React.memo() for performance optimization.

Content Components

Component | Description ------------- | ---------------------------------------- Text | Renders text with markdown support Image | Displays images with various usage hints Icon | Renders Material Symbols icons Divider | Horizontal or vertical divider Video | Video player AudioPlayer | Audio player

Layout Components

Component | Description --------- | ------------------------------------ Column | Vertical flex container Row | Horizontal flex container Card | Card container with styling List | List container (vertical/horizontal) Tabs | Tabbed interface Modal | Modal dialog

Interactive Components

Component | Description ---------------- | ------------------------------------- Button | Clickable button with action dispatch TextField | Text input (single/multiline) CheckBox | Checkbox input Slider | Range slider DateTimeInput | Date/time picker MultipleChoice | Radio/checkbox group

Component Structure

Each component mirrors the Lit renderer's Shadow DOM structure for visual parity:

// React component structure
<div className="a2ui-{component}">    {/* :host equivalent */}
  <section className="theme-classes"> {/* internal element */}
    {children}                        {/* ::slotted(*) equivalent */}
  </section>
</div>

Hooks

useA2UI()

High-level hook for external application use:

import { useA2UI } from '@a2ui/react';

function MyComponent() {
  const { processMessages, clearSurfaces } = useA2UI();

  const loadUI = async () => {
    const response = await fetch('/api/agent');
    const messages = await response.json();
    processMessages(messages);
  };

  return <button onClick={loadUI}>Load UI</button>;
}

useA2UIActions()

Access stable actions without triggering re-renders:

import { useA2UIActions } from '@a2ui/react';

function ActionButton() {
  const { dispatch } = useA2UIActions();

  const handleClick = () => {
    dispatch({
      event: { action: { name: 'submit' } },
      sourceComponent: 'button-1',
      surfaceId: 'main',
    });
  };

  return <button onClick={handleClick}>Submit</button>;
}

useA2UIState()

Subscribe to state changes (triggers re-renders):

import { useA2UIState } from '@a2ui/react';

function VersionDisplay() {
  const { version } = useA2UIState();
  return <span>State version: {version}</span>;
}

useA2UIContext()

Combined access to actions and state:

import { useA2UIContext } from '@a2ui/react';

function MyComponent() {
  const { processMessages, dispatch, version } = useA2UIContext();
  // ...
}

useA2UIComponent()

Internal hook for component implementations. Automatically subscribes to state changes so components with path bindings re-render when data updates.

import { useA2UIComponent } from '@a2ui/react';

function CustomComponent({ node, surfaceId }) {
  const {
    theme,
    resolveString,
    resolveNumber,
    resolveBoolean,
    setValue,
    getValue,
    sendAction,
    getUniqueId,
  } = useA2UIComponent(node, surfaceId);

  const text = resolveString(node.properties.text);
  // ...
}

Path Binding Reactivity: When a component uses setValue() to update the data model, all components reading from the same path via resolveString(), resolveNumber(), or resolveBoolean() will automatically re-render with the new value.

Theme System

Using the Default Theme

import { A2UIProvider, litTheme } from '@a2ui/react';

<A2UIProvider theme={litTheme}>
  {/* components */}
</A2UIProvider>

Creating a Custom Theme

import { litTheme } from '@a2ui/react';

const customTheme = {
  ...litTheme,
  components: {
    ...litTheme.components,
    Button: {
      ...litTheme.components.Button,
      all: {
        'my-button-class': true,
        'rounded-lg': true,
      },
    },
  },
};

<A2UIProvider theme={customTheme}>
  {/* components */}
</A2UIProvider>

Theme Structure

interface Theme {
  components: {
    [ComponentName]: {
      all: ClassMap;        // Always applied
      [variant]: ClassMap;  // Variant-specific (e.g., primary, secondary)
    };
  };
  elements: {
    [elementName]: ClassMap; // HTML element styling
  };
  markdown: {
    [tagName]: string[];    // Markdown element classes
  };
  additionalStyles?: {
    [ComponentName]: Record<string, string>; // Inline styles
  };
}

Component Registry

Default Catalog

The default catalog registers all standard A2UI components:

import { initializeDefaultCatalog } from '@a2ui/react';

// Call once at app startup
initializeDefaultCatalog();

Custom Components

Register custom components to extend or override the default catalog:

import { ComponentRegistry } from '@a2ui/react';

// Get the singleton registry
const registry = ComponentRegistry.getInstance();

// Register a custom component
registry.register('CustomButton', {
  component: MyCustomButton,
});

// Override an existing component
registry.register('Button', {
  component: MyEnhancedButton,
});

Lazy Loading

Components can be lazy-loaded for code splitting:

registry.register('HeavyChart', {
  component: () => import('./components/HeavyChart'),
  lazy: true,
});

Note: Small, commonly-used components (like Tabs, Modal) should be statically imported to avoid Vite cache issues during development.

Styles

Injecting Styles

Inject A2UI structural and component styles once at app startup:

import { injectStyles } from '@a2ui/react/styles';

// In your app entry point
injectStyles();

Style Architecture

The styles module provides:

  • structuralStyles: Utility classes from Lit renderer (layout-, typography-, color-*)
  • componentSpecificStyles: CSS that replicates Lit's Shadow DOM scoped styles
import { structuralStyles, componentSpecificStyles } from '@a2ui/react/styles';

CSS Variables

CSS color variables must be defined by the host application:

:root {
  --n-0: #ffffff;
  --n-100: #f5f5f5;
  /* ... other palette variables */
  --p-500: #3b82f6;
  /* ... */
}

Removing Styles

For cleanup (e.g., in tests):

import { removeStyles } from '@a2ui/react/styles';

removeStyles();

Development

Setup

cd renderers/react
npm install

Build

npm run build    # Build the package
npm run dev      # Watch mode

Type Check

npm run typecheck

Lint

npm run lint

Testing

Unit Tests

Uses Vitest + React Testing Library.

npm test              # Run once
npm run test:watch    # Watch mode

Structure: tests/ ├── setup.ts # Initializes component catalog ├── helpers.tsx # TestWrapper, TestRenderer, message creators └── components/ # Component tests (*.test.tsx)

Example: ```tsx import { render, screen, fireEvent } from '@testing-library/react'; import { TestWrapper, TestRenderer, createSimpleMessages } from '../helpers';

it('should dispatch action on click', () => { const onAction = vi.fn(); const messages = createSimpleMessages('btn-1', 'Button', { child: 'text-1', action: { name: 'submit' }, });

render( );

fireEvent.click(screen.getByRole('button')); expect(onAction).toHaveBeenCalled(); }); ```

Visual Parity Testing

The React renderer maintains visual parity with the Lit renderer (reference implementation). A comprehensive test suite compares pixel-perfect screenshots between both renderers.

Running Visual Parity Tests

cd visual-parity
npm install
npm test

Quick Commands

# Run all tests
npm test

# Run specific component tests
npm test -- --grep "button"

# Run with UI mode
npm run test:ui

# Start dev servers for manual inspection
npm run dev
# React: http://localhost:5001
# Lit: http://localhost:5002

Documentation

Key Concepts

  1. Structural Mirroring: React components mirror Lit's Shadow DOM structure
  2. CSS Selector Transformation: :host.a2ui-surface .a2ui-{component}
  3. Specificity Matching: Uses :where() to match Lit's low specificity

API Reference

Core Exports

// Provider and Renderer
export { A2UIProvider, A2UIRenderer, A2UIViewer, ComponentNode };

// Hooks
export { useA2UI, useA2UIActions, useA2UIState, useA2UIContext, useA2UIComponent };

// Registry
export { ComponentRegistry, registerDefaultCatalog, initializeDefaultCatalog };

// Theme
export { ThemeProvider, useTheme, litTheme, defaultTheme };

// Styles (from '@a2ui/react/styles')
export { injectStyles, removeStyles, structuralStyles, componentSpecificStyles };

// Utilities
export { cn, classMapToString, stylesToObject };

// All component exports
export { Text, Image, Icon, Divider, Video, AudioPlayer };
export { Row, Column, Card, List, Tabs, Modal };
export { Button, TextField, CheckBox, Slider, DateTimeInput, MultipleChoice };

Types

import type {
  Types,
  Theme,
  Surface,
  SurfaceID,
  AnyComponentNode,
  ServerToClientMessage,
  A2UIClientEventMessage,
  A2UIComponentProps,
  A2UIProviderProps,
  A2UIRendererProps,
  UseA2UIResult,
  UseA2UIComponentResult,
} from '@a2ui/react';

Contributing

Code Style

  • All components use React.memo() for performance
  • Use the two-context pattern for state management
  • Follow the existing component structure for visual parity

Adding a New Component

  1. Create component in src/components/{category}/{ComponentName}.tsx
  2. Follow wrapper div + section structure (see Component Structure)
  3. Register in src/registry/defaultCatalog.ts
  4. Export from src/index.ts
  5. Add unit tests in tests/components/{ComponentName}.test.tsx
  6. Add visual parity fixtures in visual-parity/fixtures/components/