---
title: "A Web Dashboard"
description: "Run the dispatcher behind a browser dashboard. Understand how withEve mounts the agent same-origin and how useEveAgent drives it, then theme it for the shop."
canonical_url: "https://vercel.com/academy/building-agents-with-eve/a-web-dashboard"
md_url: "https://vercel.com/academy/building-agents-with-eve/a-web-dashboard.md"
docset_id: "vercel-academy"
doc_version: "1.0"
last_updated: "2026-08-08T22:50:09.116Z"
content_type: "lesson"
course: "building-agents-with-eve"
course_title: "Building Agents with eve"
prerequisites:  []
---

<agent-instructions>
Vercel Academy — structured learning, not reference docs.
Lessons are sequenced.
Adapt commands to the human's actual environment (OS, package manager, shell, editor) — detect from project context or ask, don't assume.
The lesson shows one path; if the human's project diverges, adapt concepts to their setup.
Preserve the learning goal over literal steps.
Quizzes are pedagogical — engage, don't spoil.
Quiz answers are included for your reference.
</agent-instructions>

# A Web Dashboard

# A Web Dashboard

You've talked to the dispatcher in a terminal and over `curl`. Neither is something you'd hand a customer. The front door most people expect is a web page: a text box, a conversation, a send button.

The browser chat uses the same HTTP session API you drove by hand in 1.3. The generated web channel gives those routes a customer-facing interface while leaving the dispatcher unchanged. Two pieces handle the integration: `withEve` wires the agent's routes into a Next.js app, and `useEveAgent` drives them from React.

In this lesson, you'll inspect the generated chat UI and give it the shop's identity. The next two lessons add Slack and real authentication.

## Outcome

The dispatcher answers in a browser dashboard, approvals included, and the page wears the Spoke & Mirror name instead of the scaffold's default.

## Hands-on exercise

**Add the web channel.** One command scaffolds the whole front end:

```bash
npx eve channels add web
```

That command generates a Next.js app around your agent, including a `next.config.ts` that mounts the routes and an `app/` directory with the React chat. Run `git status` afterward to see what it added. File paths can shift between eve versions, so use your generated tree as the source of truth.

**How the door is wired.** The generated `next.config.ts` wraps your config with `withEve`:

```ts title="next.config.ts"
import type { NextConfig } from "next";
import { withEve } from "eve/next";

const nextConfig: NextConfig = {};

export default withEve(nextConfig);
```

`withEve` mounts the agent's `/eve/v1/*` routes on the Next.js app's origin. The browser can reach the agent without crossing a CORS boundary or reading an environment variable. In development, `npm run dev` starts the eve runtime alongside `next dev` and forwards those routes.

\*\*Warning: npm run dev changed meaning\*\*

Until now you ran the agent with `npx eve dev`, the terminal UI. Now that there's a web app, `npm run dev` runs `next dev` and serves the dashboard at `localhost:3000`. You can still open the TUI with `npx eve dev`; `npm run dev` now defaults to the web app.

**Now, how the browser drives it.** Open the generated chat component. It leans on one hook:

```tsx
const agent = useEveAgent();
```

That single call opens a durable session, sends turns, streams replies, and hands you render-ready state. The component reads from it:

- `agent.data.messages`, the conversation (an `EveMessage[]` following the AI SDK `UIMessage` convention), mapped to message bubbles.
- `agent.status`, `"ready" | "submitted" | "streaming" | "error"`, used to disable the composer while the agent is working.
- `agent.send({ message })`, to send a turn. The same `send` carries your answer back when the agent stops for an approval.

The approval gate you built in 3.2 shows up here automatically. When `book_repair` parks on a big booking, the message renders approve/deny controls, and clicking one sends your answer back through the hook. You wrote zero approval-UI code; `useEveAgent` surfaces the parked request and the generated component renders it.

**Make it the shop's.** The generated app names the agent after your project. Give it the shop's identity instead. Open the generated chat component, `app/_components/agent-chat.tsx` in this eve version, and rename the agent (the constant the scaffold uses for the display name):

```tsx
const AGENT_NAME = "Spoke & Mirror";
```

Then set the page metadata in the app's `layout.tsx`:

```tsx
export const metadata: Metadata = {
  title: "Spoke & Mirror Dispatcher",
  description: "Book bike repairs at Spoke & Mirror Cyclery.",
};
```

\*\*Note: Match your generated files\*\*

The exact paths and the display-name constant come from whatever `eve channels add web` generated for your eve version. If your component names the agent differently, rename whatever it actually uses. A quick `git status` plus a grep for your project name points you at both spots.

## Try It

Boot the dashboard:

```bash
npm run dev
```

Open `http://localhost:3000`. You'll see the Spoke & Mirror chat: a header with the shop's name and a message box at the bottom. Click into the box, type a real customer question, and press Enter:

```text
my rear brake feels spongy on the way down the hill
```

The reply streams into the page in the front-desk voice you wrote in 1.1. The agent is now answering in a browser without changes to any tool.

Now run it through the rest of its paces:

1. Ask "what's a brake bleed cost?" The `lookup_service` tool runs and returns the same catalog price as the TUI.
2. Ask it to book a flat repair. The booking goes straight through.
3. Ask for the Full Overhaul. The approval gate from 3.2 appears as **approve / deny buttons** in the chat. Click approve to resume the turn and complete the booking.

Only the surface changed. The dispatcher, tools, and approval logic stayed in place.

\*\*Note: The approval crossed surfaces unchanged\*\*

You wrote `approval` once in the tool. The TUI rendered it as a text prompt, while the browser used buttons. Each channel can render the same tool behavior for its platform.

Blank page or a connection error in the console? Make sure you ran `npm run dev`; `npx eve dev` opens the TUI and does not serve the web app. If the chat loads but every message errors, the agent could not reach a model. Use the credential checks from 1.1: `eve link` or `AI_GATEWAY_API_KEY`.

## Done-When

- [ ] `npm run dev` serves the dashboard at `localhost:3000`.
- [ ] The dispatcher answers in the browser and calls tools (you see real prices/slots).
- [ ] An expensive booking renders approve/deny controls, and approving completes it.
- [ ] The page and chat header show "Spoke & Mirror", not the scaffold default.

## Solution

`eve channels add web` did the wiring; `withEve` and `useEveAgent` came with it. Your only edits are the shop's identity, the display name in the chat component:

```tsx
const AGENT_NAME = "Spoke & Mirror";
```

and the page metadata in `layout.tsx`:

```tsx
export const metadata: Metadata = {
  title: "Spoke & Mirror Dispatcher",
  description: "Book bike repairs at Spoke & Mirror Cyclery.",
};
```

The generated channel gives you a working Next.js interface. The dispatcher's tools, state, skill, and approval logic remain unchanged. Next, you'll expose it through Slack.


---

[Full course index](/academy/llms.txt) · [Sitemap](/academy/sitemap.md)
