Files
wehub-resource-sync e30e75b5d4
Code Quality / Oxlint + Oxfmt (push) Waiting to run
Code Quality / Template Sync (push) Waiting to run
Code Quality / Build Changed Packages (push) Waiting to run
Code Quality / Test Changed Packages (push) Waiting to run
Deploy Expo Example / Deploy Production (push) Waiting to run
Deploy Ink Example / Deploy Production (push) Waiting to run
Python Tests / pytest (assistant-stream, 3.10) (push) Waiting to run
Python Tests / pytest (assistant-stream, 3.12) (push) Waiting to run
Python Tests / pytest (assistant-ui-sync-server-api, 3.10) (push) Waiting to run
Python Tests / pytest (assistant-ui-sync-server-api, 3.12) (push) Waiting to run
Deploy Shadcn Registry / Deploy Production (push) Waiting to run
Template Metrics / LOC + Bundle Size (push) Waiting to run
Changesets / Create Version PR (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 13:40:13 +08:00

2.3 KiB

@assistant-ui/tap

npm version npm downloads bundle size GitHub stars

React's hooks, headless. Write a resource the same way you write a component, with the same hooks (useState, useEffect, useMemo, ...) imported from "react" and the same rules, except a resource returns a plain value instead of JSX. It runs inside a React component, inside another resource, or standalone with no React tree at all.

tap powers the runtime layer of assistant-ui. Most users do not install it directly; reach for @assistant-ui/react instead.

Installation

npm install @assistant-ui/tap

Usage

import { resource, createTapRoot, useResource } from "@assistant-ui/tap";
import { useState, useEffect } from "react";

const useCounter = ({ incrementBy = 1 }: { incrementBy?: number }) => {
  const [count, setCount] = useState(0);

  useEffect(() => {
    console.log("count:", count);
  }, [count]);

  return {
    count,
    increment: () => setCount((c) => c + incrementBy),
  };
};

const Counter = resource(useCounter);

const counter = createTapRoot(function CounterRoot() {
  return useResource(Counter({ incrementBy: 2 }));
});

const unsubscribe = counter.subscribe(() => {
  console.log("counter updated:", counter.getValue().count);
});

counter.getValue().increment();

In React, host a resource with useResource:

import { useResource } from "@assistant-ui/tap";

function CounterButton() {
  const { count, increment } = useResource(Counter({ incrementBy: 1 }));
  return <button onClick={increment}>{count}</button>;
}

Hooks

Inside a resource you use React's hooks (useState, useEffect, useMemo, useCallback, useRef, use, ...) imported from "react". tap adds useResource / useResources / useTapRoot for composition and useContextProvider for context.

Full API reference at assistant-ui.com/tap/docs.

License

MIT