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

@atlas.agents/a2ui-canvas

v0.2.1

Published

A2UI-native dynamic canvas component for React — interoperable with LangChain, 3dchat, Microsoft Agent Framework, and any A2UI-speaking client

Downloads

179

Readme

@atlas.agents/a2ui-canvas

A2UI-native dynamic canvas component for React. Renders streaming, agent-driven visuals from any A2UI-speaking source — LangChain agents, Microsoft Agent Framework, or direct JSON.

Features

  • A2UI Protocol: Native support for the Agent-to-UI message protocol (createSurface, updateDataModel, updateComponents, etc.)
  • 9 Renderers: Chart, Timeline, KnowledgeGraph, Map, Media, Document, Code, Artifact, Custom
  • Streaming: JSONL, SSE, WebSocket, and direct message ingestion with batched updates
  • Lazy Loading: Heavy renderers (D3, Cesium, shiki) loaded on demand with SVG fallbacks
  • Schema-Only Export: Zero-React-dependency ./schema entry point for server-side / non-JS consumers
  • Multi-Entry Build: Tree-shake individual renderers via ./renderers/chart, ./renderers/map, etc.
  • Theme System: Built-in light/dark themes with full customization
  • Type Safe: Full TypeScript support with discriminated union component types
  • Backward Compatible: Legacy CanvasProvider, useCanvas, useCanvasContent hooks still work

Installation

npm install @atlas.agents/a2ui-canvas

Optional Peer Dependencies

Install only the renderers you need:

# Charts (D3)
npm install d3

# Documents (Markdown)
npm install react-markdown remark-gfm

# Code (syntax highlighting)
npm install shiki

# Knowledge Graph (force-directed)
npm install react-force-graph-2d
# or for 3D:
npm install react-force-graph-3d

# Map (Cesium globe)
npm install @cesium/engine resium

All renderers include built-in SVG/HTML fallbacks when their optional deps are not installed.

Quick Start

A2UI Messages (recommended)

Feed A2UI JSON messages from your AI agent and the canvas renders automatically:

import { useA2UISurface, RendererResolver } from '@atlas.agents/a2ui-canvas';

function AgentCanvas() {
  const { activeSurface, getComponents, resolveData, store } = useA2UISurface({
    sseUrl: '/api/agent/stream',
  });

  if (!activeSurface) return <div>Waiting for agent...</div>;

  return (
    <div>
      {getComponents(activeSurface.id).map((component) => (
        <RendererResolver
          key={component.id}
          component={component}
          data={resolveData(activeSurface.id, '/')}
          theme="dark"
        />
      ))}
    </div>
  );
}

Direct Messages

Process A2UI messages directly without a stream:

import { useA2UISurface } from '@atlas.agents/a2ui-canvas';

const messages = [
  { createSurface: { surfaceId: 's1' } },
  {
    updateDataModel: {
      surfaceId: 's1',
      path: '/chart',
      value: { labels: ['Jan', 'Feb', 'Mar'], values: [10, 25, 18] },
    },
  },
  {
    updateComponents: {
      surfaceId: 's1',
      components: [
        {
          id: 'c1',
          component: 'Chart',
          chartType: 'bar',
          data: '/chart',
        },
      ],
    },
  },
];

function App() {
  const { activeSurface, getComponents } = useA2UISurface({ messages });
  // ...render components
}

WebSocket / Fetch Stream

// WebSocket
const result = useA2UISurface({ wsUrl: 'wss://agent.example.com/stream' });

// Fetch (JSONL body)
const result = useA2UISurface({
  streamUrl: '/api/agent/generate',
  streamInit: { method: 'POST', body: JSON.stringify({ prompt: '...' }) },
});

Legacy API

The original CanvasProvider / useCanvas API still works:

import { CanvasProvider, useCanvas, themes } from '@atlas.agents/a2ui-canvas';

function App() {
  return (
    <CanvasProvider initialTheme={themes.dark}>
      <MyApp />
    </CanvasProvider>
  );
}

function MyApp() {
  const { content, setContent } = useCanvas();
  // ...
}

A2UI Protocol

The library speaks the A2UI (Agent-to-UI) protocol. Agents send JSON messages to create surfaces, populate data models, and attach components:

Envelope Messages

| Message | Description | |---------|-------------| | createSurface | Create a new UI surface (canvas) | | destroySurface | Remove a surface | | updateDataModel | Set data at a JSON Pointer path | | updateComponents | Set or replace components on a surface | | removeComponents | Remove components by ID | | appendData | Append items to an array (streaming convenience) | | patchDataModel | JSON merge-patch the data model |

Component Types

| Component | Description | Optional Dep | |-----------|-------------|-------------| | Chart | Bar, line, scatter, pie, area, donut charts | d3 | | Timeline | Events with alternating/grouped layouts | — | | KnowledgeGraph | Force-directed node-edge graph | react-force-graph-2d | | Map | Globe with markers | @cesium/engine + resium | | Media | Image, video, audio | — | | Document | Markdown/HTML documents | react-markdown | | Code | Syntax-highlighted code blocks | shiki | | Artifact | Sandboxed iframe (HTML/CSS/JS) | — | | Custom | Registry-key based escape hatch | — |

Example: Streaming a Chart

{"createSurface":{"surfaceId":"s1","title":"Sales Dashboard"}}
{"updateDataModel":{"surfaceId":"s1","path":"/sales","value":{"labels":["Q1","Q2","Q3","Q4"],"values":[120,340,250,410]}}}
{"updateComponents":{"surfaceId":"s1","components":[{"id":"chart1","component":"Chart","chartType":"bar","data":"/sales","title":"Quarterly Sales"}]}}

Example: Streaming a Timeline

{"createSurface":{"surfaceId":"s1"}}
{"updateDataModel":{"surfaceId":"s1","path":"/events","value":[{"date":"2024-01-15","title":"Project Started","description":"Kickoff meeting"},{"date":"2024-06-01","title":"v1.0 Release","description":"First public release"}]}}
{"updateComponents":{"surfaceId":"s1","components":[{"id":"t1","component":"Timeline","events":"/events","layout":"alternating"}]}}

Schema-Only Import

For server-side validation or non-React consumers (Python, C#, etc.), import types and validation without React:

import {
  validateMessage,
  validateComponent,
  isA2UIMessage,
  DYNAMIC_CANVAS_CATALOG,
  type A2UIMessage,
  type A2UIComponent,
} from '@atlas.agents/a2ui-canvas/schema';

const msg = { createSurface: { surfaceId: 's1' } };
const result = validateMessage(msg);
// { valid: true, messageType: 'createSurface' }

API Reference

Hooks

useA2UISurface(options?)

Primary hook — connects to a stream and manages surfaces.

interface UseA2UISurfaceOptions {
  sseUrl?: string;           // SSE endpoint
  wsUrl?: string;            // WebSocket endpoint
  streamUrl?: string;        // Fetch stream URL
  streamInit?: RequestInit;  // Fetch init options
  messages?: A2UIMessage[];  // Direct messages
  batchWindow?: number;      // Batch window in ms (default: 50)
  onError?: (error: Error) => void;
  store?: StoreApi<SurfaceManagerStore>; // Shared store
}

Returns: surfaces, activeSurface, setActiveSurface, processMessage, processMessages, resolveData, getComponents, store

useStreamingContent(options)

Subscribe to a single surface's streaming updates.

const { surface, components, dataModel, resolveData, updatedAt } = useStreamingContent({
  store,       // from useA2UISurface
  surfaceId,   // surface to watch
});

Individual Renderer Imports

Tree-shake by importing only what you need:

import ChartRenderer from '@atlas.agents/a2ui-canvas/renderers/chart';
import TimelineRenderer from '@atlas.agents/a2ui-canvas/renderers/timeline';
import KnowledgeGraphRenderer from '@atlas.agents/a2ui-canvas/renderers/knowledge-graph';
import MapRenderer from '@atlas.agents/a2ui-canvas/renderers/map';
import MediaRenderer from '@atlas.agents/a2ui-canvas/renderers/media';
import DocumentRenderer from '@atlas.agents/a2ui-canvas/renderers/document';
import CodeRenderer from '@atlas.agents/a2ui-canvas/renderers/code';
import ArtifactRenderer from '@atlas.agents/a2ui-canvas/renderers/artifact';

Content Analyzer

Auto-detect content type from raw text and convert to A2UI messages:

import { ContentAnalyzer } from '@atlas.agents/a2ui-canvas';

// Detect content type
ContentAnalyzer.detectType('```js\nconsole.log("hi")\n```'); // 'code'
ContentAnalyzer.detectType('# Hello World');                  // 'document'
ContentAnalyzer.detectType('2024-01-01 Project Start');       // 'timeline'

// Convert to A2UI messages
const messages = ContentAnalyzer.toA2UIMessages('```python\nprint("hello")\n```');
// Returns: [createSurface, updateComponents with Code component]

Theming

import { themes } from '@atlas.agents/a2ui-canvas';

// Built-in themes
themes.light
themes.dark
themes.default

// Custom theme
const myTheme = {
  colors: {
    background: '#0a0a0a',
    surface: '#1a1a2e',
    primary: '#0ea5e9',
    secondary: '#6366f1',
    text: '#e2e8f0',
    muted: '#64748b',
    border: '#334155',
    highlight: '#1e293b',
  },
  // ...spacing, typography, borderRadius, shadows
};

Custom Renderers

Register custom renderers by key:

import { ComponentRegistry } from '@atlas.agents/a2ui-canvas';

const registry = new ComponentRegistry();
registry.register('MyWidget', () => import('./MyWidget'));

// Then in A2UI messages:
{
  updateComponents: {
    surfaceId: 's1',
    components: [{ id: 'w1', component: 'Custom', rendererKey: 'MyWidget', props: { foo: 'bar' } }]
  }
}

Architecture

AI Agent → A2UI JSON → StreamProcessor → SurfaceManager → RendererResolver → React Renderers
               (JSONL/SSE/WS)    (zustand store)     (Suspense + ErrorBoundary)
                                                                    ↓
                                    Chart | Timeline | Graph | Map | Media | Doc | Code | Artifact | Custom
  • StreamProcessor: Parses JSONL, SSE, WebSocket, or direct JSON arrays
  • SurfaceManager: Zustand store managing surfaces, data models, and components
  • RendererResolver: Suspense + ErrorBoundary wrapper for lazy-loaded renderers
  • ComponentRegistry: Maps component type strings to React renderer factories

Bundle Size

| Entry | Gzip | |-------|------| | Schema only (./schema) | ~2 KB | | Core + all renderers (.) | ~8 KB | | Individual renderers | 0.4–2.5 KB each |

Heavy dependencies (D3, Cesium, shiki) are loaded on demand when a component requires them.

Development

npm install
npm run dev          # Start Vite dev server
npm run build        # Build library (ES + CJS)
npm run type-check   # TypeScript check
npm test             # Run tests (124 tests)
npm run test:watch   # Watch mode

License

MIT