Frontend

React Hooks

Call your API routes from React with useAPI, useWebsocket, and useEventStream

Call your API routes from React components using type-safe hooks from @agentuity/react.

Non-React Alternative

For non-React apps (Vue, Svelte, vanilla JS) or server-side code, use the RPC Client from @agentuity/frontend instead.

Installation

bun add @agentuity/react

Basic Usage with useAPI

The useAPI hook calls your API routes with full type safety:

import { AgentuityProvider, useAPI } from '@agentuity/react';
 
function ChatForm() {
  const { invoke, isLoading, data, error } = useAPI('POST /api/chat');
 
  const handleSubmit = async (e: React.FormEvent<HTMLFormElement>) => {
    e.preventDefault();
    const formData = new FormData(e.currentTarget);
    const message = formData.get('message') as string;
 
    try {
      await invoke({ message });
    } catch (err) {
      // Error is also available via the error state
      console.error('API call failed:', err);
    }
  };
 
  return (
    <form onSubmit={handleSubmit}>
      <input name="message" placeholder="Type a message..." disabled={isLoading} />
      <button type="submit" disabled={isLoading}>
        {isLoading ? 'Sending...' : 'Send'}
      </button>
      {error && <p>Error: {error.message}</p>}
      {data && <p>Response: {data.response}</p>}
    </form>
  );
}
 
export default function App() {
  return (
    <AgentuityProvider>
      <ChatForm />
    </AgentuityProvider>
  );
}

Return values:

PropertyTypeDescriptionMethods
invoke(input) => Promise<TOutput>Execute the requestPOST, PUT, PATCH, DELETE
refetch() => Promise<void>Manually refetch dataGET
dataTOutput | undefinedLast successful responseAll
errorError | nullLast error, if anyAll
isLoadingbooleanTrue during initial loadAll
isFetchingbooleanTrue during any fetch (including refetches)All
isSuccessbooleanTrue after successful completionAll
isErrorbooleanTrue if request failedAll
reset() => voidReset state to initial valuesAll

GET requests auto-fetch on mount. POST/PUT/PATCH/DELETE require calling invoke().

useAPI Options

Pass an options object instead of a route string for advanced configuration:

const { data, refetch } = useAPI({
  route: 'GET /api/users',
  staleTime: 30000,        // Data stays fresh for 30 seconds
  refetchInterval: 60000,  // Auto-refetch every 60 seconds
  enabled: isReady,        // Only fetch when condition is true
  onSuccess: (data) => console.log('Fetched:', data),
  onError: (err) => console.error('Failed:', err),
});
OptionTypeDefaultDescription
routestring-Route key (e.g., 'GET /api/users')
queryURLSearchParams | Record<string, string>-Query parameters
headersRecord<string, string>-Additional request headers
enabledbooleantrue (GET), false (others)Control when request executes
staleTimenumber0Milliseconds data stays fresh before refetching
refetchIntervalnumber-Auto-refetch interval in milliseconds
onSuccess(data) => void-Callback on successful request
onError(error) => void-Callback on failed request

Streaming with useAPI

For streaming routes, useAPI accumulates chunks and provides transform callbacks:

const { data, isLoading } = useAPI({
  route: 'POST /api/stream',
  input: { prompt: 'Hello' },
  delimiter: '\n',  // Split chunks by newline (default)
  onChunk: (chunk) => {
    console.log('Received chunk:', chunk);
    return chunk;  // Can transform before accumulation
  },
});
 
// data is TOutput[] - array of all received chunks
OptionTypeDescription
delimiterstringDelimiter for splitting stream chunks (default: \n)
onChunk(chunk) => chunkTransform each chunk before accumulation

Real-Time with useWebsocket

For bidirectional real-time communication, use useWebsocket:

import { useWebsocket } from '@agentuity/react';
 
function RealtimeChat() {
  const { isConnected, send, messages, clearMessages } = useWebsocket('/api/chat', {
    maxMessages: 100,  // Keep last 100 messages
  });
 
  return (
    <div>
      <p>Status: {isConnected ? 'Connected' : 'Connecting...'}</p>
      <ul>
        {messages.map((msg, i) => (
          <li key={i}>{JSON.stringify(msg)}</li>
        ))}
      </ul>
      <button onClick={() => send({ message: 'Hello!' })}>Send Hello</button>
      <button onClick={clearMessages}>Clear</button>
    </div>
  );
}

You can also use data for only the most recent message:

import { useWebsocket } from '@agentuity/react';
import { useEffect } from 'react';
 
function LatestUpdate() {
  const { isConnected, send, data } = useWebsocket('/api/updates');
 
  useEffect(() => {
    if (data) {
      console.log('Latest update:', data);
    }
  }, [data]);
 
  return <p>Latest: {data ? JSON.stringify(data) : 'Waiting...'}</p>;
}

Auto-Reconnection

WebSocket connections automatically reconnect with exponential backoff if the connection drops. Messages sent while disconnected are queued and sent when the connection is restored.

Return values:

PropertyTypeDescription
isConnectedbooleanTrue when WebSocket is open
send(data: TInput) => voidSend a message
dataTOutput | undefinedLast received message
messagesTOutput[]Array of all received messages
clearMessages() => voidClear the messages array
errorError | nullConnection or message error
isErrorbooleanTrue if an error occurred
readyStatenumberWebSocket state (0=connecting, 1=open, 2=closing, 3=closed)
close() => voidClose the connection
reset() => voidClear error state

Options:

OptionTypeDescription
queryURLSearchParamsQuery parameters to append to the WebSocket URL
subpathstringSubpath to append to the WebSocket path
signalAbortSignalAbortSignal to cancel the connection
maxMessagesnumberMaximum messages to keep in messages array (oldest removed when exceeded)

Streaming with useEventStream

For one-way streaming from server to client, use Server-Sent Events:

import { useEventStream } from '@agentuity/react';
 
function LiveStatus() {
  const { isConnected, data, error } = useEventStream('/api/status');
 
  if (!isConnected) {
    return <p>Connecting to status feed...</p>;
  }
 
  if (error) {
    return <p>Error: {error.message}</p>;
  }
 
  return (
    <div>
      <p>Live Status: {data?.status ?? 'Waiting for update...'}</p>
      <p>Last updated: {data?.timestamp ?? '-'}</p>
    </div>
  );
}

When to Use SSE

Use useEventStream when you only need server-to-client updates (e.g., progress indicators, live dashboards, notifications). For bidirectional communication, use useWebsocket.

Return values:

PropertyTypeDescription
isConnectedbooleanTrue when EventStream is open
dataTOutput | undefinedLast received event data
errorError | nullConnection error
isErrorbooleanTrue if an error occurred
readyStatenumberEventSource state (0=connecting, 1=open, 2=closed)
close() => voidClose the connection
reset() => voidClear error state

Options: Accepts query, subpath, and signal options, same as useWebsocket.

Choosing the Right Hook

HookUse CaseDirectionExamples
useAPIRequest/responseOne-timeSend a message, fetch user data, submit a form
useWebsocketBidirectional streamingClient ↔ ServerLive chat, multiplayer sync, shared editing
useEventStreamServer pushServer → ClientAI token streaming, build logs, live metrics

Request Options

Pass options to the hook for customizing requests. Options like query and headers are set when calling the hook, not when invoking:

// Options are passed to useAPI, not invoke
const { invoke } = useAPI({
  route: 'POST /api/chat',
  query: new URLSearchParams({ version: '2' }),
  headers: { 'X-Custom-Header': 'value' },
});
 
// invoke only takes the input data
await invoke({ message: 'Hello' });

For dynamic query parameters, create a new hook instance:

function SearchResults({ query }: { query: string }) {
  const { data, isLoading } = useAPI({
    route: 'GET /api/search',
    query: { q: query },
  });
 
  return <div>{isLoading ? 'Searching...' : JSON.stringify(data)}</div>;
}

Auth State with useAuth

Access authentication state for protected components or custom auth logic:

import { useAuth } from '@agentuity/react';
 
function ProtectedContent() {
  const { isAuthenticated, authLoading } = useAuth();
 
  if (authLoading) {
    return <p>Loading...</p>;
  }
 
  if (!isAuthenticated) {
    return <p>Please sign in to continue.</p>;
  }
 
  return <p>Welcome! You have access.</p>;
}

Return values:

PropertyTypeDescription
isAuthenticatedbooleanTrue when auth token is set and not loading
authLoadingbooleanTrue while auth state is initializing
authHeaderstring | nullThe current Authorization header value
setAuthHeader(token: string | null) => voidManually set the auth header
setAuthLoading(loading: boolean) => voidControl the loading state

useAuth vs useAgentuity

useAuth provides auth-specific state. For non-auth context like baseUrl, use useAgentuity() instead. See Authentication for integrating with identity providers.

Next Steps

Need Help?

Join our DiscordCommunity for assistance or just to hang with other humans building agents.

Send us an email at hi@agentuity.com if you'd like to get in touch.

Please Follow us on

If you haven't already, please Signup for your free account now and start building your first agent!