npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2025 – Pkg Stats / Ryan Hefner

react-agent-hooks

v1.0.0-beta.30

Published

| Agentic Counter Demo | Agentic Todo Demo

Readme

React Agent Hooks

| Agentic Counter Demo | Agentic Todo Demo | | :---------------------------------------------------------------------------------------------------------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Open in StackBlitz | Open in StackBlitz |

Turn React Hooks into LLM Tools

  • 🪝 Familiar: same semantics as React hooks
  • 🤝 Symbiotic: human interface and Agent interface derived from the same state.
  • 🛡️ Safe: developer controls the schema for Agentic state change.
  • ➕ Incremental adoption: use as much or as little as you want.
  • 📦 Composable: fully interoperable with classic React hooks.
  • 🔮 Future-ready: forward-compatible with MCP and llms.txt.

Before

import { useCallback, useState } from "react";

function MyComponent() {
  const [name, setName] = useState("John Doe");
  const [age, setAge] = useState(30);
  const adjust = useCallback((delta) => setAge((prev) => prev + delta), []);

  return (
    <div>
      <h1>{name}</h1>
      <p>{age}</p>
      <button onClick={() => adjust(-1)}>Be younger</button>
      <button onClick={() => adjust(1)}>Be older</button>
    </div>
  );
}

After

import { useAgent, useAgentState, useAgentTool } from "react-agent-hooks";

export function MyComponent() {
  const agent = useAgent({ apiKey: "******" });
  const [name, setName] = useAgentState("Name", "John Doe");
  const [age, setAge] = useAgentState("Age", 30);
  const adjust = useCallback((delta) => setAge((prev) => prev + delta), []);
  useAgentTool("Change age", z.number().describe("the delta of age change"), adjust);

  return (
    <div>
      <h1>{name}</h1>
      <p>{age}</p>
      <button onClick={() => agent.run("be younger")}>Be younger</button>
      <button onClick={() => agent.run("be older")}>Be older</button>
    </div>
  );
}

Open in StackBlitz

Get Started

npm install react-agent-hooks

Usage

Give Agent "Eyes" 👀

import { useAgentMemo } from "react-agent-hooks";

function MyComponent() {
  const [name, setName] = useState("John Doe");
  const [age, setAge] = useState(30);

  // Describe a readable state to the Agent
  useAgentMemo("User's profile", () => ({ name, age }), [name, age]);

  return (
    <div>
      <h1>{name}</h1>
      <p>{age}</p>
    </div>
  );
}

Give Agent "Hands" 👏

import {z} from "zod";
import { useAgentState, useAgentTool } from "react-agent-hooks";

function MyComponent() {

  // Describe a readable state to the Agent while exposing a setter function to developer
  const [foodPreferences, setFoodPreferences] = useAgentState("food preference", ["Pizza", "Sushi"]);

  // Wrap the setter as a tool and describe it to the Agent
  const addFoodPreference = useAgentTool("add-food-preference", z.object(foodItems: z.array(z.string())), (foodItems) => {
    setFoodPreferences((prev) => [...prev, ...foodItems]);
  });
  const removeFoodPreference = useAgentTool("remove-food-preference", z.object(foodItems: z.array(z.string())), (foodItems) => {
    setFoodPreferences((prev) => prev.filter((item) => !foodItems.includes(item)));
  });

  return <ul>
    {foodPreferences.map(item => <li key={item}>{item}</li>)}
    </ul>
}

Run the Agent

import { useAgent } from "react-agent-hooks";

function MyApp() {
  // Run the Agent with a prompt
  // Agent always sees the latest states from `useAgentState`, `useAgentMemo`, and can uses the latest tools from `useAgentTool`
  const agent = useAgent({ apiKey: "******" });

  // Call the Agent
  const handleFormSubmit = (e) => {
    e.preventDefault();
    const input = e.target.elements[0].value;
    agent.run(input);
  };

  return (
    <form onSubmit={handleFormSubmit}>
      <input type="text" placeholder="Your request" />
      <button onClick={handleRunAgent}>Ask Agent</button>
    </form>
  );
}

Compose Agentic Application

Inside a component, use the enabled option to dynamically show/hide states and tools to the Agent.

const shouldShowFeature = true; // You can dynamically decide this value
useAgentMemo("User's profile", () => ({ name, age }), [name, age], { enabled: shouldShowFeature });
useAgentState("some state", { name: "Some state" }, { enabled: shouldShowFeature });
useAgentTool(
  "update state",
  z.object({ name: z.string() }),
  (newState) => {
    setSomeState(newState);
  },
  { enabled: shouldShowFeature },
);

In a component tree, use JSX to dynamically show/hide states and tools to the Agent.

function ParentComponent() {
  // A higher level component can dynamically decide what lower level states/tools are available
  const = [shouldShowFeature, setShouldShowFeature] = useAgentState("toggle feature", z.boolean(), true);

  useAgentTool("toggle feature", z.object({}), () => setShouldShowFeature(prev) => !prev);

  return <AppRoot>{shouldShowFeatureB ? <ChildComponent /> : null}</AppRoot>;
}

function ChildComponent() {
  // The state and tool will be available to the Agent only if the child component is rendered
  useAgentState("some state", { name: "Some state" });
  useAgentTool("update state", z.object({ name: z.string() }), (newState) => {
    setSomeState(newState);
  });

  return <div>...</div>;
}

Build a custom Agent

Access currently active states and tools with useAgentContext hook. Here is an example of building your own agent

export function useMyAgent() {
  const openai = new OpenAI({ dangerouslyAllowBrowser: true, apiKey: "******" });
  const agentContext = useAgentContext();

  const run = async (prompt: string) => {
    const task = openai.chat.completions.runTools({
      stream: true,
      model: "gpt-4.1",
      messages: [
        {
          role: "system",
          content: `
User is interacting with a web app in the following state:
\`\`\`yaml
${agentContext.stringifyStates()}
\`\`\`

Based on user's instruction or goals, either answer user's question based on app state, or use one of the provided tools to update the state.
Short verbal answer/confirmation in the end.
          `.trim(),
        },
        {
          role: "user",
          content: prompt,
        },
      ],
      tools: agentContext.getTools(),
    });

    return task;
  };

  return {
    run,
  };
}

Scale-up with Context

The AgentContext is an optional React Context to help you hierarchically organizing states and tools. This prevents naming collisions and reduces agent confusion from too many similar states and tools.

import { AgentContext } from "react-agent-hooks";

function MyApp() {
  return (
    <AgentContext name="app root">
      <AgentContext name="nav">
        <Nav />
      </AgentContext>
      <AgentContext name="content">
        <header>
          <AgentContext name="header">
            <HeaderContent />
          </AgentContext>
        </header>
        <main>
          <AgentContext name="main">
            <MainContent />
          </AgentContext>
        </main>
      </AgentContext>
    </AgentContext>
  );
}

function HeaderContent() {
  // The Agent will see this state appear within the "app root > nav > header" context
  const [someState, setSomeState] = useAgentState("some state", { name: "Some state" });
  return <div>...</div>;
}

Future Work

Render to MCP Server

import { renderToMCPServer } from "react-agent-hooks";

function main() {
  // Spin up an MCP server at port 3000.
  // React Agent state -> MCP resource and prompts
  // React Agent tools -> MCP tools
  const server = renderToMCPServer(<App />).listen(3000);
}

Render to llms.txt

import { renderToLlmsTxt } from "react-agent-hooks";

function main() {
  server.get("/llms.txt", (req, res) => {
    const userContext = req.query.userContext;
    const llmsTxtContent = renderToLlmsTxt(<App context={userContext} />);
    res.send(llmsTxtContent);
  });
}

Reference

Blog article: React (hooks) is All You Need