JSPM

@dudko.dev/agent-web-react

0.0.1
  • ESM via JSPM
  • ES Module Entrypoint
  • Export Map
  • Keywords
  • License
  • Repository URL
  • TypeScript Types
  • README
  • Created
  • Published
  • Downloads 277
  • Score
    100M100P100Q55970F
  • License MIT

React bindings for @dudko.dev/agent-web: a headless useAgent hook, an AgentProvider context, and optional pre-styled components (chat panel, plan/step view, BYOK key form, WebLLM load bar) that connect the in-browser LLM agent to any React site. UI you can drop in β€” or a headless reducer you can build your own around.

Package Exports

  • @dudko.dev/agent-web-react
  • @dudko.dev/agent-web-react/package.json
  • @dudko.dev/agent-web-react/styles.css

Readme

@dudko.dev/agent-web-react

React bindings for @dudko.dev/agent-web β€” the headless, universal in-browser LLM agent. Drop the agent into any React app with a single hook and (optionally) a set of pre-styled components:

  • πŸͺ useAgent β€” build the agent from a config, stream its typed events into a ready-to-render state (plan, steps, tool calls, streamed answer, token usage, model-load progress, chat transcript), and get run / stop / reset / reload.
  • 🧩 <AgentProvider> + components β€” a drop-in <AgentChat> panel plus <PlanView>, <StepList>, <Composer>, <ModelLoadBar>, <UsageBadge>, and <ApiKeyForm>. Bring your own CSS or import the optional stylesheet.
  • πŸ”‘ useCredentials β€” store BYOK API keys encrypted at rest (WebCrypto + IndexedDB).
  • πŸ–₯️ useWebLLMModel β€” load a local WebGPU model with download progress.
  • πŸŽ›οΈ Headless-first β€” the eventβ†’UI logic is a pure, exported reducer (agentStateReducer); the components are optional sugar on top.

npm npm downloads license GitHub last commit

β–ΆοΈŽ Live demo β€” an agent that edits a sticky-notes board via tools. Cloud BYOK or local WebGPU, all in your browser. Source in demo/.

Install

npm install @dudko.dev/agent-web-react @dudko.dev/agent-web react react-dom

Then add only the model providers you use (optional peers of the core, dynamically imported):

# cloud, pick what you need
npm install @ai-sdk/openai        # or @ai-sdk/anthropic, @ai-sdk/google, @ai-sdk/xai, @ai-sdk/deepseek, @ai-sdk/openai-compatible
# local WebGPU models
npm install @browser-ai/web-llm @mlc-ai/web-llm

Import the optional stylesheet once (skip it if you style the components yourself):

import '@dudko.dev/agent-web-react/styles.css'

Quick start β€” a drop-in chat panel

<AgentProvider> builds one agent and shares it; <AgentChat> renders it.

import { useMemo } from 'react'
import {
  AgentProvider,
  AgentChat,
  useCredentials,
  defineTool,
} from '@dudko.dev/agent-web-react'
import { z } from 'zod'
import '@dudko.dev/agent-web-react/styles.css'

export function Assistant() {
  const credentials = useCredentials() // encrypted vault (WebCrypto + IndexedDB)

  // Your app's actions, as tools. Keep the object referentially stable.
  const tools = useMemo(
    () => ({
      add_text: defineTool({
        description: 'Add a text block to the page.',
        inputSchema: z.object({ text: z.string() }),
        execute: async ({ text }) => addTextBlock(text), // your code
      }),
    }),
    [],
  )

  return (
    <AgentProvider
      config={{
        model: { providerType: 'google', model: 'gemini-2.0-flash', credentialRef: 'google' },
        credentials: credentials.store,
        tools,
        describeState: () => serializeMyState(), // optional grounding
      }}
    >
      <AgentChat title="Assistant" style={{ height: 520 }} />
    </AgentProvider>
  )
}

Store the user's key once (encrypted at rest), e.g. from a settings form:

const credentials = useCredentials()
await credentials.setKey('google', userProvidedKey)
// or drop in <ApiKeyForm credentials={credentials} credentialRef="google" />

The useAgent hook (headless)

useAgent is the whole library in one hook β€” use it directly if you want your own UI:

import { useAgent } from '@dudko.dev/agent-web-react'

function Custom() {
  const agent = useAgent({
    model: { providerType: 'openai', model: 'gpt-4o-mini', credentialRef: 'openai' },
    credentials,
    tools,
  })

  return (
    <>
      <button disabled={!agent.isReady} onClick={() => agent.run('Add a totals row')}>
        Run
      </button>
      {agent.isRunning && <button onClick={agent.stop}>Stop</button>}

      {/* Everything below is live, derived from the event stream: */}
      {agent.plan && <p>{agent.plan.thought}</p>}
      {agent.steps.map((s) => (
        <div key={s.id}>
          {s.index}/{s.total} Β· {s.step.description} Β· {s.status}
          {s.toolCalls.map((c, i) => (
            <code key={i}>{c.name}</code>
          ))}
        </div>
      ))}
      <p>{agent.finalText}</p>
      <small>{agent.usage.totalTokens} tokens</small>
    </>
  )
}

useAgent(config, options) returns the full AgentUiState plus:

Field Description
run(goal) Start a run; resolves with the RunResult
stop() Abort the in-flight run
reset() Clear the whole conversation
reload() Rebuild the agent (e.g. after storing a new key)
status idle | initializing | ready | running | error
isReady / isRunning convenience booleans
messages chat transcript ({ role, content, pending }[])
plan / steps the live plan and per-step tool calls
finalText the streamed final answer
usage running token total
modelLoad WebLLM download progress, when loading

Options: deps (rebuild the agent when these change β€” e.g. on a model switch), onEvent (tap the raw event stream), autoStart, maxEvents.

Keep tools and describeState referentially stable (useMemo / useCallback); pass a changed deps array to rebuild the agent on a model/provider switch.

Local WebGPU model (no key, offline)

import { useAgent, useWebLLMModel, ModelLoadBar } from '@dudko.dev/agent-web-react'

function LocalAgent() {
  const local = useWebLLMModel('Llama-3.2-3B-Instruct-q4f16_1-MLC')
  const agent = useAgent(
    { model: local.model!, tools },
    { deps: [local.ready], autoStart: local.ready }, // build once the model is loaded
  )

  if (!local.ready) {
    return local.loading ? (
      <ModelLoadBar load={{ progress: local.progress, text: local.text }} />
    ) : (
      <button disabled={!local.supported} onClick={local.load}>
        Load local model
      </button>
    )
  }
  return <AgentChat controller={agent} />
}

Components

All components are optional and styled by styles.css (class-prefixed awr-, themeable via --awr-* custom properties, light + dark). Each accepts a className; data components take plain props so you can use them standalone.

Component Purpose
<AgentChat> Full panel: transcript + live activity + composer. Reads a controller prop or the <AgentProvider> context.
<MessageList> The chat transcript.
<Composer> Textarea + send/stop button (Enter to send).
<PlanView> A plan's reasoning + step list.
<StepList> Live execution steps with tool calls.
<ModelLoadBar> WebLLM download/init progress.
<UsageBadge> Compact token readout.
<ApiKeyForm> BYOK key entry that writes to the encrypted vault.

Build your own UI

The components are a thin layer over the exported, pure reducer. Use it directly if you'd rather render everything yourself:

import { agentStateReducer, createInitialAgentState } from '@dudko.dev/agent-web-react'

let state = createInitialAgentState()
state = agentStateReducer(state, { type: 'event', event }) // fold each AgentEvent

How it fits together

@dudko.dev/agent-web (peer)     β†’  the headless agent + providers + vault
@dudko.dev/agent-web-react      β†’  useAgent / <AgentProvider> / components
your app                        β†’  tools, credentials, and where the panel goes

The React package re-exports the core primitives you usually need (createAgent, defineTool, VaultCredentialStore, createWebLLMModel, isWebGPUAvailable, and the key types), so a React app can import everything from one place.

Direct browser calls & CORS: not every provider allows direct BYOK calls from a browser origin. Google (Gemini), openai-compatible and the gateway are the reliable direct paths; Anthropic works (a required header is injected); OpenAI/xAI/DeepSeek usually need a proxy. See the core's providers doc. Ship only the user's own key to the browser β€” shared/app keys belong behind a proxy or the gateway.

Demo & deployment

The demo/ app (Vite + React) is deployed to GitHub Pages by .github/workflows/deploy-demo.yml on every push to main. To run it locally:

npm install && npm run build      # build the library into dist/
cd demo && npm install && npm run dev

The demo aliases @dudko.dev/agent-web-react to the built ../dist, so rebuild the library (npm run build) after changing its source.

License

MIT Β© Siarhei Dudko