@avis-ai/avischat
v1.0.11
Published
React components and hooks for **Avis AI**: a drop-in chat widget, optional **floating launcher**, a headless hook, streaming replies, session memory, and a built-in model picker (OpenAI, Anthropic, and Google Gemini). Assistant messages render **Markdown
Readme
AvisChat (@avis-ai/avischat)
React components and hooks for Avis AI: a drop-in chat widget, optional floating launcher, a headless hook, streaming replies, session memory, and a built-in model picker (OpenAI, Anthropic, and Google Gemini). Assistant messages render Markdown with GFM tables, fenced code blocks with copy, and KaTeX math.
Demo: https://demo-ai.avi-s.in
Vue, Svelte, Angular, or vanilla JS?
@avis-ai/avischat is React-only (components + hooks). For other stacks you have two main options:
Headless client —
@avis-ai/sdk-js— sameAvisclass this package uses under the hood:chat(),chatStream(), and session ids. Build your own UI in any framework. Full documentation is in the@avis-ai/sdk-jspackage README (source tree:packages/sdk-js/README.md).No framework on your page — iframe embed — load
widget.jsfrom the API and pass your key. You must setdata-embed-urlto a public/embedpage in production (the script’s default is localhost). Example:<script src="https://api-ai.avi-s.in/widget.js" data-api-key="sk_live_YOUR_API_KEY" ></script>
The sections below apply to React integrations only.
Contents
- Getting your API key
- Installation
- Quick start
- Making good use
- Components
- Floating launcher
- AvisChat props
- useAvisChat hook
- Session persistence
- Proxy / production
- TypeScript types
- Styling & CSS
- Errors & accessibility
- Framework notes
Getting your API key
- Log in to the Dashboard
- Create a project — name it and set the system prompt
- Copy the API key — shown once; store it securely
Use the key in apiKey, or keep it on your server and proxy requests (see proxy).
Installation
Peer dependency: React 18+
npm install @avis-ai/avischat @avis-ai/sdk-js
pnpm add @avis-ai/avischat @avis-ai/sdk-js
yarn add @avis-ai/avischat @avis-ai/sdk-jsQuick start
import { AvisChat } from "@avis-ai/avischat";
export function MyChat() {
return <AvisChat apiKey="your-api-key" />;
}You get streaming bubbles, typing state, message input, and (by default) a model dropdown.
Making good use of AvisChat
Auth: only apiKey or client is required
Either pass apiKey or a client (new Avis({ apiKey, baseUrl }) from @avis-ai/sdk-js). Everything else is optional.
sessionId and memory
The API stores the full thread under sessionId. Reuse the same id after reload to continue; omit or use a new id for a fresh chat.
Persist sessionId (e.g. localStorage) yourself — <AvisChat /> does not expose it. Use useAvisChat if you want the hook to expose sessionId for persistence.
Pick the right surface
| Goal | Use |
|------|-----|
| Built-in UI | <AvisChat /> |
| Custom UI | useAvisChat |
| Read/update sessionId easily | useAvisChat |
Knowledge base
If the project has a knowledge base in the dashboard, retrieval and grounding happen on the server. No extra SDK flags.
Components
<AvisChat />
Supports theme, model / models, sessionId, baseUrl, streaming, and optional floating mode.
<AvisChat
apiKey="your-api-key"
model="gpt-4o"
sessionId="optional-session-id"
theme="light"
autoFocus
className="my-chat"
/>Floating launcher (custom icon or logo)
floating={false}(default): the panel is embedded — it’s always visible (subject to your layout).floating={true}: a fixed circular launcher opens/closes the panel.
Use launcherImageSrc / launcherImageAlt for a logo URL, launcherIcon for any React node (overrides the image), or neither for a built-in glyph. The image fills the inner circle; launcherButtonStyle padding shows as a ring around the art.
floatingTitle: non-empty string → header row with title and ×. Omitted or blank → no header; close by clicking the launcher again.
Open state: defaultOpen (uncontrolled), or open + onOpenChange (controlled).
className: applies to the chat panel only, not the floating FAB. Style the FAB with launcherButtonClassName / launcherButtonStyle.
<AvisChat
apiKey="your-api-key"
floating
theme="dark"
launcherImageSrc="https://example.com/logo.png"
launcherImageAlt="Chat with us"
launcherPosition="bottom-right"
floatingTitle="Support"
showModelSelector={false}
/>launcherButtonStyle / sendButtonStyle use React’s CSSProperties (import type { CSSProperties } from "react").
AvisChat props
Required: apiKey or client. All other props are optional.
| Prop | Type | Default | Description |
|------|------|---------|-------------|
| apiKey | string | — | API key. Omit if client is set. |
| client | Avis | — | Ready-made client from @avis-ai/sdk-js. |
| baseUrl | string | https://api-ai.avi-s.in | API origin (no trailing slash). Dev proxy or self-hosted gateway. |
| model | string | "gpt-4o-mini" | Model id; backend must route it. |
| sessionId | string | generated | Conversation id for server-side memory. |
| theme | "light" \| "dark" | "light" | |
| models | ModelOption[] | curated list | { value, label, group? }; group → <optgroup>. |
| showModelSelector | boolean | true | Hide to lock UI to model. |
| autoFocus | boolean | false | Focus the input on mount; in floating mode also when the panel opens. |
| className | string | — | CSS class on the panel container (not the launcher). |
| floating | boolean | false | Floating FAB + toggle panel. |
| defaultOpen | boolean | false | (Floating) initial open if uncontrolled. |
| open | boolean | — | (Floating) controlled open. |
| onOpenChange | (open: boolean) => void | — | (Floating) open/close callback. |
| floatingTitle | string | — | (Floating) Title + ×, or omit for no header. |
| launcherPosition | "bottom-right" \| "bottom-left" | "bottom-right" | |
| launcherAriaLabel | string | "Open chat" | Launcher aria-label. |
| launcherButtonClassName | string | — | FAB className. |
| launcherButtonStyle | CSSProperties | — | FAB inline styles (merged after defaults). |
| sendButtonClassName | string | — | Send button className. |
| sendButtonStyle | CSSProperties | — | Send button inline styles (merged after defaults). |
| launcherIcon | ReactNode | — | Custom FAB content; wins over launcherImageSrc. |
| launcherImageSrc | string | — | Image URL on the FAB. |
| launcherImageAlt | string | "Chat" | Alt text for launcherImageSrc. |
The same hook fields apply to useAvisChat (apiKey, client, baseUrl, model, sessionId only).
useAvisChat hook
Headless streaming + sessionId + reset.
import { useAvisChat } from "@avis-ai/avischat";
function CustomChat() {
const { sessionId, messages, sendMessage, isLoading, error, reset } = useAvisChat({
apiKey: "your-api-key",
});
return (
<div>
{messages.map((m, i) => (
<div key={i}>{m.role}: {m.content}</div>
))}
<button type="button" onClick={() => sendMessage("Hello!")} disabled={isLoading}>
Send
</button>
{error && <p>{error.message}</p>}
<button type="button" onClick={reset}>New chat</button>
</div>
);
}Return value
| Field | Type | Description |
|-------|------|-------------|
| sessionId | string | Current id — persist and pass back as sessionId. |
| messages | AvisChatMessage[] | role + content. |
| sendMessage | (content: string) => Promise<void> | Sends user text; streams assistant reply. |
| isLoading | boolean | true while streaming. |
| error | Error \| null | Last request error. |
| reset | () => void | Clears messages/error; new sessionId. |
Session persistence (step by step)
- Load or create a
sessionId - Pass it into
AvisChatoruseAvisChat - When it changes, save it (e.g.
localStorage)
You own sessionId with <AvisChat />
function PersistentAvisChat() {
const [sessionId] = useState(
() => localStorage.getItem("avis-session") ?? crypto.randomUUID()
);
useEffect(() => {
localStorage.setItem("avis-session", sessionId);
}, [sessionId]);
return <AvisChat apiKey="your-api-key" sessionId={sessionId} />;
}useAvisChat returns sessionId
function PersistentCustomChat() {
const [storedSessionId] = useState(
() => localStorage.getItem("avis-session") ?? undefined
);
const { sessionId, messages, sendMessage, isLoading, error, reset } = useAvisChat({
apiKey: "your-api-key",
sessionId: storedSessionId,
});
useEffect(() => {
if (sessionId) localStorage.setItem("avis-session", sessionId);
}, [sessionId]);
return (
<div>
{messages.map((m, i) => (
<div key={i}>{m.role}: {m.content}</div>
))}
{error && <p>{error.message}</p>}
<button type="button" onClick={reset}>New conversation</button>
</div>
);
}Next.js / SSR
Don’t read localStorage during the first server render. Initialize sessionId in useState + useEffect (or pass undefined until mounted) so the tree matches SSR, then hydrate a stable id. Example:
"use client";
import { useState, useEffect } from "react";
import { AvisChat } from "@avis-ai/avischat";
const KEY = "avis-chat-session";
export function Chat() {
const [sessionId, setSessionId] = useState<string | null>(null);
useEffect(() => {
let id = localStorage.getItem(KEY);
if (!id) {
id = crypto.randomUUID();
localStorage.setItem(KEY, id);
}
setSessionId(id);
}, []);
if (!sessionId) return null; // or a skeleton
return (
<AvisChat
apiKey={process.env.NEXT_PUBLIC_AVIS_API_KEY!}
baseUrl={process.env.NEXT_PUBLIC_AVIS_BASE_URL}
sessionId={sessionId}
/>
);
}Using a proxy (recommended for production)
@avis-ai/sdk-js warns if a real apiKey runs in the browser. For production, proxy /v1/chat/completions (and related routes) from your origin and attach the secret on the server.
Option A — baseUrl only
<AvisChat
apiKey={process.env.NEXT_PUBLIC_AVIS_API_KEY!}
baseUrl={process.env.NEXT_PUBLIC_AVIS_BASE_URL}
/>Option B — shared Avis client
import { Avis } from "@avis-ai/sdk-js";
import { AvisChat } from "@avis-ai/avischat";
const client = new Avis({
apiKey: process.env.NEXT_PUBLIC_AVIS_API_KEY!,
baseUrl: "/api/avis-proxy",
});
export function Chat() {
return <AvisChat client={client} />;
}If baseUrl is unset, the default host is https://api-ai.avi-s.in.
TypeScript types
Re-exported from @avis-ai/avischat:
import type {
AvisChatProps,
AvisChatMessage,
ModelOption,
UseAvisChatOptions,
UseAvisChatReturn,
} from "@avis-ai/avischat";interface AvisChatMessage {
role: "system" | "user" | "assistant";
content: string;
}
interface ModelOption {
value: string;
label: string;
group?: string;
}
// UseAvisChatOptions = pick from AvisChatProps: apiKey, client, baseUrl, model, sessionId
// AvisChatProps extends UseAvisChatOptions with UI / floating fields (see props table).Styling & CSS
- Defaults are inline styles.
className— panel wrapper (embedded or floating panel).launcherButtonClassName— floating FAB only.launcherButtonStyle/sendButtonStyle— ReactCSSProperties, merged on top of defaults.- Messages scroll area: class
.avis-chat-messages(scrollbar theming for light/dark).
.my-chat {
height: 500px !important;
max-height: 90vh !important;
}Errors & accessibility
- Errors:
<AvisChat />shows the last error in a strip above the input (from failed stream or network). - Launcher: set
launcherAriaLabelfor a clear FAB label;aria-expandedtracks panel open state. - Model selector: labeled select (
aria-label="Select model") when visible.
Framework notes
- React 18+ required.
- Works with Vite, Next.js, CRA, etc.
- For Next.js App Router, use
"use client"on the component tree that importsAvisChat/useAvisChat, and follow the SSRsessionIdpattern above.
