packages/docs/examples/browser.mdx
Run AI agents entirely in the browser. No server required for basic functionality. These examples use in-browser databases and pattern matching to work offline.
| Example | Framework | Directory | Features |
|---|---|---|---|
| HTML | Vanilla JS | examples/html/ | Zero dependencies, CRT terminal UI |
| React | React + Vite | examples/react/ | Full React app with PGLite |
| React WASM | React + Rust WASM | examples/react-wasm/ | Rust core compiled to WASM |
| Next.js | Next.js 14 | examples/next/ | SSR + API routes |
cd examples/react-wasm bun install bun dev
</Tab>
<Tab title="Next.js">
```bash
cd examples/next
bun install
OPENAI_API_KEY="your-key" bun dev
# Visit http://localhost:3000
The simplest browser implementation. Pure HTML, CSS, and JavaScript with a beautiful retro CRT terminal interface.
Features:
<!DOCTYPE html>
<html lang="en">
<head>
<title>ELIZA - elizaOS Browser Demo</title>
<script type="importmap">
{
"imports": {
"@elizaos/core": "../../packages/typescript/dist/browser/index.browser.js",
"@elizaos/plugin-eliza-classic": "../../plugins/plugin-eliza-classic/dist/browser/index.browser.js",
"@elizaos/plugin-localdb": "../../plugins/plugin-localdb/dist/browser/index.browser.js"
}
}
</script>
</head>
<body>
<div id="chat"></div>
<input
type="text"
id="input"
placeholder="Tell me what's troubling you..."
/>
<script type="module">
import { AgentRuntime, ModelType, stringToUuid } from "@elizaos/core";
import { elizaClassicPlugin } from "@elizaos/plugin-eliza-classic";
import { plugin as localdbPlugin } from "@elizaos/plugin-localdb";
// Create runtime
const runtime = new AgentRuntime({
character: {
name: "Eliza",
bio: "A Rogerian psychotherapist simulation.",
},
plugins: [localdbPlugin, elizaClassicPlugin],
});
await runtime.initialize();
// Handle input
document
.getElementById("input")
.addEventListener("keypress", async (e) => {
if (e.key === "Enter") {
const text = e.target.value.trim();
if (!text) return;
// Display user message
appendMessage("You", text);
e.target.value = "";
// Get response
const response = await runtime.useModel(ModelType.TEXT_LARGE, {
prompt: text,
});
appendMessage("Eliza", String(response));
}
});
function appendMessage(sender, text) {
const chat = document.getElementById("chat");
chat.innerHTML += `<div><strong>${sender}:</strong> ${text}</div>`;
chat.scrollTop = chat.scrollHeight;
}
</script>
</body>
</html>
Full React application with PGLite for in-browser PostgreSQL-compatible storage.
Features:
// eliza-runtime.ts
import { AgentRuntime, stringToUuid } from "@elizaos/core";
import { elizaClassicPlugin } from "@elizaos/plugin-eliza-classic";
import { plugin as sqlPlugin } from "@elizaos/plugin-sql";
let runtime: AgentRuntime | null = null;
export async function getRuntime(): Promise<AgentRuntime> {
if (runtime) return runtime;
runtime = new AgentRuntime({
character: {
name: "Eliza",
bio: "A Rogerian psychotherapist simulation.",
},
plugins: [sqlPlugin, elizaClassicPlugin],
});
await runtime.initialize();
return runtime;
}
// App.tsx
import { useState, useEffect, useRef } from "react";
import { getRuntime } from "./eliza-runtime";
import type { AgentRuntime } from "@elizaos/core";
import { ModelType } from "@elizaos/core";
interface Message {
sender: "user" | "eliza";
text: string;
}
export default function App() {
const [messages, setMessages] = useState<Message[]>([]);
const [input, setInput] = useState("");
const [loading, setLoading] = useState(false);
const [runtime, setRuntime] = useState<AgentRuntime | null>(null);
useEffect(() => {
getRuntime().then(setRuntime);
}, []);
const sendMessage = async () => {
if (!input.trim() || !runtime || loading) return;
const userMessage = input.trim();
setInput("");
setMessages((prev) => [...prev, { sender: "user", text: userMessage }]);
setLoading(true);
try {
const response = await runtime.useModel(ModelType.TEXT_LARGE, {
prompt: userMessage,
});
setMessages((prev) => [
...prev,
{ sender: "eliza", text: String(response) },
]);
} catch (error) {
console.error("Error:", error);
} finally {
setLoading(false);
}
};
return (
<div className="terminal">
<div className="chat">
{messages.map((msg, i) => (
<div key={i} className={`message ${msg.sender}`}>
<strong>{msg.sender === "user" ? "YOU" : "ELIZA"}:</strong>
<span>{msg.text}</span>
</div>
))}
{loading && <div className="typing">ELIZA is typing...</div>}
</div>
<input
value={input}
onChange={(e) => setInput(e.target.value)}
onKeyPress={(e) => e.key === "Enter" && sendMessage()}
placeholder="Tell me what's troubling you..."
disabled={!runtime || loading}
/>
</div>
);
}
Uses the Rust implementation compiled to WebAssembly for the best performance.
Features:
// eliza-runtime.ts (WASM version)
import init, { WasmAgentRuntime } from "@elizaos/core/rust";
let runtime: WasmAgentRuntime | null = null;
export async function getRuntime(): Promise<WasmAgentRuntime> {
if (runtime) return runtime;
// Initialize WASM module
await init();
// Create runtime with character JSON
runtime = await new WasmAgentRuntime(
JSON.stringify({
name: "Eliza",
bio: "A Rogerian psychotherapist simulation.",
}),
);
await runtime.initialize();
return runtime;
}
Full-stack Next.js application with server-side rendering and API routes.
Features:
// app/api/chat/route.ts
import { NextResponse } from "next/server";
import { AgentRuntime } from "@elizaos/core";
import { openaiPlugin } from "@elizaos/plugin-openai";
import { plugin as sqlPlugin } from "@elizaos/plugin-sql";
let runtime: AgentRuntime | null = null;
async function getRuntime() {
if (runtime) return runtime;
runtime = new AgentRuntime({
character: {
name: "Eliza",
bio: "A helpful AI assistant.",
secrets: {
OPENAI_API_KEY: process.env.OPENAI_API_KEY,
},
},
plugins: [sqlPlugin, openaiPlugin],
});
await runtime.initialize();
return runtime;
}
export async function POST(request: Request) {
const { message } = await request.json();
const runtime = await getRuntime();
// Stream response
const encoder = new TextEncoder();
const stream = new ReadableStream({
async start(controller) {
await runtime.messageService?.handleMessage(
runtime,
createMessage(message),
async (content) => {
if (content?.text) {
controller.enqueue(encoder.encode(content.text));
}
return [];
},
);
controller.close();
},
});
return new Response(stream, {
headers: { "Content-Type": "text/plain; charset=utf-8" },
});
}
| Feature | Chrome | Firefox | Safari | Edge |
|---|---|---|---|---|
| ES Modules | ✅ | ✅ | ✅ | ✅ |
| Import Maps | ✅ | ✅ | ✅ | ✅ |
| WASM | ✅ | ✅ | ✅ | ✅ |
| IndexedDB (PGLite) | ✅ | ✅ | ✅ | ✅ |
| localStorage | ✅ | ✅ | ✅ | ✅ |
| Plugin | Requires API | Offline | Response Time |
|---|---|---|---|
plugin-eliza-classic | ❌ | ✅ | ~1ms |
plugin-openai | ✅ | ❌ | ~500-2000ms |
plugin-anthropic | ✅ | ❌ | ~500-2000ms |