@guidelytech/sveltekit
v0.8.0
Published
SvelteKit helpers for Guidely
Maintainers
Readme
@guidelytech/sveltekit
SvelteKit helpers: creates the client in the browser only and calls page() after every
navigation. Re-exports everything from @guidelytech/svelte.
pnpm add @guidelytech/sveltekit<!-- src/routes/+layout.svelte -->
<script lang="ts">
import { env } from "$env/dynamic/public";
import { initGuidely, guidelyEnv, syncGuidelyUser } from "@guidelytech/sveltekit";
let { data, children } = $props();
initGuidely({ ...guidelyEnv(env), debug: import.meta.env.DEV });
// data.user from +layout.server.ts — identifies on sign-in/change, resets on sign-out.
$effect(() =>
syncGuidelyUser(
data.user
? {
id: String(data.user.id),
traits: { name: data.user.name, email: data.user.email, role: data.user.role },
account: { id: String(data.user.companyId), traits: { name: data.user.companyName } },
}
: null,
),
);
</script>
{@render children()}// src/routes/+layout.server.ts
export const load = ({ locals }) => ({ user: locals.user ?? null });Page views are tracked automatically on every navigation; users are anonymous until identified.
The name and email traits are what the dashboard's Users list shows and searches, so send them
when you have them (otherwise users appear by id only). An account's name trait labels the
Account column. Traits merge on each identify, so existing users pick up their name and email the
next time they load the app.
| Export | Description |
|---|---|
| initGuidely(options) | Call once during root layout initialisation. Returns the client in the browser, null on the server. Uses afterNavigate from $app/navigation to call page() and destroys the client when the layout is destroyed. |
| guidelyEnv(env) | Reads PUBLIC_GUIDELY_PROJECT_ID/_ENV/_API_URL/_RELEASE ($env/static/public, $env/dynamic/public) or the VITE_GUIDELY_* equivalents (import.meta.env). |
| syncGuidelyUser(user \| null \| undefined) | Keeps the Guidely user in step with your session (re-exported from @guidelytech/svelte). |
| everything from @guidelytech/svelte | stores (including the flag stores flag, featureValue, flagDetails, allFlags, flagsReady), actions and context helpers. |
| createServerFlags, getFlags, loadFlags | Server helpers, also at @guidelytech/sveltekit/server (see below). |
Feature flags
Flags are on with the same public key. Evaluate them on the server and hand the payload to the browser so the first render matches — no flicker:
// src/hooks.server.ts
import { createServerFlags } from "@guidelytech/sveltekit/server";
export const handle = async ({ event, resolve }) => {
// One client per process (polls Guidely, evaluates locally). Env: GUIDELY_SDK_KEY or PUBLIC_GUIDELY_PROJECT_ID.
event.locals.flags = createServerFlags().forUser({ id: event.locals.user?.id, plan: event.locals.user?.plan });
return resolve(event);
};// src/routes/+layout.server.ts
import { loadFlags } from "@guidelytech/sveltekit/server";
export const load = async (event) => ({ user: event.locals.user ?? null, flags: await loadFlags(event) });<!-- src/routes/+layout.svelte -->
<script lang="ts">
import { untrack } from "svelte";
let { data, children } = $props();
initGuidely({ ...guidelyEnv(env), flags: untrack(() => data.flags) }); // { payload, attributes }
</script><!-- any component -->
<script lang="ts">
import { flag, featureValue } from "@guidelytech/sveltekit";
const newBanner = flag("new-dashboard-banner");
const color = featureValue("checkout-button-color", "blue");
</script>
{#if $newBanner}<NewBanner />{/if}In +page.server.ts and endpoints read locals.flags.isEnabled("…") / getValue(…) directly.
Add the type to src/app.d.ts:
declare global {
namespace App {
interface Locals {
flags: import("@guidelytech/sveltekit/server").FlagsEvaluator;
}
}
}
export {};| @guidelytech/sveltekit/server | Description |
|---|---|
| createServerFlags(options?) | The process-wide flags client (kept on globalThis across dev reloads). Options default to GUIDELY_SDK_KEY (pk_… or server-only gly_sk_…) or PUBLIC_GUIDELY_PROJECT_ID, GUIDELY_API_URL / PUBLIC_GUIDELY_API_URL, GUIDELY_ENV / PUBLIC_GUIDELY_ENV. On serverless adapters pass { pollingMode: "lazy" }. |
| getFlags(attributes, { client?, timeoutMs? }) | Getters for one user; waits up to timeoutMs (1500) for the first download. |
| loadFlags(event \| attributes, { client?, timeoutMs? }) | { payload, attributes } for initGuidely({ flags }); attributes come from event.locals.flags. The payload is null for a gly_sk_ client (it contains server-only flags). |
Use the same attributes on the server as the browser will have (id, traits, account) so both
agree; rollouts and experiments for anonymous visitors (no id on the server) settle in the browser.
