Skip to content
AIRN

Driving a React Native UI With LangChain Agents: The Full Agentic Mobile Stack

Malik Chohra

Malik Chohra

May 21, 2026 · 5 min read

Two LangChain agents (a Coach and a Journal-Summarizer) running on a FastAPI server, talking to each other via supervisor routing, with the final response routed back to a React Native app over the A2A protocol. WireAI renders the cards. This is the stack I am running for the PH-1 demo in June 2026.

The short version: two LangChain agents (a Coach and a Journal-Summarizer) on a FastAPI server, talking to each other through supervisor routing, with the final response routed back to a React Native app over Google's A2A protocol. WireAI renders the cards. The user taps. The agents respond. Mobile interface, agent-native loop, no WebView, no LangChain code in the JS bundle. This is the stack I am running for the PH-1 demo in June 2026.

Agent UIs feel different on mobile. Web has reload tolerance, broad screen real estate, browser-native streaming, and an audience used to waiting. Mobile has none of that. Building this stack taught me where the web playbook breaks.

Why does mobile change the agent UX problem?

A web agent app can sit on a tab, stream tokens into a scrolling div, and recover from a 30-second latency spike by showing a spinner. Nobody loves it but the platform absorbs it. Mobile has different physics:

  • Cold start. Apps get backgrounded. The agent state has to survive a tab swap and a sleep cycle.
  • Battery. A 60-second streaming response over LTE drains visibly. Users notice.
  • Network. WiFi to LTE to 4G handoff happens mid-session. The agent loop has to recover.
  • Screen size. A 12-message agent conversation that fits in a web sidebar fills a phone screen three times over. UI density matters.
  • No reload. Web reloads recover from anything. Mobile users force-quit the app and never come back.

The web agent UX answer is "stream tokens into a chat bubble." The mobile answer is closer to "render one decisive card per turn, let the agent decide what comes next, design for tap not type." That is the loop WireAI was built for.

The architecture I am running

React Native app (WireAI)
        │
        │  POST /a2a  (JSON-RPC 2.0)
        ▼
   FastAPI server
        │
   Supervisor agent (LangGraph)
        │
   ┌────┼────┐
   ▼    ▼    ▼
 Coach   Journal-Summarizer   Future agents...

Three pieces, each doing one thing. WireAI on the device renders components, sends user input, receives JSON-RPC responses, displays the next component. No agent logic in the JS bundle. FastAPI plus an A2A endpoint receives the JSON-RPC message and hands it to the LangGraph supervisor. The LangGraph supervisor plus specialists routes between the Coach (next-step guidance) and the Journal-Summarizer (extracts patterns from the user's recent journal entries). The structured response is a WireAI component directive that the mobile app validates via Zod and renders.

Step 1: The mobile side (WireAI with A2A adapter)

import { WireAIProvider, useWireAIThread, WireAIMessageRenderer } from 'wireai-rn';

function CoachScreen() {
  const { messages, sendMessage } = useWireAIThread();
  return (
    <View>
      {messages.map((m) => <WireAIMessageRenderer key={m.id} message={m} />)}
    </View>
  );
}

export default function App() {
  return (
    <WireAIProvider
      adapter={{
        kind: 'a2a',
        endpoint: 'https://coach-agents.fly.dev/a2a',
      }}
    >
      <CoachScreen />
    </WireAIProvider>
  );
}

That is the whole mobile side. The hook handles the JSON-RPC handshake, the A2A capability discovery, and the streaming response. The component arrives as structured JSON, gets validated against a Zod schema, and renders. Worth noticing: the mobile bundle has zero LangChain code. The agent logic lives on the server. The bundle stays small. Hot reload stays fast. App size does not bloat. If you want the message format itself, what each agent response carries and how the client parses it, see A2A protocol payloads explained.

Step 2: The supervisor agent (LangGraph)

The supervisor decides which specialist handles the user's turn. In LangGraph, that is a state graph with conditional edges:

from langgraph.graph import StateGraph, END
from typing import TypedDict

class AgentState(TypedDict):
    user_message: str
    journal_context: str
    next_action: str
    response_component: dict

def route_supervisor(state: AgentState) -> str:
    # Cheap LLM call decides: coach response or journal summary?
    # In practice: gpt-4o-mini with a short routing prompt.
    if needs_journal_context(state["user_message"]):
        return "journal_summarizer"
    return "coach"

graph = StateGraph(AgentState)
graph.add_node("supervisor", supervisor_node)
graph.add_node("coach", coach_node)
graph.add_node("journal_summarizer", journal_summarizer_node)
graph.add_conditional_edges("supervisor", route_supervisor)
graph.add_edge("coach", END)
graph.add_edge("journal_summarizer", "coach")  # summarizer feeds the coach
graph.set_entry_point("supervisor")
app = graph.compile()

The supervisor's job is one cheap LLM call: "does this user message need journal context?" If yes, the summarizer runs first, then the coach. If no, the coach runs alone. I logged supervisor decisions during a week of dogfood usage. The supervisor routes correctly on roughly 88% of turns (in our own logs, not a controlled study). The 12% miss cases are when the user message is genuinely ambiguous, which is fine; the coach handles them passably.

Step 3: The specialist agents

The Coach is a single-turn agent with a system prompt that emits a WireAI component:

COACH_SYSTEM_PROMPT = """
You are a daily coach. Given the user's message and (optional) journal summary,
respond with ONE WireAI component as JSON:

{ "action": "render", "component": "<Name>", "props": {...} }

Available components: ActionCard, ContentSelectCard, ChipSelectCard, StatusCard, TextInputCard.
Always end with something the user can tap or type.
"""

The Journal-Summarizer is a different beast. It fetches the last 7 days of journal entries from the user's local store (via an MCP tool call), runs a summarization pass, and emits a structured summary that the Coach then uses as context. Both agents are LangGraph nodes. Both return structured JSON. The supervisor routes. The final node's output is what FastAPI returns to the mobile app as the A2A response.

Step 4: The FastAPI + A2A glue

A2A is JSON-RPC 2.0 with capability discovery. FastAPI gives you the endpoint:

from fastapi import FastAPI
from pydantic import BaseModel

app = FastAPI()

class A2ARequest(BaseModel):
    jsonrpc: str
    method: str
    params: dict
    id: str

@app.post("/a2a")
async def a2a_endpoint(req: A2ARequest):
    if req.method == "message/send":
        result = await langgraph_app.ainvoke({
            "user_message": req.params["message"]["parts"][0]["text"],
            "journal_context": "",
            "next_action": "",
            "response_component": {},
        })
        return {
            "jsonrpc": "2.0",
            "id": req.id,
            "result": result["response_component"],
        }

That is the smallest version that works. Production code adds streaming via SSE (LangGraph supports astream_events), retry logic, agent capability cards, and authentication. The shape stays the same. LangSmith's Agent Server now ships a hosted /a2a/{assistant_id} endpoint with message/send and message/stream methods if you prefer not to roll your own.

What surprised me in the build

Three things I did not expect. First, the flat component model survives multi-agent routing. I worried that two agents would emit conflicting component types and the mobile app would jitter. It does not, because the supervisor decides which agent runs, and only the final node emits a component. The mobile app sees one component per turn, same as a single-LLM setup.

Second, latency from supervisor routing is small. I assumed adding a routing LLM call would add a noticeable second to every turn. The supervisor uses gpt-4o-mini with a 200-token prompt. It returns in roughly 500ms. The user does not notice. The Coach takes 2 to 4 seconds anyway.

Third, the A2A protocol carries more than just text. The first time I sent a ContentSelectCard props payload (options, descriptions, icons) over A2A, I expected to need a custom adapter. I did not. JSON-RPC 2.0 carries arbitrary payloads, and WireAI's renderer trusts the Zod schema. The protocol was sufficient.

What still does not work

  • Streaming through the LangGraph pipeline. Token-level streaming from the Coach back to the mobile app is wired but fragile. SSE over JSON-RPC has caveats. WireAI 0.2 cleans this up.
  • Tool calling visibility. When the Journal-Summarizer calls an MCP tool, the mobile UI does not currently show "fetching journal context." It just looks idle for 1 to 2 seconds. Need a loading component variant.
  • Offline. No graceful offline mode. If the server is unreachable, the user sees an error. A local fallback agent (on-device Ollama) is on the roadmap.
  • Agent observability on the mobile side. I can see the LangGraph trace in LangSmith. The mobile user cannot. Intentional, but rough for debugging.
  • A2UI protocol. This stack uses A2A (Agent-to-Agent). A2UI (Agent-to-UI) is a deeper integration where the agent directly drives the component renderer beyond single-turn JSON. That arrives in WireAI 0.3 and is the PH-1 headline demo. Details in the A2UI post.

Is this what AI-native mobile becomes?

I do not know yet. What I do know: the stack runs, the demo is shippable, and it feels different from "chat with my LLM" apps. The cards are decisive. The agent picks the next move. The user taps. Less typing, less waiting, less wall of text.

If you are building an AI-native mobile app in 2026, the stack I would start with: WireAI on the device, A2A as the wire, LangGraph on the server. WireAI ships the mobile half. LangChain has the agent half mature enough. The A2A protocol is the glue.

Where to start

npm install wireai-rn@0.1.3 zod

A2A docs at getwireai.com/docs/a2a-protocol. Repo at github.com/chohra-med/wireai-rn (MIT). Full stack runs in the AI Pro tier of the AI Mobile Launcher boilerplate at aimobilelauncher.com as the dogfood, shipped as Morrow Self, our self-improvement app. Weekly mobile-AI issue (including the agent pieces) at codemeetai.substack.com.

Built by Malik Chohra. 7+ years React Native. Shipped DocMorris (9M users, regulated digital health, NFC for electronic health cards), Mindshine (4.3 to 4.9 App Store rating), and ScorePlay (AI spec workflows as App Lead).

FAQ

Can I use LangChain agents directly in a React Native app?

Not the Python LangChain runtime. The mobile app needs a remote agent server it talks to over an HTTP-friendly protocol like A2A or plain REST. WireAI's A2A adapter handles the JSON-RPC handshake. You run LangChain on a Python server (FastAPI is the simplest), and the mobile app stays a thin client.

What is the A2A protocol?

A2A (Agent-to-Agent) is Google's open protocol for inter-agent communication. It uses JSON-RPC 2.0 with capability discovery (AgentCard), structured messages, and a small set of methods (message/send, message/stream, tasks/get). WireAI ships a 532-line A2A adapter that lets a React Native app act as an A2A client to any A2A-compatible server, including LangSmith Agent Server.

How do I connect WireAI to a LangGraph supervisor?

Wrap your LangGraph state machine in a FastAPI /a2a endpoint that accepts JSON-RPC 2.0 requests, invokes the graph, and returns the final node's output as a WireAI component directive. On the mobile side, configure the provider with an A2A adapter pointing at the endpoint. The docs at getwireai.com/docs/a2a-protocol walk through the FastAPI side end to end.

Why not just use a REST API instead of A2A?

You can. A2A buys three things over plain REST: structured agent capability discovery, multi-agent routing semantics, and forward compatibility with the agent protocol ecosystem (MCP, A2UI). For a single-agent app, REST is fine. For multi-agent or future-proofing, A2A pays off.

What is the difference between A2A and A2UI?

A2A is agent-to-agent communication. A2UI is agent-to-UI direct rendering, where agents drive the UI runtime beyond emitting single-turn JSON. WireAI ships A2A in 0.1.3. A2UI lands in 0.3 (PH-1 launch, June 2026). For most agentic apps today, A2A plus WireAI's flat component model is enough.

How do you handle agent latency on mobile?

Three patterns: show a loading component the moment the user taps (do not wait for the agent), keep the supervisor LLM cheap (gpt-4o-mini with a short prompt), and stream tokens for any text-heavy response so the user sees progress. WireAI's streaming layer handles the last piece (Hermes-safe XHR + SSE; details in the Hermes streaming post).