Guide
React Integration
Add Traverse capabilities to your React app. You do not need to know Rust. The browser adapter handles all capability execution. Your app only needs to make HTTP requests.
v0.7.0
~20 minutes
Node.js 18+
Rust 1.94+
Start the browser adapter
The browser adapter is a local HTTP server. It takes requests from your React app, runs the WASM capability, and returns a structured response. Start it first, before your React dev server.
$ cargo run -p traverse-cli -- browser-adapter serve \
--bind 127.0.0.1:4174 \
--bundle ./bundles/expedition
Loading bundle: ./bundles/expedition
plan-expedition v1.0.0 ... ok
Registry ready. 1 capability loaded.
Adapter listening on 127.0.0.1:4174
Leave this running. Open a second terminal for the next step.
Run the React demo
The repo includes a working React demo. Use it to see a complete integration before adding Traverse to your own app.
$ node apps/react-demo/server.mjs
React demo ready at http://localhost:3000
Open http://localhost:3000 in your browser. Submit the expedition form to see a live execution and trace output.
Both processes must be running. The adapter on port 4174 and the React server on port 3000. The demo shows a blank page if the adapter is not up.
How the connection works
Your React app never touches Rust or WASM directly. It talks to the browser adapter over plain HTTP. The adapter owns everything else.
React app → POST /execute → Browser adapter :4174
JSON body: goal, requested_target, caller
↓
Traverse runtime
↓
WASM capability
↓
Browser adapter :4174 → JSON response → React app
status, output, trace_id, events_emitted
The adapter exposes two endpoints: POST /execute for running capabilities and GET /health for liveness checks.
Submit a governed request from React
Use any HTTP client. The body is a JSON object with three fields: goal, requested_target, and caller.
const executeCapability = async (goal) => {
const response = await fetch('http://127.0.0.1:4174/execute', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
goal: goal,
requested_target: 'local',
caller: 'my-react-app'
})
});
if (!response.ok) {
throw new Error(`Adapter error: ${response.status}`);
}
return response.json();
};
The goal is a plain language description. The runtime matches it to the best capability in the loaded bundle. The caller field identifies your app in trace records.
requested_target tells the runtime which placement to prefer. Use local in v0.7.0. Other targets (browser, edge, cloud, ai-pipeline) are specified but not yet fully implemented.
Reading the response
A successful response is a JSON object. Check status first, then read output.
{
"status": "completed",
"trace_id": "trc_7f3a1b",
"capability": "plan-expedition@1.0.0",
"output": {
"plan": "Day 1: Ascent to base camp at 2800m...",
"equipment_list": ["crampons", "ice axe", "tent"]
},
"events_emitted": ["expedition.planned"],
"duration_ms": 143
}
status
completed or error. Always check this before reading output. On error, an error object is present with reason and stage.
trace_id
Unique execution ID. Store it if you want to correlate frontend events with backend traces.
capability
The ID and version of the capability that ran. Useful for logging and debugging.
output
The capability's output payload. Shape is defined by the contract's outputs schema. Will vary by capability.
events_emitted
List of event IDs published during execution. Empty array if none.
duration_ms
Total execution time in milliseconds. Useful for displaying in developer tooling.
Handling streaming updates
The adapter exposes a server-sent events stream at /events. Subscribe to it to receive execution lifecycle events in real time without polling.
const subscribeToEvents = () => {
const source = new EventSource('http://127.0.0.1:4174/events');
source.addEventListener('execution', (event) => {
const data = JSON.parse(event.data);
console.log('State transition:', data.state);
});
source.addEventListener('error', () => {
source.close();
});
return () => source.close();
};
Each SSE message includes the trace_id so you can match stream events to the response from /execute. Use this to show a live progress indicator during long-running capabilities.
Displaying trace data in your UI
The trace data in the response gives you everything you need to build a developer-facing execution panel. Here is a minimal React component that renders the key fields.
function TracePanel({ result }) {
if (!result) return null;
return (
<div className="trace-panel">
<div className="trace-row">
<span>Status</span>
<span className={result.status === 'completed' ? 'success' : 'error'}>
{result.status}
</span>
</div>
<div className="trace-row">
<span>Capability</span>
<span>{result.capability}</span>
</div>
<div className="trace-row">
<span>Trace ID</span>
<code>{result.trace_id}</code>
</div>
<div className="trace-row">
<span>Duration</span>
<span>{result.duration_ms}ms</span>
</div>
{result.events_emitted.length > 0 && (
<div className="trace-row">
<span>Events</span>
<span>{result.events_emitted.join(', ')}</span>
</div>
)}
</div>
);
}
Show the trace panel in a collapsible section alongside your main output. It gives developers immediate visibility into what ran, which version, and how long it took.
No Rust required on your side. Your React app is a plain HTTP client. The browser adapter handles registry lookups, constraint evaluation, WASM execution, and event emission. You only deal with JSON.