Skip to main content

Svelte Integration

Integrate Ozwell into your Svelte or SvelteKit application with a reactive component.

Installation​

npm install @ozwell/svelte
# or
yarn add @ozwell/svelte
# or
pnpm add @ozwell/svelte

Quick Start​

Svelte​

<script>
import { OzwellChat } from '@ozwell/svelte';

const apiKey = import.meta.env.VITE_OZWELL_API_KEY;
const agentId = import.meta.env.VITE_OZWELL_AGENT_ID;
</script>

<h1>My App</h1>

<OzwellChat {apiKey} {agentId} />

SvelteKit​

<!-- +layout.svelte -->
<script>
import { OzwellChat } from '@ozwell/svelte';
import { browser } from '$app/environment';
import { PUBLIC_OZWELL_API_KEY, PUBLIC_OZWELL_AGENT_ID } from '$env/static/public';
</script>

<slot />

{#if browser}
<OzwellChat
apiKey={PUBLIC_OZWELL_API_KEY}
agentId={PUBLIC_OZWELL_AGENT_ID}
/>
{/if}

Component API​

<OzwellChat />​

<script>
import { OzwellChat } from '@ozwell/svelte';

let context = { page: window.location.pathname };

function handleReady() {
console.log('Widget ready');
}

function handleUserShare(event) {
// Only fires when user explicitly shares
console.log('User shared:', event.detail);
}
</script>

<OzwellChat
apiKey="ozw_scoped_xxxxxxxx"
agentId="agent_xxxxxxxx"
theme="auto"
position="bottom-right"
primaryColor="#4f46e5"
width="400px"
height="600px"
autoOpen={false}
greeting="Hello! How can I help?"
placeholder="Type a message..."
{context}
on:ready={handleReady}
on:open={() => console.log('Opened')}
on:close={() => console.log('Closed')}
on:user-share={(e) => console.log('User shared:', e.detail)}
on:error={(e) => console.error(e.detail)}
/>

Props​

PropTypeDefaultDescription
apiKeystringrequiredScoped API key
agentIdstringrequiredAgent ID
theme'light' | 'dark' | 'auto''auto'Color theme
position'bottom-right' | 'bottom-left''bottom-right'Widget position
primaryColorstring'#4f46e5'Accent color
widthstring'400px'Chat window width
heightstring'600px'Chat window height
autoOpenbooleanfalseOpen on mount
greetingstringAgent defaultInitial message
placeholderstring'Type a message...'Input placeholder
contextRecord<string, unknown>{}Context data for agent
showTriggerbooleantrueShow default trigger button

Events​

EventDetailDescription
on:readyβ€”Widget initialized
on:openβ€”Chat window opened
on:closeβ€”Chat window closed
on:user-shareunknownUser explicitly shared data
on:errorOzwellErrorError occurred

Privacy Note: There is no on:message event. Conversation content is private between the user and Ozwell.


Store & Actions​

ozwellStore​

Access the Ozwell state reactively:

<script>
import { OzwellChat, ozwellStore } from '@ozwell/svelte';

const apiKey = import.meta.env.VITE_OZWELL_API_KEY;
const agentId = import.meta.env.VITE_OZWELL_AGENT_ID;
</script>

<p>Ready: {$ozwellStore.isReady}</p>
<p>Open: {$ozwellStore.isOpen}</p>

<OzwellChat {apiKey} {agentId} />

Actions​

<script>
import {
OzwellChat,
ozwellStore,
openChat,
closeChat,
toggleChat,
sendMessage,
setContext
} from '@ozwell/svelte';

const apiKey = import.meta.env.VITE_OZWELL_API_KEY;
const agentId = import.meta.env.VITE_OZWELL_AGENT_ID;

function handleSendHello() {
sendMessage('Hello!');
}
</script>

<button on:click={openChat}>Open</button>
<button on:click={closeChat}>Close</button>
<button on:click={toggleChat}>Toggle</button>
<button on:click={handleSendHello}>Say Hello</button>

<OzwellChat {apiKey} {agentId} />

Examples​

With SvelteKit Page Context​

<!-- +layout.svelte -->
<script>
import { OzwellChat } from '@ozwell/svelte';
import { page } from '$app/stores';
import { browser } from '$app/environment';
import { PUBLIC_OZWELL_API_KEY, PUBLIC_OZWELL_AGENT_ID } from '$env/static/public';

$: context = {
page: $page.url.pathname,
query: Object.fromEntries($page.url.searchParams),
};
</script>

<slot />

{#if browser}
<OzwellChat
apiKey={PUBLIC_OZWELL_API_KEY}
agentId={PUBLIC_OZWELL_AGENT_ID}
{context}
/>
{/if}

Custom Trigger Button​

<script>
import { OzwellChat, ozwellStore, openChat } from '@ozwell/svelte';

const apiKey = import.meta.env.VITE_OZWELL_API_KEY;
const agentId = import.meta.env.VITE_OZWELL_AGENT_ID;
</script>

{#if !$ozwellStore.isOpen}
<button class="chat-trigger" on:click={openChat}>
πŸ’¬ Need help?
</button>
{/if}

<OzwellChat {apiKey} {agentId} showTrigger={false} />

<style>
.chat-trigger {
position: fixed;
bottom: 20px;
right: 20px;
padding: 12px 24px;
background: #4f46e5;
color: white;
border: none;
border-radius: 24px;
cursor: pointer;
}
</style>

With Authentication​

<script>
import { OzwellChat } from '@ozwell/svelte';
import { browser } from '$app/environment';
import { user } from '$lib/stores/auth';
import { PUBLIC_OZWELL_API_KEY, PUBLIC_OZWELL_AGENT_ID } from '$env/static/public';

$: context = {
userId: $user?.id,
email: $user?.email,
plan: $user?.subscription?.plan,
};
</script>

{#if browser}
<OzwellChat
apiKey={PUBLIC_OZWELL_API_KEY}
agentId={PUBLIC_OZWELL_AGENT_ID}
{context}
/>
{/if}

Route-Based Visibility​

<!-- +layout.svelte -->
<script>
import { OzwellChat } from '@ozwell/svelte';
import { page } from '$app/stores';
import { browser } from '$app/environment';
import { PUBLIC_OZWELL_API_KEY, PUBLIC_OZWELL_AGENT_ID } from '$env/static/public';

const hiddenRoutes = ['/checkout', '/auth', '/admin'];

$: showChat = !hiddenRoutes.some(route =>
$page.url.pathname.startsWith(route)
);
</script>

<slot />

{#if browser && showChat}
<OzwellChat
apiKey={PUBLIC_OZWELL_API_KEY}
agentId={PUBLIC_OZWELL_AGENT_ID}
/>
{/if}

Analytics Integration​

Track chat lifecycle events (not contentβ€”that's private):

<script>
import { OzwellChat } from '@ozwell/svelte';
import { track } from '$lib/analytics';

const apiKey = import.meta.env.VITE_OZWELL_API_KEY;
const agentId = import.meta.env.VITE_OZWELL_AGENT_ID;

function handleOpen() {
track('chat_opened');
}

function handleClose() {
track('chat_closed');
}

function handleUserShare(event) {
// Only fires when user explicitly shares
track('user_shared_data', event.detail);
}
</script>

<OzwellChat
{apiKey}
{agentId}
on:open={handleOpen}
on:close={handleClose}
on:user-share={handleUserShare}
/>

TypeScript​

Full TypeScript support included:

<script lang="ts">
import { OzwellChat } from '@ozwell/svelte';
import type { OzwellError } from '@ozwell/svelte';

const apiKey: string = import.meta.env.VITE_OZWELL_API_KEY;
const agentId: string = import.meta.env.VITE_OZWELL_AGENT_ID;

function handleUserShare(event: CustomEvent<unknown>) {
// Only fires when user explicitly shares
console.log('User shared:', event.detail);
}

function handleError(event: CustomEvent<OzwellError>) {
console.error(event.detail.code, event.detail.message);
}
</script>

<OzwellChat
{apiKey}
{agentId}
on:user-share={handleUserShare}
on:error={handleError}
/>

Privacy Note: There is no on:message event or Message type for content. Conversations are private.


SvelteKit SSR Considerations​

The widget must render client-side only. Use the browser check:

<script>
import { browser } from '$app/environment';
</script>

{#if browser}
<!-- Client-only content -->
{/if}

Or use a dynamic import in +page.svelte:

<script>
import { onMount } from 'svelte';

let OzwellChat;

onMount(async () => {
const module = await import('@ozwell/svelte');
OzwellChat = module.OzwellChat;
});
</script>

{#if OzwellChat}
<svelte:component
this={OzwellChat}
apiKey="..."
agentId="..."
/>
{/if}

Troubleshooting​

Widget Not Appearing​

  1. Ensure browser check is in place for SvelteKit
  2. Verify API key and agent ID are correct
  3. Check browser console for errors

Context Not Updating​

Use reactive declarations:

<script>
import { page } from '$app/stores';

// βœ… Reactive
$: context = { page: $page.url.pathname };

// ❌ Not reactive
const context = { page: $page.url.pathname };
</script>

Multiple Instances​

Only render one <OzwellChat /> component per page.


Next Steps​