# React

[`@zap-studio/webmcp-react`](https://www.npmjs.com/package/@zap-studio/webmcp-react) is a separate package with one hook: `useWebMCPTool(tool, deps?)`. It registers a tool with the native WebMCP API on mount, and unregisters it on unmount. It works the same way in Next.js App Router client components and TanStack Start client components — both are React, so no separate adapter is needed per meta-framework.

## Installation

```bash
npm install @zap-studio/webmcp-react @zap-studio/webmcp
```

## Import

```ts
import { useWebMCPTool } from "@zap-studio/webmcp-react";
```

## Basic Usage

```tsx
import { useWebMCPTool } from "@zap-studio/webmcp-react";

function LikeButton({ postId }: { postId: string }) {
  useWebMCPTool(
    {
      name: "posts_like",
      description: "Like a post by ID",
      execute: async ({ id }: { id: string }) => ({ liked: await likePost(id) }),
    },
    [postId],
  );

  return <button onClick={() => likePost(postId)}>Like</button>;
}
```

Registration runs in `useEffect`, so it happens after mount, in the browser only — the hook is safe to render on the server, where it simply registers nothing.

## Changing Tools

`deps` controls when the tool re-registers, exactly like `useEffect`'s dependency array. With the default `[]`, the tool registers once, on mount, using the first render's `tool`:

```tsx
useWebMCPTool(
  {
    name: "posts_like",
    description: "Like a post by ID",
    execute: async ({ id }: { id: string }) => ({ liked: await likePost(id) }),
  },
  [],
);
```

Pass the values `tool` depends on to re-register when they change — for example, when `execute` closes over a prop:

```tsx
useWebMCPTool(
  {
    name: "posts_like",
    description: "Like a post by ID",
    execute: async () => ({ liked: await likePost(postId) }),
  },
  [postId],
);
```

## Unmount / Cleanup

The tool unregisters automatically when the component unmounts, or right before it re-registers on a `deps` change — no manual cleanup needed. If registration is still pending when the component unmounts, `useWebMCPTool` unregisters it as soon as it resolves instead of leaking a dangling tool.

## Handling Errors

Registration failures — most commonly an unsupported browser — are caught internally and surfaced through the returned `error`, not thrown, so a missing agent-callable tool never crashes the component tree:

```tsx
function LikeButton({ postId }: { postId: string }) {
  const { error } = useWebMCPTool(
    {
      name: "posts_like",
      description: "Like a post by ID",
      execute: async ({ id }: { id: string }) => ({ liked: await likePost(id) }),
    },
    [postId],
  );

  return (
    <button onClick={() => likePost(postId)}>Like{error ? " (agent tool unavailable)" : ""}</button>
  );
}
```

## See Also

* [Getting Started](/webmcp/getting-started) — `defineTool`, `registerTool`, and SSR safety
* [Tool Registry](/webmcp/registry) — batch-register tools outside of a component's own lifecycle
* [Errors](/webmcp/errors) — `WebMCPNotSupportedError` and `hasWebMCPSupport`
