Frontend Tools

Let your agent interact with and update your application's UI.

Open your coding agent in your project's folder, or in an empty folder for a new app.This runs in a coding agent on your computer.

See this in Inspector

Open Inspector on localhost. Go to Agents, then Frontend Tools. Your tool and its schema are listed.

More detail: Inspector.

What is this?#

Frontend tools let your agent define and invoke client-side functions that run entirely in the user's browser. Because the handler executes on the frontend, it has direct access to component state, browser APIs, and any third-party UI library the page already uses. That's how an agent can "reach into" the app: update React state, trigger animations, read localStorage, pop a toast, or steer the user's view.

This page covers the "agent drives the UI" shape of frontend tools. The same primitive also powers Generative UI and Human-in-the-loop; see those pages for interaction patterns.

When should I use this?#

Use frontend tools when your agent needs to:

  • Read or modify React component state
  • Access browser APIs like localStorage, sessionStorage, or cookies
  • Trigger UI updates, animations, or transitions
  • Show alerts, toasts, or notifications
  • Interact with third-party frontend libraries
  • Perform anything that requires the user's immediate browser context

How it works in code#

Register a frontend tool with useFrontendTool. Give it a name, a Zod schema for parameters, and a handler. The agent can then call it like any other tool and your frontend runs it in the browser.

The handler receives the parsed, type-safe parameters and can do anything the browser can: update state, call an API, touch the DOM. Its return value is sent back to the agent as the tool result so the model can reason about what happened.

Registering a list of tools#

useFrontendTool registers one tool per call, so it cannot be called in a loop over a list whose length changes between renders. When the set of tools comes from state, from props, or from a backend response, use useFrontendTools instead. It takes an array and runs a single effect over it, so the array can be empty on one render and hold twenty entries on the next.

useFrontendTools(
  reports.map((report) => ({
    name: `open_${report.id}`,
    description: `Open the ${report.title} report`,
    handler: async () => navigate(`/reports/${report.id}`),
  })),
  [navigate],
);

Tools that leave the array are unregistered, tools that join it are registered, and a re-render that produces an equal list does not re-register anything. A description built from your data stays current on its own. The second argument is for values a handler closes over, such as navigate above.

Choose your AI backend

See Integrations for all available frameworks (frontend-tools).