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 🙏

© 2026 – Pkg Stats / Ryan Hefner

@tagit/ai-client-react

v1.2.0

Published

Self-contained React chat widget and hooks for TagIt AI client

Readme

@tagit/ai-client-react

React integration layer for tagIt AI.

Use this package when you want a self-contained chat experience in a React app. It builds on @tagit/ai-client-core, which owns the client engine, orchestrator transport, state, and collector emission. The core package README is the source of truth for those engine details.

What Is tagIt?

tagIt is the platform for building governed AI experiences. It gives teams a way to define AI use cases, connect models, optionally attach MCP servers, and shape the orchestration logic that drives the overall experience.

What Is the AI Orchestrator?

The AI Orchestrator is the runtime layer that executes a use case. It combines the configured model, optional tools and MCP servers, conversation context, and policy behavior to produce the response flow for a specific scenario.

Why These Packages Exist

The @tagit/ai-client-core and @tagit/ai-client-react packages are the client-side pieces of that system. They connect your app to a tagIt account and a configured orchestrator so you can embed AI experiences in a product or website.

You need a tagIt account and at least one configured use case before these packages can drive a real experience. MCP servers are optional, but they can extend the orchestrator with tools and external capabilities.

To learn more, visit tagit.live.

This README is the source of truth for integrating the React package.

What This Package Provides

  • AIClientProvider for React context
  • useAIClient, useConversation, useClientEvents, and useAgents
  • ChatWidget for a full embeddable chat shell
  • ChatConversation for a lighter conversation surface
  • the widget stylesheet for direct import

Provider Contract

AIClientProvider is the only required wrapper.

| Field | Type | Required | What it does | Example | |---|---|---:|---|---| | client | AIClient | yes | The live client instance used by hooks and widgets below the provider. | client={client} | | children | ReactNode | yes | The React subtree that receives AI client context. | <ChatWidget /> |

<AIClientProvider client={client}>
  <ChatWidget />
</AIClientProvider>

Public Imports

Supported public imports:

  • @tagit/ai-client-core
  • @tagit/ai-client-core/config
  • @tagit/ai-client-core/types
  • @tagit/ai-client-react
  • @tagit/ai-client-react/chat-widget
  • @tagit/ai-client-react/chat-widget.css
  • @tagit/ai-client-react/hooks
  • @tagit/ai-client-react/provider
  • @tagit/ai-client-react/conversation

Do not import from dist/* or from internal source paths.

Install

npm install @tagit/ai-client-core @tagit/ai-client-react

Minimal Working Example

This is the smallest known-good React embed.

import { AIClient, createDefaultConfig } from '@tagit/ai-client-core';
import { AIClientProvider, ChatWidget } from '@tagit/ai-client-react';
import '@tagit/ai-client-react/chat-widget.css';

const client = new AIClient(
  createDefaultConfig('your-use-case-id', 'https://your-orchestrator.example.com', {
    user: {
      entityId: 'anonymous-or-authenticated-user-id',
      entityidType: 'internal_user_id',
      entityidAlgo: 'native',
      entityidOwner: 'your-service',
    },
    conversation: {
      conversationId: 'optional-durable-conversation-id',
      title: 'Optional conversation title',
      memoryMaxTurns: 6,
    },
    collector: {
      enabled: false,
    },
  }),
);

export default function App() {
  return (
    <AIClientProvider client={client}>
      <ChatWidget />
    </AIClientProvider>
  );
}

If that exact example does not render the default empty state and footer, verify the package install and runtime config before customizing anything.

Core Concepts

@tagit/ai-client-core

This is the headless engine:

  • orchestrator config and transport
  • conversation state
  • SSE streaming
  • retry and timeout handling
  • collector emission

@tagit/ai-client-react

This is the React shell:

  • provider + hooks
  • stock chat widget
  • inline or overlay rendering
  • shell styling and theming

Treat the core package as the engine and the React package as the UI layer on top.

Widget Modes

ChatWidget supports two layout APIs:

  • presentation, the preferred API for new integrations
  • mode, the legacy API retained for existing integrations

Use presentation when embedding the widget into production applications:

  • presentation="overlay" for floating chat over host content
  • presentation="sidePanel" for a viewport-docked panel where the host reserves side space
  • presentation="inline" for embedded layouts inside the host page

The older mode="overlay" and mode="inline" props continue to work.

For legacy inline mode, there are two common host patterns:

  • dockMode="page" for a page-anchored rail
  • dockMode="viewport" for a viewport-docked rail that stays fixed to the browser edge

Use presentation="sidePanel" for new viewport sidebar integrations. The SDK keeps the panel pinned to the browser edge, owns resize math, and automatically switches to mobile fullscreen at the configured breakpoint. The host should only reserve page space from the controlled open and width values.

Choosing a Presentation

| Use case | Recommended presentation | Host responsibility | |---|---|---| | Marketing site, support bubble, simple app assistant | presentation="overlay" | None, unless you want a backdrop or controlled open state. | | Application copilot that should push content aside | presentation="sidePanel" | Reserve side space from open and width; let the SDK render and resize the panel. | | Fully embedded chat region inside a page layout | presentation="inline" | Provide the parent container size. | | Existing integrations already using mode | Keep mode for now | Migrate to presentation when you next touch layout code. |

What The SDK Owns

The React package owns:

  • panel shell rendering
  • viewport docking
  • resize handle behavior
  • mobile fullscreen takeover
  • tab positioning
  • close and new-chat controls
  • stock conversation body

The host application should not wrap the widget in invisible resize frames or constrain the panel with max-width: 100% when using presentation="sidePanel". Those patterns can break bidirectional resize math.

What The Host Owns

The host application owns:

  • the AIClientProvider and configured core client
  • whether the panel is controlled or uncontrolled
  • reserved page space for presentation="sidePanel"
  • theme/color values when the default palette is not enough
  • application-specific quick actions, page context, or custom callbacks

Overlay Example

<ChatWidget
  presentation="overlay"
  side="right"
  title="tagIt Assistant"
  showBackdrop
  closeOnOutsideClick
/>

Side-Panel Example

const [open, setOpen] = useState(false);
const [width, setWidth] = useState('420px');

return (
  <div
    style={{
      minHeight: '100dvh',
      paddingRight: open ? width : 0,
      transition: 'padding-right 180ms ease',
    }}
  >
    <main>
      <AppContent />
    </main>

    <ChatWidget
      presentation="sidePanel"
      side="right"
      open={open}
      onOpenChange={setOpen}
      width={width}
      onWidthChange={setWidth}
      minWidthPx={360}
      maxWidthPx={720}
      mobileBreakpointPx={768}
      mobilePresentation="fullscreen"
      title="Tacky"
    />
  </div>
);

For a left-side panel, reserve paddingLeft instead of paddingRight.

Side-Panel Integration Checklist

  • Use controlled open and onOpenChange.
  • Store width as a string, for example '420px'.
  • Pass the same width to the widget and to the host reserved space.
  • Keep minWidthPx and maxWidthPx aligned with your application layout.
  • Use mobilePresentation="fullscreen" unless your mobile layout has a strong reason not to.
  • Do not put the widget inside a parent that clips overflow or limits width when using presentation="sidePanel".
  • Do not implement a separate resize handle in the host application.

Legacy Page-Rail Inline Example

<ChatWidget
  mode="inline"
  dockMode="page"
  side="right"
  open={true}
  title="tagIt Assistant"
  tabLabel="AI Client"
/>

Legacy Viewport-Docked Inline Example

<ChatWidget
  mode="inline"
  dockMode="viewport"
  side="right"
  open={isOpen}
  onOpenChange={setIsOpen}
  title="tagIt Assistant"
  tabLabel="AI Client"
  tabHeightPercent={18}
  width={440}
  minWidthPx={360}
  maxWidthPx={440}
/>

Prefer the new side-panel example for new application copilot integrations. The legacy viewport-docked inline mode remains available for existing apps that already use it.

Widget Configuration Dictionary

This section documents the public ChatWidget props that matter for integrations. If a field is omitted, the widget uses its built-in default.

Layout and shell fields

| Field | Type / values | Default | What it does | Example | |---|---|---:|---|---| | presentation | 'overlay' \| 'sidePanel' \| 'inline' | derived from mode | Preferred layout API for new integrations. sidePanel is viewport-docked and designed for host-reserved side space. | presentation="sidePanel" | | mode | 'overlay' \| 'inline' | overlay | Chooses between floating chat and embedded chat. | mode="inline" | | dockMode | 'page' \| 'viewport' | page | Controls how inline mode is docked. page is a normal page rail. viewport pins the rail to the browser edge. | dockMode="viewport" | | side | 'left' \| 'right' | right | Chooses which side the tab and panel live on. | side="right" | | open | boolean | uncontrolled | Controlled open state. Use when the host needs to manage layout changes. | open={isOpen} | | defaultOpen | boolean | false | Uncontrolled initial open state. | defaultOpen={true} | | onOpenChange | (open: boolean) => void | undefined | Callback fired when the widget opens or closes. | onOpenChange={setIsOpen} | | width | number \| string | 420 | Sets the panel width. Numbers are treated as pixels. | width={440} or width="32vw" | | resizable | boolean | true | Enables or disables the drag resize handle. | resizable={false} | | minWidthPx | number | 280 | Minimum panel width in pixels when resizing. | minWidthPx={360} | | maxWidthPx | number | undefined | Maximum panel width in pixels when resizing. | maxWidthPx={520} | | mobileBreakpointPx | number | 720 | Width breakpoint where the widget switches to mobile behavior. | mobileBreakpointPx={768} | | mobilePresentation | 'fullscreen' \| 'default' | fullscreen | Controls mobile behavior. fullscreen makes the panel a 100vw x 100dvh takeover and disables resize. | mobilePresentation="fullscreen" | | tabHeightPercent | number from 0 to 100 | 40 | Positions the tab vertically. 100 is near the top, 0 near the bottom. | tabHeightPercent={18} | | className | string | undefined | Adds host styling hooks to the widget root. | className="product-rail" | | children | ReactNode | undefined | Replaces the stock conversation body when intentionally supplied. | <CustomConversation /> |

Identity and labeling fields

| Field | Type / values | Default | What it does | Example | |---|---|---:|---|---| | title | string | Ask TagIt | Panel header title. | title="tagIt Assistant" | | titleIconUrl | string \| null | undefined | Optional header icon URL. | titleIconUrl="/icons/agent.png" | | tabLabel | string \| null | Chat | Text label on the tab. Set to null for icon-only mode. | tabLabel="AI Client" | | tabIcon | ReactNode | 'AI' | Fallback tab icon/content when no icon URL is provided. | tabIcon={<Sparkles />} | | tabIconUrl | string \| null | undefined | Optional image URL for the tab icon. | tabIconUrl="/icons/chat.png" | | tabTooltipOpen | string \| null | derived | Tooltip shown when the widget is closed. | tabTooltipOpen="Open AI Client" | | tabTooltipClose | string \| null | derived | Tooltip shown when the widget is open. | tabTooltipClose="Collapse AI Client" |

Theme and color fields

| Field | Type / values | Default | What it does | Example | |---|---|---:|---|---| | theme | 'light' \| 'dark' \| 'system' | system | Chooses the widget theme. system follows the OS preference. | theme="dark" | | colorScheme | ChatColorScheme | undefined | Single theme palette for the widget. | { bg: '#0b1220', primary: '#60a5fa' } | | colorSchemes | Partial<Record<'light' \| 'dark', ChatColorScheme>> | undefined | Separate palettes for light and dark themes. | { light: {...}, dark: {...} } |

Visibility and actions

| Field | Type / values | Default | What it does | Example | |---|---|---:|---|---| | showBackdrop | boolean | false | Shows the overlay scrim in overlay mode. | showBackdrop | | closeOnOutsideClick | boolean | false | Allows clicking the backdrop to close the widget in overlay mode. | closeOnOutsideClick | | onWidthChange | (width: string) => void | undefined | Called when the user resizes the panel. | onWidthChange={setWidth} | | onNewChat | () => void | undefined | Overrides the default new-chat action. | onNewChat={handleNewChat} |

Open / Close Control

ChatWidget supports both controlled and uncontrolled open state.

  • open + onOpenChange for controlled usage
  • defaultOpen for uncontrolled usage

Use controlled state when the host page needs to:

  • reserve layout space only while the panel is open
  • coordinate open state with surrounding page layout
  • keep the tab at the browser edge in a viewport-docked integration

Example: controlled side panel

const [isOpen, setIsOpen] = useState(true);
const [width, setWidth] = useState('420px');

<ChatWidget
  presentation="sidePanel"
  side="right"
  open={isOpen}
  onOpenChange={setIsOpen}
  width={width}
  onWidthChange={setWidth}
  title="tagIt Assistant"
  tabLabel="AI Client"
/>

Mobile Behavior

By default, the widget switches to fullscreen when the viewport is at or below mobileBreakpointPx.

Fullscreen mobile behavior means:

  • the panel uses 100vw and 100dvh
  • the panel is anchored to the viewport instead of the page
  • resize is disabled
  • the tab becomes a floating button near the lower-right edge
  • the host should not reserve side-panel layout space for mobile

If your host reserves side-panel space, clear that reservation at the same breakpoint:

const isMobile = useMediaQuery('(max-width: 768px)');

<div style={{ paddingRight: open && !isMobile ? width : 0 }}>
  <AppContent />
  <ChatWidget
    presentation="sidePanel"
    open={open}
    onOpenChange={setOpen}
    width={width}
    onWidthChange={setWidth}
    mobileBreakpointPx={768}
  />
</div>

Troubleshooting Layout

| Symptom | Likely cause | Fix | |---|---|---| | Panel opens but leaves a blank rail after closing | Host always reserves width instead of checking open. | Reserve side space only when open is true and not mobile. | | Panel can shrink but not grow | Parent container is constraining width. | Use presentation="sidePanel" and avoid parent max-width: 100% constraints around the widget. | | Panel scrolls with the page | The host is using an embedded/inline layout when it wanted a viewport panel. | Use presentation="sidePanel" or presentation="overlay". | | Mobile inherits desktop side-panel behavior | Host is not clearing reserved side space at the mobile breakpoint. | Use mobilePresentation="fullscreen" and clear host padding/margin on mobile. | | Backdrop shows in a side panel | presentation="overlay" is being used. | Use presentation="sidePanel" for app copilots that push content aside. |

Conversation Action Options

The useConversation() hook exposes sendMessage(), retry(), cancel(), clearHistory(), and reEditMessage(). The request option bag belongs to the core client and is documented in @tagit/ai-client-core.

Conversation History UI

When conversationHistory.enabled is configured on the core client, the stock React UI adds first-class history controls:

  • the widget header shows a conversation history button
  • the empty state shows up to five recent conversations
  • users can load a previous conversation
  • users can rename conversations
  • users can archive conversations
  • the New Chat button starts a new durable conversation instead of only clearing local state
  • after New Chat, the empty-state recent conversation list refreshes from the configured history transport
const client = new AIClient(
  createDefaultConfig('uc-platform-assistant-v2', 'https://ai.tagit.live', {
    user: {
      entityId: currentUser.uid,
      userId: currentUser.uid,
    },
    conversationHistory: {
      enabled: true,
      recentLimit: 5,
      surface: window.location.pathname,
      source: 'platform',
    },
  })
);

For custom shells, use useConversationHistory() to render your own list, picker, rename action, archive action, or recent-conversation empty state.

const history = useConversationHistory({ limit: 5 });

await history.loadConversation(conversationId);
await history.renameConversation(conversationId, 'Campaign planning');
await history.archiveConversation(conversationId);
await history.startNewConversation('New chat');

Multi-Agent UI

When the core AIClient is configured with multiple host-authorized agents, the stock ChatWidget shows the active agent name and logo in the header. The identity is selectable only when more than one agent is available.

The built-in Agent Picker:

  • lists the host-supplied agent names, logos, descriptions, and active state
  • uses the same overlay panel pattern as Conversation History
  • confirms before replacing a non-empty active chat
  • preserves an unsent composer draft when the user cancels
  • starts a fresh local conversation when the user confirms an agent switch
  • scopes Conversation History to the selected agent's useCaseId

The host app owns the agent list, default selection, route/page/global policy, and runtime variables. The SDK only renders the supplied agents and performs the switch safely.

const client = new AIClient(
  createDefaultConfig('uc-platform-help', 'https://ai.tagit.live', {
    user: { entityId: currentUser.uid, userId: currentUser.uid },
    conversationHistory: { enabled: true, surface: window.location.pathname },
    agents: [
      { useCaseId: 'uc-platform-help', name: 'Platform Help' },
      { useCaseId: 'uc-dashboard-insights', name: 'Dashboard Insights' },
    ],
    defaultUseCaseId: 'uc-dashboard-insights',
    resolveRequestOptions(agent) {
      return {
        surface: window.location.pathname,
        runtimeVariables: {
          'page.path': window.location.pathname,
          'agent.name': agent.name,
        },
      };
    },
  }),
);

Custom shells can use useAgents() to render their own selector.

ChatConversation

Use ChatConversation when you want a lighter conversation surface and you are already handling the shell yourself.

import { AIClientProvider, ChatConversation } from '@tagit/ai-client-react';

export default function App() {
  return (
    <AIClientProvider client={client}>
      <ChatConversation />
    </AIClientProvider>
  );
}

Use ChatWidget when you want the full built-in shell, header, tab, and sizing controls.

Custom Conversation Body

Only pass children to ChatWidget if you intentionally want to replace the built-in conversation body.

<ChatWidget mode="overlay" side="right" title="tagIt Assistant">
  <CustomConversation />
</ChatWidget>

If you do not pass children, the stock conversation UI is rendered automatically.

Configuration

The React package owns the widget shell, provider, hooks, and layout guidance. The core config, request, event, and collector contracts live in @tagit/ai-client-core.

Use the core README for:

  • createDefaultConfig()
  • shared user and conversation config
  • orchestrator config
  • collector config
  • UI config
  • sendMessage() / retry() option bags
  • conversation state
  • client events
  • orchestrator request shape
  • collector envelope shape

See packages/ai-client-core/README.md.

Starter Harness

The starter app in apps/starter is the local demo and shell playground.

Use it to test:

  • overlay mode
  • inline page-rail mode
  • inline viewport-docked mode
  • theming and color schemes
  • tab labels and tooltips
  • open/close behavior
  • sizing and resize behavior
  • request-level runtimeVariables against a real /v2/chat use case
  • one-agent and multi-agent configuration
  • agent switching, confirmation cancellation, scoped history, and host-resolved runtime variables

The starter is not the distributable package.

Recommended Integration Patterns

Simple Embed

Use this when you want the fastest possible install:

  • create one AIClient
  • provide user.entityId so V2 memory and collector events share the same user identity
  • optionally provide conversation.conversationId when you want a durable conversation across reloads
  • wrap the app with AIClientProvider
  • render <ChatWidget />
  • import @tagit/ai-client-react/chat-widget.css

V2 Contract Notes

This beta client targets the orchestrator V2 contract only. The core client appends /v2/chat, sends the current user message, and lets the orchestrator rehydrate recent conversation memory from conversation.conversationId and user.userId ?? user.entityId.

Checkpoint controls are not part of the first React beta. Workflow metadata is still captured in client state and events, so host apps can inspect workflowId, workflowStatus, and checkpointPrompt if a workflow use case returns them.

Viewport-Docked Sidebar

Use this when the chat should feel like a persistent product rail:

  • keep the widget fixed to the browser edge
  • reserve page width only when open
  • let the tab collapse back to the browser edge
  • keep the host page scroll independent of the rail

This pattern pairs best with:

  • mode="inline"
  • dockMode="viewport"
  • controlled open state
  • host-page padding or content width reservation that only applies while the panel is open
<div className={`page-shell ${isOpen ? 'page-shell-chat-open' : ''}`}>
  <main>...</main>
  <ChatWidget
    mode="inline"
    dockMode="viewport"
    side="right"
    open={isOpen}
    onOpenChange={setIsOpen}
  />
</div>

Custom Shell

Use useConversation() when the host app wants to render its own shell and only consume conversation state and actions.

Troubleshooting

| Symptom | Likely cause | Check | |---|---|---| | Header/tab only, empty shell | stale package cache or old bundle | clear node_modules/.vite, restart dev server, confirm children is not being passed | | No styling | CSS not imported | verify @tagit/ai-client-react/chat-widget.css import | | Missing body after passing custom JSX | children overrides stock body | remove children to restore the stock widget | | No messages sent | invalid orchestrator config | verify orchestrator.baseUrl and useCaseId, confirm baseUrl does not include /v2/chat | | V2 request is rejected for missing user | missing shared identity | set user.entityId; optionally set user.userId when it should differ from the entity id | | Runtime import error | unsupported import path | use only documented public exports | | Collector events do not appear | collector config incomplete or disabled | confirm collector.enabled, collector.baseUrl, and collector.tagId | | Inline mode looks cramped | panel width too small | pass a wider width or switch to dockMode="viewport" with host-side reserved space | | Viewport-docked mode takes over the whole page | host layout is stretching the widget root | keep the docked widget fixed to the viewport edge and reserve page width only in the host shell |

Related Docs