> ## Documentation Index
> Fetch the complete documentation index at: https://docs.traffical.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Svelte SDK

> Svelte 5 runes and SvelteKit integration for parameter resolution, event tracking, and SSR hydration.

`@traffical/svelte` integrates Traffical with Svelte's runes and SvelteKit's data-loading system. It wraps `@traffical/js-client`, so everything the browser client supports works here too.

Requires Svelte 5 (uses runes).

## Installation

<CodeGroup>
  ```bash npm theme={null}
  npm install @traffical/svelte
  ```

  ```bash pnpm theme={null}
  pnpm add @traffical/svelte
  ```

  ```bash yarn theme={null}
  yarn add @traffical/svelte
  ```
</CodeGroup>

## Setup

Wrap your app with `TrafficalProvider` in a root layout:

```svelte theme={null}
<!-- src/routes/+layout.svelte -->
<script lang="ts">
  import { TrafficalProvider } from "@traffical/svelte";

  let { children } = $props();
</script>

<TrafficalProvider config={{
  orgId: "org_acme",
  projectId: "proj_marketplace",
  env: "production",
  apiKey: import.meta.env.PUBLIC_TRAFFICAL_API_KEY,
}}>
  {@render children()}
</TrafficalProvider>
```

Use an **SDK key** (`traffical_sk_...`, scopes `sdk:read`+`sdk:write`) in browser code — it is browser-safe.

## Resolving parameters

Use `useTraffical` in any component:

```svelte theme={null}
<script lang="ts">
  import { useTraffical } from "@traffical/svelte";

  const { params, track } = useTraffical({
    defaults: {
      "checkout.button.color": "#1E6EFB",
      "checkout.button.label": "Buy now",
    },
  });
</script>

<button
  style:background-color={params["checkout.button.color"]}
  onclick={() => track("cta_click")}
>
  {params["checkout.button.label"]}
</button>
```

`params` is reactive via runes — Svelte automatically re-renders when the bundle refreshes or context changes.

## Setting context

`TrafficalProvider` takes exactly two props: `config` and `children`. Context is supplied through `config.contextFn` — a function called on each resolution, so hooks re-resolve when the values it returns change:

```svelte theme={null}
<script lang="ts">
  import { TrafficalProvider } from "@traffical/svelte";
  import { page } from "$app/state";

  let { children } = $props();
</script>

<TrafficalProvider
  config={{
    orgId: "org_acme",
    projectId: "proj_marketplace",
    env: "production",
    apiKey: import.meta.env.PUBLIC_TRAFFICAL_API_KEY,
    contextFn: () => ({
      userId: page.data.user?.id ?? "anonymous",
      locale: navigator.language,
    }),
  }}
>
  {@render children()}
</TrafficalProvider>
```

To customize the unit key, pass `unitKeyFn` inside `config` as well.

## SvelteKit SSR

Fetch the bundle in a server `load` function and pass it through to the client to avoid a second fetch:

```typescript theme={null}
// src/routes/+layout.server.ts
import { loadTrafficalBundle } from "@traffical/svelte/sveltekit";
import { TRAFFICAL_API_KEY } from "$env/static/private";

export async function load({ fetch }) {
  const { bundle } = await loadTrafficalBundle({
    orgId: "org_acme",
    projectId: "proj_marketplace",
    env: "production",
    apiKey: TRAFFICAL_API_KEY,
    fetch,    // SvelteKit's fetch for proper caching
  });

  return { traffical: { bundle } };
}
```

```svelte theme={null}
<!-- src/routes/+layout.svelte -->
<script lang="ts">
  import { TrafficalProvider } from "@traffical/svelte";
  let { data, children } = $props();
</script>

<TrafficalProvider
  config={{
    orgId: "org_acme",
    projectId: "proj_marketplace",
    env: "production",
    apiKey: import.meta.env.PUBLIC_TRAFFICAL_API_KEY,
    initialBundle: data.traffical.bundle,
  }}
>
  {@render children()}
</TrafficalProvider>
```

The server resolves with the bundle. The client picks up the same bundle via `initialBundle` and hydrates without a second fetch. No flash of original content.

<Note>
  The server-fetched bundle goes through `initialBundle` — that is what marks the provider ready during SSR. `localConfig` is the separate build-time/offline fallback bundle. Without an `initialBundle` (client-only usage), `params` start at your defaults and update as soon as the first background fetch lands.
</Note>

See [SSR patterns](/sdks/ssr) for the full pattern, including per-page pre-resolution.

## Anonymous users and `identify`

The Svelte SDK uses the browser client's stable-ID handling. When the user logs in:

`useTrafficalClient()` returns `{ client, ready, error }` — destructure `client` (it can be `null` until the provider initializes):

```svelte theme={null}
<script lang="ts">
  import { useTrafficalClient } from "@traffical/svelte";

  const { client } = useTrafficalClient();

  function login(user) {
    client?.identify(user.id);
  }
</script>
```

## Next steps

<CardGroup cols={2}>
  <Card title="SSR patterns" icon="server" href="/sdks/ssr">
    Avoiding FOOC with SvelteKit and Next.js.
  </Card>

  <Card title="Canonical experiments" icon="book-open" href="/guides/canonical-experiments">
    Patterns for web UI and SSR tests.
  </Card>
</CardGroup>
