Svelte Adapter
The @presage-kit/svelte package provides Svelte stores and context helpers for building adaptive interfaces.
Installation
Section titled “Installation”pnpm add @presage-kit/core @presage-kit/svelteContext Setup
Section titled “Context Setup”Use setAdaptiveClient() in your root layout to make the client available to all child components via Svelte’s context API.
<script> import { setAdaptiveClient } from '@presage-kit/svelte' import { client } from '$lib/adaptive-client'
setAdaptiveClient(client)</script>
<slot />useAdaptive() Store
Section titled “useAdaptive() Store”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:
| Param | Type | Description |
|---|---|---|
adaptationId | string | Unique identifier for the adaptation point |
config.variants | readonly string[] | Available variant IDs |
config.defaultVariant | string | Fallback variant |
config.strategy | Strategy | Optional. Defaults to { type: 'rules' } |
Return type:
All reactive values are Svelte Readable stores — use the $ prefix to subscribe.
| Field | Type | Description |
|---|---|---|
selectedVariant | Readable<V> | The selected variant string |
resolution | Readable<ResolvedAdaptation> | Full resolution details |
context | Readable<UserContext> | Current user context |
track | (event: string, properties?) => void | Scoped track function |
Automatic impression tracking: useAdaptive() watches for variant changes and automatically tracks presage:impression events.
useVariant() Store
Section titled “useVariant() Store”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.
getAdaptiveClient()
Section titled “getAdaptiveClient()”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.
useAdaptiveOrder() Store
Section titled “useAdaptiveOrder() Store”A generic ordering function for any list of items. Returns a Readable store of items that are automatically reordered, hidden, or preserved based on adaptation rules. Use this when you need adaptive ordering for non-navigation content such as feature cards, pricing tiers, or tab panels.
<script lang="ts"> import { useAdaptiveOrder } from '@presage-kit/svelte'
const features = [ { id: 'analytics', title: 'Analytics', description: 'Track user behavior' }, { id: 'automation', title: 'Automation', description: 'Automate repetitive tasks' }, { id: 'integrations', title: 'Integrations', description: 'Connect your tools' }, ]
const orderedFeatures = useAdaptiveOrder('feature-highlights', features)</script>
<div class="feature-grid"> {#each $orderedFeatures as feature (feature.id)} <div class="feature-card"> <h3>{feature.title}</h3> <p>{feature.description}</p> </div> {/each}</div>With a reorder rule, items are rearranged based on user context:
{ id: 'power-user-features', adaptationId: 'feature-highlights', priority: 10, conditions: { all: [{ field: 'maturity', operator: 'eq', value: 'power' }], }, action: { type: 'reorder', order: ['automation', 'integrations', 'analytics'] },}Parameters:
| Param | Type | Description |
|---|---|---|
id | string | Adaptation point identifier |
items | T[] (extends Orderable) | Array of items, each with at least an id field |
Return type: Readable<T[]> — A store containing the ordered items. Returns an empty array when a hide action matches.
useAdaptiveNav() Store
Section titled “useAdaptiveNav() Store”A thin wrapper around useAdaptiveOrder(), specialized for navigation lists. Internally delegates all logic to useAdaptiveOrder().
Returns a Readable store of navigation items that are automatically reordered or hidden by adaptation rules.
<script lang="ts"> import { useAdaptiveNav } from '@presage-kit/svelte'
const items = [ { id: 'home', label: 'Home', href: '/' }, { id: 'analytics', label: 'Analytics', href: '/analytics' }, { id: 'settings', label: 'Settings', href: '/settings' }, ]
const navItems = useAdaptiveNav('sidebar-nav', items)</script>
<nav> {#each $navItems as item (item.id)} <a href={item.href}>{item.label}</a> {/each}</nav>With a reorder rule, the navigation order changes based on user context:
{ id: 'power-user-nav-order', adaptationId: 'sidebar-nav', priority: 10, conditions: { all: [{ field: 'maturity', operator: 'eq', value: 'power' }], }, action: { type: 'reorder', order: ['analytics', 'home', 'settings'] },}Parameters:
| Param | Type | Description |
|---|---|---|
id | string | Adaptation point identifier |
items | T[] (extends NavItem) | Array of items, each with at least an id field |
Return type: Readable<T[]> — A store containing the ordered items. Returns an empty array when a hide action matches.
Alternative: Manual navigation with evaluateAction()
Section titled “Alternative: Manual navigation with evaluateAction()”You can also use evaluateAction() with a derived() store for full control over reorder and hide logic.
<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>Full Example: SaaS Dashboard
Section titled “Full Example: SaaS Dashboard”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'), },})<script> import { setAdaptiveClient } from '@presage-kit/svelte' import { client } from '$lib/adaptive-client'
setAdaptiveClient(client)</script>
<div class="layout"> <SidebarNav /> <main> <slot /> </main></div><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}