AG-UI React Native

April 24, 2026 · View on GitHub

A headless React Native runtime that converts AG-UI event streams into UI and app actions via a pluggable component and handler system.


Architecture

AG-UI Backend (any)

Transport Layer  (@ag-ui/client)   ← WS / SSE / Mock

Event Normalizer                   ← raw → AGUIEvent

Core Engine      (@ag-ui/core)     ← state machine

Renderer Adapter (@ag-ui/react-native)

Components + Action Handlers       ← your UI

Packages

PackageDescription
@ag-ui/coreEngine, types, registry — zero framework dependencies
@ag-ui/clientWebSocket, SSE, and mock transports
@ag-ui/react-nativeAgentRenderer, AgentProvider, hooks, default components
@ag-ui/adaptersLangGraph + CopilotKit event normalizers
@ag-ui/dev-serverLocal mock backend for development

Quick Start

1 — Install

# From your React Native project
pnpm add @ag-ui/core @ag-ui/client @ag-ui/react-native

2 — Mock mode (no backend needed)

import React, { useMemo } from "react"
import { SafeAreaView } from "react-native"
import { Engine } from "@ag-ui/core"
import { createClient } from "@ag-ui/client"
import { AgentRenderer, ChatInput } from "@ag-ui/react-native"

export default function App() {
  const engine = useMemo(() => new Engine(), [])
  const client = useMemo(() => {
    const c = createClient({ mock: true, mockScenario: "weather" })
    c.connect()
    return c
  }, [])

  return (
    <SafeAreaView style={{ flex: 1 }}>
      <AgentRenderer
        engine={engine}
        client={client}
        ListFooterComponent={<ChatInput />}
      />
    </SafeAreaView>
  )
}

3 — Real backend (WebSocket)

const client = useMemo(() => {
  const c = createClient({ url: "ws://your-backend/ag-ui" })
  c.connect()
  return c
}, [])

4 — Custom components + action handlers

import { useNavigation } from "@react-navigation/native"

function App() {
  const navigation = useNavigation()
  const engine = useMemo(() => new Engine(), [])
  const client = useMemo(() => createClient({ url: "ws://backend" }).connect(), [])

  return (
    <AgentRenderer
      engine={engine}
      client={client}
      components={{
        TEXT_MESSAGE: MyBubble,       // override built-in
        STOCK_CHART: StockChartCard,  // custom event type
      }}
      handlers={{
        NAVIGATE: ({ screen, params }) => navigation.navigate(screen, params),
        OPEN_MODAL: ({ id }) => openModal(id),
        SHOW_TOAST: ({ message, variant }) => Toast.show(message, variant),
      }}
      ListFooterComponent={<ChatInput placeholder="Ask anything…" />}
    />
  )
}

Core Concepts

Engine

The Engine is a state machine that consumes AG-UI events and maintains UI state. It's framework-agnostic — usable in React Native, web, CLI, or tests.

import { Engine } from "@ag-ui/core"

const engine = new Engine()

// Subscribe to state changes
const unsub = engine.subscribe((state) => {
  console.log(state.messages)   // accumulated messages
  console.log(state.toolCalls)  // tool call lifecycle
  console.log(state.status)     // "idle" | "streaming" | "error"
})

// Feed events manually (or via client.subscribe)
engine.consume({ type: "RUN_STARTED", payload: { runId: "r1" } })

unsub() // clean up

Client

Handles transport, reconnection, and event normalization.

import { createClient } from "@ag-ui/client"

// WebSocket
const wsClient = createClient({ url: "ws://backend/stream" })

// SSE
const sseClient = createClient({
  url: "https://backend/stream",
  transport: "sse",
})

// Mock — no backend
const mockClient = createClient({ mock: true, mockScenario: "weather" })

// Wire client → engine
wsClient.subscribe(engine.consume)
wsClient.connect()

// Send user input
wsClient.send("Show me the weather in Tokyo")

Component Registry

Maps event type strings to React components. Built-in defaults are provided; override any or add new types.

const components = {
  TEXT_MESSAGE: MyTextBubble,     // replaces built-in TextMessageBubble
  TOOL_CALL: MyToolCard,          // replaces built-in ToolCallCard
  STOCK_CHART: StockChartWidget,  // brand-new custom type
}

Action Handler System

The key differentiator. Handlers intercept events and execute side-effects instead of rendering UI.

const handlers = {
  NAVIGATE: ({ screen, params }) => navigation.navigate(screen, params),
  OPEN_MODAL: ({ id, props }) => openModal(id, props),
  CLOSE_MODAL: () => closeModal(),
  SHOW_TOAST: ({ message, variant }) => showToast(message, variant),
  REFRESH_FEED: () => feedRef.current?.refresh(),
}

The renderer checks: handler present → execute, no handler → render component.

Hooks

// Inside any component under <AgentProvider> or <AgentRenderer>
function ChatInput() {
  const { send, isStreaming, state, error } = useAgentUI()

  return (
    <TextInput
      editable={!isStreaming}
      onSubmitEditing={(e) => send(e.nativeEvent.text)}
    />
  )
}
// Direct engine subscription outside of a provider
const state = useEngineState(engine)

AGUIEvent Schema

type AGUIEvent = {
  type: string                    // e.g. "TEXT_MESSAGE_CONTENT"
  payload: Record<string, any>   // event-specific data
  meta?: {
    id?: string
    timestamp?: number
    runId?: string
    threadId?: string
  }
}

Built-in Event Types

EventPayloadBehavior
RUN_STARTED{ runId, threadId? }sets status → streaming
RUN_FINISHED{ runId }sets status → idle
RUN_ERROR{ runId, message, code? }sets status → error
TEXT_MESSAGE_START{ messageId, role }opens streaming message
TEXT_MESSAGE_CONTENT{ messageId, delta }appends delta to message
TEXT_MESSAGE_END{ messageId }closes streaming message
TOOL_CALL_START{ toolCallId, toolName }opens tool call
TOOL_CALL_ARGS{ toolCallId, delta }streams tool args
TOOL_CALL_END{ toolCallId }closes tool call
TOOL_RESULT{ toolCallId, result }attaches result to tool call
NAVIGATE{ screen, params? }action — no default component
OPEN_MODAL{ id, props? }action — no default component
SHOW_TOAST{ message, variant?, duration? }action — no default component

Backend Adapters

LangGraph

import { createClient } from "@ag-ui/client"
import { langGraphClientOptions } from "@ag-ui/adapters/langgraph"

const client = createClient({
  url: "http://localhost:2024/stream",
  ...langGraphClientOptions,
})

CopilotKit

import { createClient } from "@ag-ui/client"
import { copilotKitClientOptions } from "@ag-ui/adapters/copilotkit"

const client = createClient({
  url: "https://your-copilotkit-endpoint/stream",
  ...copilotKitClientOptions,
})

Custom Transform

const client = createClient({
  url: "ws://your-custom-backend",
  transform: (raw) => ({
    type: raw.event_name,
    payload: raw.data,
    meta: { timestamp: raw.ts },
  }),
})

Dev Server

A local mock SSE backend — no AI service required.

# From packages/dev-server
pnpm start

# Or via CLI
npx ag-ui-dev

# Custom port + scenario
PORT=3333 SCENARIO=weather npx ag-ui-dev

Endpoints:

MethodPathDescription
POST/stream?scenario=weatherSSE stream
GET/scenarioslist available scenarios
GET/healthhealth check

Connect your app to it:

const client = createClient({
  url: "http://localhost:4000/stream",
  transport: "sse",
})

Mock Scenarios

Built into both @ag-ui/client (mock transport) and @ag-ui/dev-server:

ScenarioDescription
greetingSimple streaming text response
weatherText → tool call → result → follow-up text
navigationEmits a NAVIGATE action handler event
errorSimulates a backend RUN_ERROR

Extensibility

Custom Event Types

Define a component for any event type string:

// Backend sends: { type: "STOCK_CHART", payload: { ticker: "AAPL", data: [...] } }

components={{
  STOCK_CHART: ({ ticker, data }) => <StockChart ticker={ticker} data={data} />,
}}

Plugin System (future)

// Planned API
engine.use(chartPlugin)
engine.use(offlinePlugin)

Development

# Install all dependencies
pnpm install

# Build all packages
pnpm build

# Type-check all packages
pnpm typecheck

# Start dev server
pnpm --filter @ag-ui/dev-server start

Non-Goals

  • LLM integration
  • Tool execution
  • Backend orchestration
  • Opinionated UI design / theming

Roadmap

  • Streaming optimization (batched renders)
  • Offline queue support
  • Devtools event inspector (React Native Debugger integration)
  • Visual UI builder
  • Web renderer (@ag-ui/react)
  • CLI renderer (@ag-ui/ink)
  • Plugin system