Svelte SDK
@cstar.help/svelte provides Svelte 5 rune classes for customer-facing chat and public
knowledge base features. Uses $state and $derived for automatic reactivity
— no stores, no boilerplate.
Installation
npm install @cstar.help/svelteThis automatically installs @cstar.help/js (the core client) as a dependency.
Requires Svelte 5+ and SvelteKit 2+. For real-time chat, also install @supabase/supabase-js (optional — falls back to polling without it).
Two Provider Architecture
The Svelte SDK has two independent providers — one for chat (authenticated, real-time) and one for the knowledge base (public, no auth needed). Use one or both depending on your needs.
CStarChatProvider
Wrap your chat widget with the chat provider. It creates a ChatClient and makes it
available via context. Requires teamSlug.
<script>
import { CStarChatProvider } from '@cstar.help/svelte';
let { children } = $props();
</script>
<CStarChatProvider
teamSlug="acme"
supabaseUrl={import.meta.env.VITE_SUPABASE_URL}
supabaseAnonKey={import.meta.env.VITE_SUPABASE_ANON_KEY}
>
{@render children()}
</CStarChatProvider>Without Supabase config, chat falls back to polling (every 3 seconds). The provider auto-disconnects on component destruction.
Chat State Classes
ChatState
Identify customers and manage the chat session. Must call identify() with an HMAC signature
before using other chat classes.
<script>
import { getChatClient, ChatState } from '@cstar.help/svelte';
const client = getChatClient();
const chat = new ChatState(client);
async function startChat() {
await chat.identify({
externalId: 'user_123',
email: 'jane@example.com',
name: 'Jane Doe',
timestamp: Math.floor(Date.now() / 1000)
}, 'hmac_signature_from_your_server');
}
</script>
{#if !chat.isIdentified}
<button onclick={startChat}>Start Chat</button>
{:else}
<p>Chat ready! {chat.isRealtimeReady ? '(live)' : '(polling)'}</p>
{/if}ConversationsState
List and create customer conversations. Auto-fetches on construction.
<script>
import { getChatClient, ConversationsState } from '@cstar.help/svelte';
const client = getChatClient();
const convos = new ConversationsState(client);
async function startNew() {
const convo = await convos.create({
subject: 'Help with billing',
message: 'I have a question about my invoice.'
});
console.log('Created:', convo.id);
}
</script>
{#if convos.isLoading}
<p>Loading...</p>
{:else}
{#each convos.conversations as c (c.id)}
<div>
<h3>{c.subject}</h3>
<span>{c.status} · {c.messageCount} messages</span>
</div>
{/each}
<button onclick={startNew}>New Conversation</button>
{/if}MessagesState
Send and receive messages with optimistic updates and real-time delivery. Call destroy() on unmount to clean up subscriptions.
<script>
import { onDestroy } from 'svelte';
import { getChatClient, MessagesState } from '@cstar.help/svelte';
let { ticketId } = $props();
const client = getChatClient();
const thread = new MessagesState(client, ticketId);
let draft = $state('');
async function handleSend() {
await thread.send(draft); // Optimistic — appears instantly
draft = '';
}
onDestroy(() => thread.destroy());
</script>
{#each thread.messages as msg (msg.id)}
<div>
<strong>{msg.sender_name}</strong>
<p>{msg.content}</p>
</div>
{/each}
<input bind:value={draft} placeholder="Type a message..." />
<button onclick={handleSend}>Send</button>TypingState
Show agent typing indicators. Auto-clears after 4 seconds of inactivity. Call destroy() on unmount.
<script>
import { onDestroy } from 'svelte';
import { getChatClient, TypingState } from '@cstar.help/svelte';
let { ticketId } = $props();
const client = getChatClient();
const typing = new TypingState(client, ticketId);
onDestroy(() => typing.destroy());
</script>
{#if typing.typingAgents.length > 0}
<p>{typing.typingAgents.map(a => a.agentName).join(', ')} is typing...</p>
{/if}CStarLibraryProvider
Wrap your help center with the library provider. No authentication needed — the knowledge base is public.
<script>
import { CStarLibraryProvider } from '@cstar.help/svelte';
let { children } = $props();
</script>
<CStarLibraryProvider teamSlug="acme">
{@render children()}
</CStarLibraryProvider>Library State Classes
CategoriesState
<script>
import { getLibraryClient, CategoriesState } from '@cstar.help/svelte';
const client = getLibraryClient();
const cats = new CategoriesState(client);
</script>
{#if cats.isLoading}
<p>Loading...</p>
{:else}
{#each cats.categories as cat (cat.id)}
<a href="/help/{cat.slug}">
{cat.name} ({cat.article_count} articles)
</a>
{/each}
{/if}ArticlesState
<script>
import { getLibraryClient, ArticlesState } from '@cstar.help/svelte';
let { categorySlug } = $props();
const client = getLibraryClient();
const list = new ArticlesState(client, { categorySlug, limit: 10 });
</script>
{#if list.isLoading}
<p>Loading...</p>
{:else}
{#each list.articles as article (article.id)}
<a href="/help/articles/{article.slug}">
<h3>{article.title}</h3>
<p>{article.excerpt}</p>
</a>
{/each}
{/if}ArticleSearchState
Full-text search with built-in 300ms debounce. Call destroy() on unmount.
<script>
import { onDestroy } from 'svelte';
import { getLibraryClient, ArticleSearchState } from '@cstar.help/svelte';
const client = getLibraryClient();
const searcher = new ArticleSearchState(client);
let query = $state('');
$effect(() => {
searcher.search(query); // Debounced automatically
});
onDestroy(() => searcher.destroy());
</script>
<input bind:value={query} placeholder="Search articles..." />
{#if searcher.isLoading}
<p>Searching...</p>
{/if}
<p>{searcher.totalCount} results</p>
{#each searcher.results as article (article.id)}
<a href="/help/articles/{article.slug}">{article.title}</a>
{/each}SvelteKit SSR
For server-side data loading, use the core @cstar.help/js client with a secret API
key in +page.server.js.
import { CStarClient } from '@cstar.help/js';
export async function load() {
const cstar = new CStarClient({
apiKey: import.meta.env.CSTAR_SECRET_KEY,
teamId: import.meta.env.CSTAR_TEAM_ID
});
const { data } = await cstar.articles.list({
status: 'published',
category: 'Getting Started'
});
return { articles: data };
}Community State Classes
Wrap in <CStarCommunityProvider> for public community forum access — no auth
required. See the Svelte SDK README for full usage examples.
Available Classes
CStarCommunityProvider — Provider component. Pass teamSlug to connect.
TopicsState — Reactive topics list. Properties: topics, isLoading, error. Methods: refresh().
PostsState — Reactive posts list with filters. Properties: posts, count, isLoading, error. Methods: refresh().
PostState — Single post with comments. Properties: data, isLoading, error. Methods: refresh().
CommunitySearchState — Search across posts. Properties: results, isLoading, error. Methods: search(query).
Context Functions
getCommunityClient() — Retrieve the client from the nearest CStarCommunityProvider.
setCommunityClient(client) — Manually set the client in context.
Quick Example
// Inside a child of CStarCommunityProvider
import { TopicsState, PostsState, getCommunityClient } from '@cstar.help/svelte';
const client = getCommunityClient();
const topics = new TopicsState(client);
const posts = new PostsState(client, { sort: 'votes' });
// topics.topics, posts.posts, posts.count are reactive ($state)