Skip to main content
userTourKit
@tour-kit/ai

Components

Pre-built AI chat components: AiChatPanel, AiChatToggle, AiChatMessageList, and AiChatInput with shadcn/ui styling

domidex01Published

@tour-kit/ai provides headless components that you can style with your own design system.

AiChatProvider

The root provider that manages chat state and configuration.

import { AiChatProvider } from '@tour-kit/ai'

<AiChatProvider
  config={{
    endpoint: '/api/chat',
    tourContext: true,
    rateLimit: {
      maxMessages: 10,
      windowMs: 60_000,
    },
    suggestions: {
      static: ['How do I get started?'],
      dynamic: true,
    },
  }}
>
  {children}
</AiChatProvider>

Props

PropTypeDescription
configAiChatConfigChat configuration object
childrenReactNodeChild components

AiChatSuggestions

Renders AI-generated follow-up suggestions as clickable chips.

import { AiChatSuggestions } from '@tour-kit/ai'

<AiChatSuggestions
  suggestions={['How do I start?', 'What features exist?']}
  onSelect={(suggestion) => sendMessage({ text: suggestion })}
  className="flex gap-2"
/>

You can customize rendering with renderSuggestion:

<AiChatSuggestions
  suggestions={['How do I start?']}
  onSelect={(suggestion) => sendMessage({ text: suggestion })}
  renderSuggestion={(suggestion, onSelect) => (
    <span key={suggestion} onClick={onSelect} className="px-3 py-1 rounded-full bg-gray-100 cursor-pointer">
      {suggestion}
    </span>
  )}
/>

Props

PropTypeDescription
suggestionsstring[]Explicit suggestions list (overrides hook data)
onSelect(suggestion: string) => voidCalled when a suggestion is clicked
renderSuggestion(suggestion: string, onSelect: () => void) => ReactNodeCustom chip renderer
classNamestringContainer class name

AiChatPanel

Pre-built chat panel. Renders a header, message list, suggestions strip, and input.

import { AiChatPanel } from '@tour-kit/ai'

<AiChatPanel
  size="default"
  position="bottom-right"
  title="Need help?"
  emptyState={<p>Ask me anything.</p>}
  showSuggestions
/>

Props

PropTypeDescription
size'default' | 'sm' | 'lg'Panel size variant
position'bottom-right' | 'bottom-left'Anchor position
titleReactNodeHeader title content
emptyStateReactNodeRendered when no messages
showSuggestionsbooleanShow the suggestions chip strip
classNamestringContainer class name
childrenReactNodeOverride the default body
renderMessage(message, index) => ReactNodeCustom message renderer

AiChatToggle

Floating button that opens/closes the chat panel.

import { AiChatToggle } from '@tour-kit/ai'

<AiChatToggle size="default" position="bottom-right" />

Props

PropTypeDescription
size'default' | 'sm' | 'lg'Button size variant
position'bottom-right' | 'bottom-left'Anchor position
iconReactNodeOverride the default icon
classNamestringClass name override

AiChatHeader

Header bar inside <AiChatPanel>. Wires up the close button and panel title slot. Use directly when composing your own panel layout.

import { AiChatHeader } from '@tour-kit/ai'

<AiChatHeader title="Tour Kit AI" showClose onClose={() => /* ... */} />

Props

PropTypeDescription
titleReactNodeHeader title
showClosebooleanRender the close button
onClose() => voidClose-button handler
classNamestringClass name override
childrenReactNodeReplace default content

AiChatMessageList

Scrollable list of chat messages. Auto-scrolls to the latest message; respects prefers-reduced-motion.

import { AiChatMessageList } from '@tour-kit/ai'

<AiChatMessageList
  emptyState={<p>Ask a question to get started.</p>}
  renderMessage={(message) => <YourBubble message={message} />}
/>

Props

PropTypeDescription
classNamestringClass name override
emptyStateReactNodeRendered when there are no messages
renderMessage(message, index) => ReactNodeCustom per-message renderer

AiChatMessage

Single chat-message bubble. Pass role to switch between user/assistant styling.

import { AiChatMessage } from '@tour-kit/ai'

<AiChatMessage role="assistant">
  Hi! How can I help with your tour?
</AiChatMessage>

Props

PropTypeDescription
role'user' | 'assistant'Message author
childrenReactNodeMessage content
classNamestringClass name override

AiChatInput

Message input field with send button. Reads/writes through useAiChat so it stays in sync with the rest of the panel.

import { AiChatInput } from '@tour-kit/ai'

<AiChatInput placeholder="Ask anything..." />

Props

PropTypeDescription
classNamestringClass name override
placeholderstringInput placeholder
disabledbooleanDisable input (also auto-disables while streaming)

AiChatPortal

Portals its children to container (defaults to document.body). Use to escape stacking contexts or to mount the chat panel inside a specific overlay container.

import { AiChatPortal, AiChatPanel } from '@tour-kit/ai'

<AiChatPortal container={document.getElementById('chat-root')}>
  <AiChatPanel />
</AiChatPortal>

Props

PropTypeDescription
childrenReactNodeSubtree to portal
containerHTMLElement | nullTarget node; defaults to document.body when omitted

Headless Pattern

All components follow the headless pattern. Use the hooks directly for full control:

import { useAiChat } from '@tour-kit/ai'

function CustomChat() {
  const { messages, sendMessage, status } = useAiChat()

  // Build your own UI
  return (
    <div>
      {messages.map((msg) => (
        <div key={msg.id}>{msg.content}</div>
      ))}
      <button onClick={() => sendMessage({ text: 'Hello' })}>Send</button>
    </div>
  )
}

See Hooks for the full hook surface and API Reference for variants/types.