Customizing
Everything below is optional. The defaults give a working chat; this page shows the knobs you are most likely to turn, from the server prompt to a widget you build yourself. The full lists are in Configuration and Theming.
The server options are the same on every framework: pass them to aide() on SvelteKit, or to
createAide() on Next.js and elsewhere. The widget examples are Svelte; the React
<ChatWidget> takes the same props (see Next.js and React).
Shape the answers
The server assembles the prompt from your system prompt, a persona, extra facts and the site
outline. Change any of them in the aide() config:
// src/hooks.server.ts
import { aide } from '@chat-aide/sveltekit/server';
export const handle = aide({
provider,
origin: env.ORIGIN,
persona: {
name: 'Pip',
tone: 'friendly and brief',
audience: 'developers evaluating the product',
language: 'English',
refusals: ['legal advice', 'questions about competitors']
},
facts: ['Support is at [email protected].', 'The free plan has 3 seats.']
});personais rendered under the system prompt.factscarries things your pages do not say: prices, contact addresses, opening hours.systemPromptreplaces the default entirely. Pass a string, or a function that receives{ route, site, persona }. Keep its output stable between requests, or the provider's prompt cache cannot be reused (Configuration → the prompt).
Choose what it reads
aide({
provider,
pages: {
include: ['/changelog'], // extra crawl seeds
exclude: ['/admin/**', '/drafts/*'] // never read, mapped or served
}
});The crawl starts at the root, follows links, and reads your /sitemap.xml and /llms.txt if
you have them. If your llms.txt lists pages, its order becomes the outline's order. For content
in a CMS, return pages yourself with pages.source (Configuration).
Suggest first questions
Suggestions appear as buttons before the first message. Set them on the server, per route:
aide({
provider,
suggestions: {
'*': ['What does this site do?', 'How do I get started?'],
'/pricing': ['Is there a free plan?']
}
});or on the widget with the same shape: <ChatWidget suggestions={['…']} />. Up to four are shown.
Change the words
Every string of the widget can be replaced, in any language:
<ChatWidget
strings={{
title: 'Ask the docs',
greeting: 'Hi! What are you building?',
placeholder: 'Type a question…',
disclosure: (provider) => `Your question is sent to ${provider}.`
}}
/>The keys are greeting, placeholder, send, stop, retry, open, close, title,
disclosure, copy, copied and errors.
Restyle the widget
- shadcn theme (default): it reads your
--background,--primary,--borderand other shadcn variables, and follows.dark. Change your theme and the widget follows. This site does exactly that. - plain theme: override
--aide-*custom properties in one rule. - placement:
position(bottom-right,bottom-left,top-right,top-left),offset, andmode:floating,inline(in the page flow, e.g. a docs column) orsheet(a side panel).
[data-aide-theme='plain'] {
--aide-accent: rebeccapurple;
--aide-radius: 8px;
}Bring your own launcher
Replace the floating button with anything, for example a button in your header:
<ChatWidget>
{#snippet launcher({ open, toggle })}
<button onclick={toggle} aria-expanded={open}>Ask AI</button>
{/snippet}
</ChatWidget>Build your own widget
The headless layer gives you the store and unstyled parts. Each part carries a data-part
attribute to style against:
<script>
import { createChat, ChatWindow } from '@chat-aide/sveltekit/headless';
const chat = createChat({ endpoint: '/api/chat' });
</script>
<button onclick={() => chat.toggle()}>Chat</button>
{#if chat.isOpen}
<ChatWindow {chat} />
{/if}createChat exposes messages, status, suggestions, send, stop, retry, open, close
and more. See Theming → headless.
React to events
Client callbacks (onopen, onsend, onresponse, onerror, …) and matching aide:* window
events feed analytics. Server callbacks (onMessage, onResponse, onRefused, onError) run
after the response and never delay it; use them for logging and usage reports.
Gate access
authorize(event) runs before each chat request. Return false to refuse, or a Response of your
own. Combine it with enabled={false} on the widget to hide the chat until a visitor consents or
signs in.