Svelte SDK
@cstar.help/svelte ships rune classes — $state and $derived all the way down. Skip the stores, skip the boilerplate. New up a class with your
params; the data, loading, and error fields are reactive.
Installation
npm install @cstar.help/sveltePulls in @cstar.help/js as a transitive dep. Needs Svelte 5+ and SvelteKit 2+. Real-time
messaging is on by default.
Browse by topic
Drilling into something specific? Topics with a dedicated sub-page are linked below; the rest scroll to the matching section on this page.
Setup
Chat classes
Library classes
Recipes
Two providers, two surfaces
Chat needs auth and runs over real-time. Library is public, safe to render statically. Separate provider trees so one tree can't leak state into the other.
CStarChatProvider
Wrap your chat widget with the chat provider. It creates a ChatClient and makes it
available via context. Only teamSlug is required — real-time delivery is enabled by default
with an automatic polling fallback.
<script>
import { CStarChatProvider } from '@cstar.help/svelte';
let { children } = $props();
</script>
<CStarChatProvider teamSlug="acme">
{@render children()}
</CStarChatProvider>Pass realtime=false to disable the live connection and poll instead (every 3 seconds
by default). 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}TicketsState
List and create the customer's tickets. Auto-fetches on construction.
<script>
import { getChatClient, TicketsState } from '@cstar.help/svelte';
const client = getChatClient();
const tix = new TicketsState(client);
async function startNew() {
const ticket = await tix.create({
title: 'Help with billing',
message: 'I have a question about my invoice.'
});
console.log('Created:', ticket.id);
}
</script>
{#if tix.isLoading}
<p>Loading...</p>
{:else}
{#each tix.tickets as t (t.id)}
<div>
<h3>{t.title}</h3>
<span>{t.status} · {t.messageCount} messages</span>
</div>
{/each}
<button onclick={startNew}>New Ticket</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)