Skip to content

Svelte Adapter

The @presage-kit/svelte package provides Svelte stores and context helpers for building adaptive interfaces.

Terminal window
pnpm add @presage-kit/core @presage-kit/svelte

Use setAdaptiveClient() in your root layout to make the client available to all child components via Svelte’s context API.

src/routes/+layout.svelte
<script>
import { setAdaptiveClient } from '@presage-kit/svelte'
import { client } from '$lib/adaptive-client'
setAdaptiveClient(client)
</script>
<slot />

The primary function for resolving adaptation points. Returns Svelte readable stores that update reactively when user context changes.

<script lang="ts">
import { useAdaptive } from '@presage-kit/svelte'
const { selectedVariant, resolution, context, track } = useAdaptive('onboarding', {
variants: ['guided-tour', 'standard', 'minimal'] as const,
defaultVariant: 'standard',
})
function handleComplete() {
track('onboarding_completed')
}
</script>
{#if $selectedVariant === 'guided-tour'}
<h2>Welcome! Let us show you around.</h2>
<button on:click={handleComplete}>Complete tour</button>
{:else if $selectedVariant === 'standard'}
<h2>Welcome</h2>
{:else}
<p>Good to see you again.</p>
{/if}

Parameters:

ParamTypeDescription
adaptationIdstringUnique identifier for the adaptation point
config.variantsreadonly string[]Available variant IDs
config.defaultVariantstringFallback variant
config.strategyStrategyOptional. Defaults to { type: 'rules' }

Return type:

All reactive values are Svelte Readable stores — use the $ prefix to subscribe.

FieldTypeDescription
selectedVariantReadable<V>The selected variant string
resolutionReadable<ResolvedAdaptation>Full resolution details
contextReadable<UserContext>Current user context
track(event: string, properties?) => voidScoped track function

Automatic impression tracking: useAdaptive() watches for variant changes and automatically tracks presage:impression events.

A lighter function that returns only the selected variant as a readable store.

<script lang="ts">
import { useVariant } from '@presage-kit/svelte'
const variant = useVariant('cta-style', {
variants: ['primary', 'secondary', 'subtle'] as const,
defaultVariant: 'primary',
})
</script>
<button class="cta cta--{$variant}">Get Started</button>

Parameters: Same as useAdaptive().

Return type: Readable<V> — A store containing the selected variant string.

Returns the AdaptiveClient instance from context. Use for imperative operations.

<script lang="ts">
import { getAdaptiveClient } from '@presage-kit/svelte'
const client = getAdaptiveClient()
function handleExport() {
client.track('feature_used', { featureId: 'export' })
}
</script>
<button on:click={handleExport}>Export Data</button>

Throws an error if setAdaptiveClient() has not been called in a parent component.

Like the Vue adapter, the Svelte package does not ship a dedicated navigation component. Use evaluateAction() with a reactive declaration to reorder or hide navigation items.

<script lang="ts">
import { getAdaptiveClient } from '@presage-kit/svelte'
import { useAtom } from '@presage-kit/svelte'
import { derived } from 'svelte/store'
const client = getAdaptiveClient()
const baseItems = [
{ id: 'home', label: 'Home', href: '/' },
{ id: 'analytics', label: 'Analytics', href: '/analytics' },
{ id: 'settings', label: 'Settings', href: '/settings' },
]
const navItems = derived(useAtom(client.state), () => {
const action = client.evaluateAction('sidebar-nav')
if (action?.type === 'hide') return []
if (action?.type === 'reorder') {
const map = new Map(baseItems.map((item) => [item.id, item]))
const ordered = action.order.filter((id) => map.has(id)).map((id) => map.get(id)!)
const rest = baseItems.filter((i) => !action.order.includes(i.id))
return [...ordered, ...rest]
}
return baseItems
})
</script>
<nav>
{#each $navItems as item (item.id)}
<a href={item.href}>{item.label}</a>
{/each}
</nav>
src/lib/adaptive-client.ts
import { createAdaptiveClient, createLocalStorageDriver } from '@presage-kit/core'
export const client = createAdaptiveClient({
rules: [
{
id: 'power-user-sidebar',
adaptationId: 'sidebar-nav',
priority: 10,
conditions: {
all: [{ field: 'maturity', operator: 'eq', value: 'power' }],
},
action: { type: 'reorder', order: ['analytics', 'dashboards', 'settings', 'home'] },
},
{
id: 'new-user-tour',
adaptationId: 'onboarding',
priority: 10,
conditions: {
all: [{ field: 'maturity', operator: 'eq', value: 'new' }],
},
action: { type: 'show', variantId: 'guided-tour' },
},
{
id: 'experienced-skip',
adaptationId: 'onboarding',
priority: 5,
conditions: {
any: [
{ field: 'maturity', operator: 'eq', value: 'active' },
{ field: 'maturity', operator: 'eq', value: 'power' },
],
},
action: { type: 'show', variantId: 'minimal' },
},
],
persistence: {
driver: createLocalStorageDriver('svelte-demo'),
},
})
src/routes/+layout.svelte
<script>
import { setAdaptiveClient } from '@presage-kit/svelte'
import { client } from '$lib/adaptive-client'
setAdaptiveClient(client)
</script>
<div class="layout">
<SidebarNav />
<main>
<slot />
</main>
</div>
src/lib/components/Onboarding.svelte
<script lang="ts">
import { useAdaptive } from '@presage-kit/svelte'
const { selectedVariant, track } = useAdaptive('onboarding', {
variants: ['guided-tour', 'standard', 'minimal'] as const,
defaultVariant: 'standard',
})
function handleComplete() {
track('onboarding_completed')
}
</script>
{#if $selectedVariant === 'guided-tour'}
<div class="onboarding">
<h2>Welcome! Let us show you around.</h2>
<button on:click={handleComplete}>Complete Tour</button>
</div>
{:else if $selectedVariant === 'standard'}
<div class="onboarding">
<h2>What is new</h2>
<p>Check out the latest features.</p>
</div>
{:else}
<div class="onboarding">
<p>Welcome back!</p>
</div>
{/if}