How It Works

Claude in Browser lets you chat with Claude in a web page — no setup, no API keys, no cloud accounts. If Claude Code works in your terminal, this works too.

You type a message
→
Browser the chat app
→
Server the middleman
→
Claude the brain
Everything runs on your computer. The browser talks to a small local server, and that server talks to the same Claude you already use in the terminal. No external services to configure.

What happens when you send a message?

You press Enter
Your message appears instantly in the chat. Behind the scenes, it's sent to the local server.
The server starts Claude
The server runs the same Claude command you'd type in your terminal, passing it your message.
Claude thinks
A pulsing cursor appears in the chat while Claude generates a response. Words start appearing as soon as the first ones are ready.
The response streams in
Text flows back through the server to your browser in real time. You see it build up word by word, just like in a real conversation.

Why does it feel instant?

Because everything is local. The server is running on your own computer, so there's no network delay between the browser and the server. The only real wait is for Claude to think — and the response starts appearing the moment the first words are ready, not after the entire reply is finished.

How does it know who you are?

When you set up Claude Code (the terminal tool), it created a login session on your computer. This app reuses that exact same session. The server simply asks Claude Code to handle your message, and Claude Code already has your credentials. That's why there's nothing to configure.

The pieces

Chat Interface
Dark-themed chat window. Purple bubbles for you, dark cards for Claude. Smooth animations throughout.
Local Server
Lightweight background program that relays messages between the browser and Claude. Always-on connection.
Claude CLI
The same Claude you use in the terminal. Already logged in, already set up. Nothing new to install.
WebSocket
A persistent two-way channel between browser and server. Like a phone call — always open, always ready.

Troubleshooting

"Connecting..." won't go away
The server probably isn't running. Open a terminal, navigate to the project folder, and run npm run dev.
Messages aren't sending
Check the green dot in the top bar. If it's red, the connection dropped — it'll try to reconnect automatically in a few seconds.
Claude isn't responding
Make sure Claude Code works in your terminal first. If you can type claude --version and get a response, this app will work too.

Technical Architecture

How Claude in Browser connects your browser to the Claude CLI via WebSocket and process spawning.

Browser :5214
WebSocket ←——→
Express :4820
stdin / stdout ←——→
claude CLI -p --stream-json
No API key required. The server spawns claude -p which reuses your existing Claude Code OAuth session.

Message Flow

  1. User sends a message
    The Zustand store creates a user message and an empty assistant placeholder, sets isStreaming: true, and sends the full conversation history over the WebSocket as { type: "chat", messages: [...] }.
  2. Server spawns the CLI
    handleChat() formats the conversation, then spawns:
    claude -p --verbose --output-format stream-json
    Writes the prompt to stdin, closes it. The -p flag is pipe mode — stdin in, stdout out.
  3. Streaming response
    stdout emits line-delimited JSON events. On each assistant event, the server extracts the text delta (new text since last send) and forwards it as { type: "text", text: "..." }. On result, sends { type: "done" }.
  4. Browser renders progressively
    The store appends each text chunk to the assistant message. React re-renders the MessageBubble, transitioning from a pulsing cursor to growing text.

Frontend Components

FilePurpose
main.tsxEntry point — renders BrowserChat into #root
index.cssDesign tokens, base styles, dark theme
claude-in-browser/index.tsxChat view — message list, auto-scroll, empty state, typing indicator
claude-in-browser/store.tsZustand store — WebSocket, messages, streaming state
components/ChatInput.tsxAuto-resizing textarea, Enter-to-send
components/MessageBubble.tsxUser/assistant bubbles, pulsing cursor while streaming
components/StatusBar.tsxConnection status, clear button, initiates WebSocket on mount

Backend Server

FilePurpose
server/index.tsExpress + WebSocket server, spawns claude -p per chat request
server/run-claude.shShell wrapper — strips Claude env vars to avoid nested session detection

Environment Variable Stripping

When running inside Claude Code, env vars like CLAUDECODE are set. The CLI detects these and refuses to run (nested session protection). The server strips them before spawning:

unset CLAUDECODE CLAUDE_CODE CLAUDE_CODE_ENTRYPOINT CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS

Vite Proxy

Both HTTP and WebSocket traffic are proxied through Vite to avoid CORS:

PathTargetProtocol
/api/*http://localhost:4820HTTP
/ws/*ws://localhost:4820WebSocket

Process Lifecycle

Limitations

Not true token streaming
claude -p emits the full response in one assistant event rather than token-by-token. Streaming granularity depends on CLI buffering.
Simulated multi-turn
Conversation history is formatted as a text prompt, not native API multi-turn messages.
Single user
No session management. One conversation at a time on one machine.

Ports

ServicePortNotes
Express + WebSocket4820Backend API
Vite dev server5214Frontend, proxies to 4820