Svelte
This guide shows you how to integrate Basestack Feature Flags into Svelte 5 applications using the JavaScript SDK (@basestack/flags-js). You'll build custom stores and utilities that provide reactive feature flags throughout your Svelte application.
Overview
This guide will help you build:
- Flags Store: A Svelte store that manages the SDK instance and flag state
- useFlag Function: A function to reactively access individual flags
- useFlags Function: A function to reactively access all flags
- Server Utilities: Helper functions for server-side usage (SvelteKit)
The implementation uses Svelte's reactivity system and stores to provide reactive flag state throughout your application.
Getting Started
Install Dependencies
Install the core JavaScript SDK. Svelte works perfectly with the JavaScript SDK.
npm install @basestack/flags-jspnpm install @basestack/flags-jsyarn add @basestack/flags-jsbun add @basestack/flags-jsEnvironment Variables
Configure your environment variables. The prefix depends on your build tool (Vite uses VITE_, SvelteKit uses PUBLIC_, etc.).
When it comes to environment variables, pay attention to the framework you're
using. For example, PUBLIC_ is specific to SvelteKit, while in
Vite.js, it would be VITE_.
# BASESTACK FEATURE FLAGS
VITE_FEATURE_FLAGS_BASE_URL="https://flags-api.basestack.co/v1"
VITE_FEATURE_FLAGS_PROJECT_KEY=""
VITE_FEATURE_FLAGS_ENVIRONMENT_KEY=""You can find your project and environment keys in your Basestack Feature Flags Dashboard.
Create Project Structure
Create a folder structure to organize your feature flags implementation:
File Structure:
lib/stores/flags.ts- Svelte store for flagslib/utils/flags.ts- Utility functions for flags
Implementation Guide
Step 1: Create the Flags Store
Create a Svelte store that manages the SDK instance and flag state:
import { writable, derived, get } from "svelte/store";
import { FlagsSDK, type Flag, type SDKConfig } from "@basestack/flags-js";
import { browser } from "$app/environment"; // SvelteKit only, remove if using plain Svelte
// Create SDK instance
function createFlagsClient(config: SDKConfig): FlagsSDK {
return new FlagsSDK({
baseURL: config.baseURL,
projectKey: config.projectKey,
environmentKey: config.environmentKey,
});
}
// Store state
interface FlagsState {
client: FlagsSDK | null;
flags: Flag[];
isLoading: boolean;
error: Error | null;
isInitialized: boolean;
}
// Create the store
function createFlagsStore(config: SDKConfig) {
const { subscribe, set, update } = writable<FlagsState>({
client: null,
flags: [],
isLoading: true,
error: null,
isInitialized: false,
});
// Initialize SDK
const client = createFlagsClient(config);
// Fetch all flags
async function fetchFlags() {
update((state) => ({ ...state, isLoading: true, error: null }));
try {
const { flags } = await client.getAllFlags();
update((state) => ({
...state,
flags,
isLoading: false,
isInitialized: true,
}));
} catch (error) {
update((state) => ({
...state,
error: error instanceof Error ? error : new Error(String(error)),
isLoading: false,
isInitialized: true,
}));
}
}
// Initialize on client side
if (browser) {
fetchFlags();
}
return {
subscribe,
client,
fetchFlags,
refresh: fetchFlags,
};
}
// Initialize store with config
const config: SDKConfig = {
baseURL: import.meta.env.VITE_FEATURE_FLAGS_BASE_URL,
projectKey: import.meta.env.VITE_FEATURE_FLAGS_PROJECT_KEY,
environmentKey: import.meta.env.VITE_FEATURE_FLAGS_ENVIRONMENT_KEY,
};
export const flagsStore = createFlagsStore(config);
// Derived stores for convenience
export const flags = derived(flagsStore, ($store) => $store.flags);
export const isLoading = derived(flagsStore, ($store) => $store.isLoading);
export const error = derived(flagsStore, ($store) => $store.error);If you're using plain Svelte (not SvelteKit), remove the import { browser } from "$app/environment" line and the if (browser) check.
Step 2: Create Utility Functions
Create utility functions to access flags:
import { get } from "svelte/store";
import { flagsStore } from "../stores/flags";
import type { Flag } from "@basestack/flags-js";
/**
* Get a single flag by slug
*/
export async function getFlag(slug: string): Promise<Flag> {
const store = get(flagsStore);
if (!store.client) {
throw new Error("Flags store not initialized");
}
// First check if flag exists in preloaded flags
const preloadedFlag = store.flags.find((f) => f.slug === slug);
if (preloadedFlag) {
return preloadedFlag;
}
// Otherwise fetch directly
return store.client.getFlag(slug);
}
/**
* Get all flags
*/
export async function getAllFlags(): Promise<Flag[]> {
const store = get(flagsStore);
if (!store.client) {
throw new Error("Flags store not initialized");
}
if (store.flags.length > 0) {
return store.flags;
}
const { flags } = await store.client.getAllFlags();
return flags;
}
/**
* Check if a flag is enabled
*/
export async function isFlagEnabled(slug: string): Promise<boolean> {
try {
const flag = await getFlag(slug);
return flag.enabled;
} catch {
return false;
}
}Step 3: Create Reactive Flag Functions
Create reactive functions using Svelte's reactivity:
import { readable } from "svelte/store";
import { flagsStore } from "../stores/flags";
import type { Flag } from "@basestack/flags-js";
/**
* Create a reactive store for a single flag
*/
export function useFlag<P = Record<string, unknown>>(slug: string) {
return readable<{
flag: Flag | null;
enabled: boolean;
payload: P | null;
isLoading: boolean;
error: Error | null;
}>(null, (set) => {
const unsubscribe = flagsStore.subscribe(async (state) => {
if (!state.isInitialized) {
set({
flag: null,
enabled: false,
payload: null,
isLoading: true,
error: null,
});
return;
}
if (state.error) {
set({
flag: null,
enabled: false,
payload: null,
isLoading: false,
error: state.error,
});
return;
}
// Find flag in preloaded flags
const flag = state.flags.find((f) => f.slug === slug);
if (flag) {
set({
flag,
enabled: flag.enabled,
payload: (flag.payload as P) ?? null,
isLoading: false,
error: null,
});
} else {
// Flag not found, try to fetch it
if (state.client) {
try {
const fetchedFlag = await state.client.getFlag(slug);
set({
flag: fetchedFlag,
enabled: fetchedFlag.enabled,
payload: (fetchedFlag.payload as P) ?? null,
isLoading: false,
error: null,
});
} catch (err) {
set({
flag: null,
enabled: false,
payload: null,
isLoading: false,
error: err instanceof Error ? err : new Error(String(err)),
});
}
}
}
});
return unsubscribe;
});
}
/**
* Get all flags reactively
*/
export function useFlags() {
return readable<{
flags: Flag[];
isLoading: boolean;
error: Error | null;
}>({ flags: [], isLoading: true, error: null }, (set) => {
const unsubscribe = flagsStore.subscribe((state) => {
set({
flags: state.flags,
isLoading: state.isLoading,
error: state.error,
});
});
return unsubscribe;
});
}Usage Examples
Using Flags in Components
Use the stores and utilities in any Svelte component:
<script lang="ts">
import { useFlag } from "$lib/utils/flags";
import { flagsStore } from "$lib/stores/flags";
interface HeaderPayload {
variant: string;
showLogo: boolean;
}
const headerFlag = useFlag<HeaderPayload>("header");
</script>
{#if $headerFlag?.isLoading}
<div>Loading flags...</div>
{:else if $headerFlag?.error}
<div>Error: {$headerFlag.error.message}</div>
{:else if $headerFlag?.enabled}
<header>
<h1>My App</h1>
{#if $headerFlag.payload}
<p>Variant: {$headerFlag.payload.variant}</p>
{/if}
</header>
{:else}
<header>
<h1>My App (Legacy)</h1>
</header>
{/if}
<button on:click={() => flagsStore.refresh()}>Refresh Flags</button>Display All Flags
Show all flags in a component:
<script lang="ts">
import { useFlags } from "$lib/utils/flags";
const allFlags = useFlags();
</script>
<div>
<h2>Feature Flags</h2>
{#if $allFlags.isLoading}
<p>Loading flags...</p>
{:else if $allFlags.error}
<p>Error: {$allFlags.error.message}</p>
{:else}
<ul>
{#each $allFlags.flags as flag (flag.slug)}
<li>
<strong>{flag.slug}</strong>:
{flag.enabled ? "enabled" : "disabled"}
</li>
{/each}
</ul>
{/if}
</div>Conditional Rendering
Use flags for conditional feature rendering:
<script lang="ts">
import { useFlag } from "$lib/utils/flags";
import NewFeature from "./NewFeature.svelte";
import LegacyFeature from "./LegacyFeature.svelte";
interface FeaturePayload {
theme: string;
}
const featureFlag = useFlag<FeaturePayload>("new-feature");
</script>
{#if $featureFlag?.enabled}
<NewFeature theme={$featureFlag.payload?.theme} />
{:else}
<LegacyFeature />
{/if}Using in Script Blocks
Access flags in script blocks for logic:
<script lang="ts">
import { onMount } from "svelte";
import { getFlag, isFlagEnabled } from "$lib/utils/flags";
let showNewFeature = false;
onMount(async () => {
showNewFeature = await isFlagEnabled("new-feature");
// Or get full flag data
const flag = await getFlag("header");
console.log("Flag payload:", flag.payload);
});
</script>
{#if showNewFeature}
<NewFeature />
{:else}
<LegacyFeature />
{/if}SvelteKit Integration
For SvelteKit, initialize flags in +layout.svelte:
<script lang="ts">
import { onMount } from "svelte";
import { flagsStore } from "$lib/stores/flags";
onMount(() => {
// Flags are automatically initialized in the store
// But you can refresh them if needed
flagsStore.refresh();
});
</script>
<slot />Or use server-side loading in +page.server.ts:
import { ServerFlagsSDK } from "$lib/utils/flags-server";
export async function load() {
const client = ServerFlagsSDK.getInstance();
const { flags } = await client.getAllFlags();
return {
flags,
};
}Create server utilities:
import { FlagsSDK, type SDKConfig } from "@basestack/flags-js";
export class ServerFlagsSDK {
private static instance: FlagsSDK;
private static config: SDKConfig = {
baseURL: process.env.PUBLIC_FEATURE_FLAGS_BASE_URL,
projectKey: process.env.PUBLIC_FEATURE_FLAGS_PROJECT_KEY!,
environmentKey: process.env.PUBLIC_FEATURE_FLAGS_ENVIRONMENT_KEY!,
};
public static getInstance(): FlagsSDK {
if (!ServerFlagsSDK.instance) {
ServerFlagsSDK.instance = new FlagsSDK(ServerFlagsSDK.config);
}
return ServerFlagsSDK.instance;
}
}Advanced Usage
Custom Store with Actions
Create a more advanced store with actions:
import { writable } from "svelte/store";
import { FlagsSDK, type Flag, type SDKConfig } from "@basestack/flags-js";
function createFlagsStore(config: SDKConfig) {
const { subscribe, set, update } = writable<{
flags: Flag[];
isLoading: boolean;
error: Error | null;
}>({
flags: [],
isLoading: true,
error: null,
});
const client = new FlagsSDK(config);
return {
subscribe,
async init() {
update((state) => ({ ...state, isLoading: true }));
try {
await client.init();
const { flags } = await client.getAllFlags();
update({ flags, isLoading: false, error: null });
} catch (error) {
update({
flags: [],
isLoading: false,
error: error instanceof Error ? error : new Error(String(error)),
});
}
},
async refresh() {
const { flags } = await client.getAllFlags();
update((state) => ({ ...state, flags }));
},
clearCache() {
client.clearCache();
},
};
}
export const flagsStore = createFlagsStore({
baseURL: import.meta.env.VITE_FEATURE_FLAGS_BASE_URL,
projectKey: import.meta.env.VITE_FEATURE_FLAGS_PROJECT_KEY,
environmentKey: import.meta.env.VITE_FEATURE_FLAGS_ENVIRONMENT_KEY,
});Best Practices
- Initialize Early: Initialize the flags store as early as possible in your app
- Handle Loading States: Always check
isLoadingbefore using flags - Type Payloads: Use TypeScript generics for type-safe payloads
- Error Handling: Provide fallback behavior when flags fail to load
- Use Stores: Leverage Svelte stores for reactive flag state
Troubleshooting
Store Not Initialized
If you see "Flags store not initialized":
Solution: Make sure the store is created and initialized. The store auto-initializes on creation, but you can manually call flagsStore.refresh() if needed.
Flags Not Updating
If flags aren't updating:
- Check Cache: Flags are cached for 5 minutes by default
- Call Refresh: Use
flagsStore.refresh()to manually refresh - Verify Environment: Ensure you're using the correct environment key
TypeScript Errors
If you're getting TypeScript errors:
- Type Payloads: Use generics:
useFlag<YourPayloadType>("slug") - Check Imports: Ensure you're importing from the correct paths
- Install Types: Types are included with
@basestack/flags-js
Next Steps
- Explore the JavaScript SDK documentation for more SDK features