No results for ""
EXPAND ALL
  • Home
  • API docs

Using LaunchDarkly with Svelte

Read time: 13 minutes
Last edited: Sep 18, 2024

Overview

This guide explains how to connect LaunchDarkly with Svelte and SvelteKit on both the client and server sides.

Svelte is a JavaScript tool set similar to React. It changes user interfaces (UIs) by using document object model (DOM) updates and by managing state. However, while React performs its updates with client-side script that runs in the browser, Svelte does much of its processing at build-time. This lets it minimize the JavaScript bundle size and limit the amount of JavaScript required to perform DOM updates. This can make your application lighter and potentially faster than a comparable React or Vue-based application.

SvelteKit is full-stack framework for building web applications based on Svelte. It is officially supported by the Svelte project and recently reached a 1.0 stable milestone. It provides a similar full-stack JavaScript client and server architecture to what Next.js provides in relation to React. SvelteKit lets you write server-side code using Node.js and add file-based routing to a Svelte application.

Using LaunchDarkly with Svelte lets you toggle features in the UI without needing to reload the page, modify the text on the page, or even test new styles and layout in production. Combining LaunchDarkly with SvelteKit also offers the ability to modify and test server-side logic in your code using feature flags.

The example in this guide demonstrates how to use LaunchDarkly SDKs on both the client and server sides in an application built with SvelteKit.

Prerequisites

To complete this guide, it will be helpful, though not required, if you have the following prerequisites:

  • Experience using LaunchDarkly in client-side JavaScript applications using the JavaScript SDK.
  • Some experience using LaunchDarkly in server-side JavaScript applications using the Node.js (server-side) SDK.

Install the guide exercise files locally

For this guide, we created a simple SvelteKit project that demonstrates how to incorporate LaunchDarkly feature flags in both the backend server-side code and the frontend client-side code. The app is a basic blog site that pulls blog post data from the DEV API based upon specific usernames. It also builds an "About" page using Markdown content loaded via the file system on the server. The project is available on GitHub.

To run the project locally, clone the GitHub project, navigate to it in the terminal, and run npm install. This installs all of the project's dependencies, including Svelte and SvelteKit.

Choose a LaunchDarkly SDK

Because the example application has flags that are evaluated on both the server-side and the client-side, it requires a separate SDK for each.

Server-side code in SvelteKit runs in a Node.js environment, so you need to use LaunchDarkly's Node.js (server-side) SDK. To learn more, read Node.js (server-side) SDK.

The SDK is included in the project's dependencies, but you can also install it with npm:

npm install @launchdarkly/node-server-sdk

However, client-side code must use the client-side JavaScript SDK. You do not need a framework-specific client-side SDK for Svelte. To learn more, read JavaScript SDK reference.

This SDK is also included in the project's dependencies, but you can also install it with npm:

npm install launchdarkly-js-client-sdk

The relationship between server- and client-side code in SvelteKit

One of the unique aspects of SvelteKit is the concept of universal load components. This means that JavaScript within a component is, by default, run on both the client and the server. This lets it "rehydrate" the client, updating the page with new data, without reloading the full page from the server. However, this behavior can cause build errors if you are using both the client-side JavaScript and server-side Node.js SDKs. You can avoid these errors by isolating your usage of the two different kinds of SDKs.

If the server-side Node.js SDK loads in an environment that also runs in the browser, it will cause build errors because the environment lacks modules like util, fs, and others. To prevent this, isolate usage of the server-side Node.js SDK to server-side only code in SvelteKit.

The example application isolates usage of the server-side Node.js SDK to server load components, which are typically identified by the -server in their name, and only run on the server. To learn more about universal versus server load components, read the SvelteKit Universal vs server documentation.

If the client-side JavaScript SDK loads in an environment that also runs on the server, it will cause errors because the Node.js environment lacks browser defaults like window. To prevent this, isolate browser-specific code. SvelteKit provides a default $app/environment module that includes a browser value to indicate whether the app is running in the browser or the server.

Use the module to isolate browser code, as shown below:

if (browser) {
// browser specific code
}

Set up server-side rendering (SSR)

A typical SvelteKit route or page is a +page.svelte file and is paired with a JavaScript +page.js file that provides the data for the page. Because we need to isolate our server-side usage, the data file is named +page.server.js, which tells SvelteKit to only run this on the server. For example, the example site's home page is in /src/routes/+page.svelte. This file contains route-specific UI code, JavaScript code that runs on both the client and server, and CSS. It is paired with a data loading file called /src/api/+page.server.js that provides the data backend for this page, supplying it with whatever data it needs from the server. The data loading file runs entirely on the server-side.

A .svelte file does not require a matching .js file, but, unless the page requires no data, you'll typically need one. You can read more about loading data in the SvelteKit documentation.

Create a server-side wrapper

The example application centralizes all calls to the SDK into a single file. This allows you to initialize the SDK in a single location, reuse the client instance if it is already initialized, and standardize the way your code interacts with the API. The example application includes a basic server-side wrapper for the server-side Node.js SDK.

The server-side wrapper is in /src/lib/launchdarkly/server.js. The wrapper does not implement all of the SDK functionality but has the following functions:

  • initialize() initializes the SDK using the LaunchDarkly SDK key that is stored in an environment variable in an .env file. The environment variable is named VITE_LAUNCHDARKLY_SDK_KEY as SvelteKit only exposes environment variables with the VITE_ prefix. It then waits for the SDK to initialize and returns the SDK client. To learn more about SvelteKit environment variables, read this FAQ.
  • getClient() checks if the client has already initialized and, if not, initializes it. In either case, it returns an instance of the SDK client.
  • getFlagValue() returns the value of a LaunchDarkly flag based upon the flag's key and the evaluation context that is passed in. If no evaluation context is passed in, the function assumes a "user" context kind and will pass a generic anonymous key.

Here is an example:

import LaunchDarkly from 'launchdarkly-node-server-sdk'
let launchDarklyClient
async function initialize() {
const client = LaunchDarkly.init(import.meta.env.VITE_LAUNCHDARKLY_SDK_KEY)
try {
await client.waitForInitialization({timeout: 10});
} catch (err) {
// timeout or initialization failure
}
return client
}
async function getClient() {
if (launchDarklyClient) return launchDarklyClient
return (launchDarklyClient = await initialize())
}
export async function getFlagValue(key, context) {
const client = await getClient()
let flagValue
if (!context) {
context = {
key: 'anonymous',
}
}
flagValue = await client.variation(key, context, false)
return flagValue
}

In the example, any server-side logic that requires a flag value imports the getFlagValue() function and passes it the flag key:

import { getFlagValue } from "../../lib/launchdarkly/server";
...
const myFlag = await getFlagValue("flag-key-123abc");

You do not need to initialize the client. The wrapper does that as needed. The getFlagValue() function is asynchronous, so it returns a JavaScript Promise. You can use the await keyword if you're using it within an async function or use .then() otherwise.

Use server-side flag values

The example application includes several routes that use LaunchDarkly flags to alter server-side logic in the application. The server-side logic that calls the Node.js SDK is kept within server-side-only data loading components to ensure that the code runs only on the server.

In this first example, the application evaluates a multivariate flag, and loads data based on its value.

The following example code imports the server-side wrapper and uses the getFlagValue() function to load the value of the featured-username flag. This is a string flag that contains the username of the person whose posts the application home page loads. These posts come from DEV API. The posts and the featured username are passed back from the endpoint to display on the page.

import { getFlagValue } from '../../lib/launchdarkly/server'
export async function get() {
const featuredUsername = await getFlagValue('featured-username')
const response = await fetch(`https://dev.to/api/articles?username=${featuredUsername}&page=1&per_page=10`)
let posts = await response.json()
// clean up the username and organization
posts = posts.map(post => {
post.username = post.organization ? post.organization.username : post.user.username
return post
})
return {
body: {
posts,
featuredUsername,
},
}
}

Data from this file is automatically available within the page component, in the data variable.

<script>
export let data;
export let posts = data.posts;
export let featuredCategory = data.featuredCategory;
</script>

In this second example, the application evaluates another multivariate flag and displays content based on the value.

In the following example code, the data load component uses the value of a string flag to determine which version of Markdown content to load. You could easily modify this example to pass in end-user data to getFlagValue(). Then you could use the new-about-us flag to target only internal end users. For example, you could allow the marketing team to test new copy in production without impacting external end users.

import { processMarkdown } from '../../lib/markdown'
import { getFlagValue } from '../../lib/launchdarkly/server'
export async function get() {
const aboutUsPage = await getFlagValue('new-about-us')
const { frontMatter, body } = await processMarkdown(`./src/posts/${aboutUsPage}.md`)
return {
body: {
frontMatter: frontMatter,
body: body,
},
}
}

Set up client-side rendering

Svelte and SvelteKit don't require any special framework libraries to work with LaunchDarkly. You can work with the standard JavaScript client-side SDK. However, if you are running in SvelteKit, you must isolate calls to the SDK into code that only runs on the client, not the server.

Integration with Svelte

The standard client-side JavaScript integration with Svelte requires no special libraries or code. You can just work with it exactly as it is shown in the JavaScript SDK documentation.

For example, the Svelte component below:

  1. Imports the JavaScript SDK.
  2. Initializes the client using the client-side ID and an anonymous context key.
  3. When the client is initialized and ready, gets the value of the show-button boolean flag.
  4. Sets a listener on the change event for the flag so that any change to the flag value in LaunchDarkly will be immediately reflected on the client.
  5. If the flag variation is true, displays the button within the component. If it is false, does not display the button.
<script>
import * as LaunchDarkly from 'launchdarkly-js-client-sdk'
let showButton = false
const client = LaunchDarkly.initialize('<LAUNCHDARKLY_CLIENT_ID>', {
key: 'anonymous',
})
client.on('ready', () => {
setShowButton(client.variation('show-button', false))
client.on('change:show-button', setShowButton)
})
function setShowButton(val) {
showButton = val
}
</script>
<style>
main {
font-family: sans-serif;
text-align: center;
}
</style>
<main>
<p>This will always show</p>
{#if showButton}
<button>Flag is true</button>
{/if}
</main>

Although it doesn't require any special set up to implement LaunchDarkly in a Svelte application, there can be advantages to creating a wrapper around the JavaScript SDK interaction, especially when you use the SDK in a SvelteKit application.

Create a client-side wrapper

Much like the server-side interaction, it can be useful to create a wrapper for interaction with the client-side SDK. This can accomplish several things, including isolating the initialization code to a single location, automatically initializing the client if it hasn't been initialized yet, and automatically adding change listeners on flags.

The client-side wrapper is in the file /src/lib/launchdarkly/client.js. The wrapper does not implement all the SDK functionality, but has the following functions:

  • initialize() initializes the SDK using the LaunchDarkly SDK key that is stored in an environment variable in the .env file. If no evaluation context is passed, the function will pass a generic anonymous context key. It then waits for the SDK to initialize and returns the SDK client.
  • getClient() checks if the client has already been initialized and, if not, initializes it. In either case, it returns an instance of the SDK client.
  • getFlagValue() returns the value of a LaunchDarkly flag based upon the flag's key. If a callback function is passed, it adds that as a change listener specific to the passed flag key, meaning that the passed function will only be called on a change to that specific flag rather than a change to any flag in the connected environment.
import * as LaunchDarkly from 'launchdarkly-js-client-sdk'
let launchDarklyClient
async function initialize(context) {
if (!context) {
context = {
key: 'anonymous',
}
}
const client = LaunchDarkly.initialize(import.meta.env.VITE_LAUNCHDARKLY_SDK_CLIENT, context)
await client.waitForInitialization()
return client
}
async function getClient() {
if (launchDarklyClient) return launchDarklyClient
return (launchDarklyClient = await initialize())
}
export async function getFlagValue(key, fnChangeListener) {
const client = await getClient()
let flagValue
flagValue = await client.variation(key, false)
if (fnChangeListener) {
client.on('change:' + key, fnChangeListener)
}
return flagValue
}

In the example code, any client-side logic that requires a flag value imports the getFlagValue() function. You also need to import the browser value to perform a check and verify that your code is running in the browser environment.

import { browser } from '$app/env'
import { getFlagValue } from '../launchdarkly/client'

Before you can use the function, you must wrap it in a browser check. Use a .then() to properly set the value, unless the call to getFlagValue() exists within an async function. A simple solution for this is to create a setter function for the value.

Here's how:

let myFlag
if (browser) {
getFlagValue('flag-key-123abc').then(setMyFlag)
}
function setMyFlag(val) {
myFlag = val
}

The wrapper initializes the client as needed. The getFlagValue() function is asynchronous, so it returns a JavaScript Promise.

If you want to set a change listener for this particular flag key, pass the setter as a secondary argument to getFlagValue().

let myFlag
if (browser) {
getFlagValue('flag-key-123abc', setMyFlag).then(setMyFlag)
}
function setMyFlag(val) {
myFlag = val
}

If a change to the flag within LaunchDarkly is detected, the setter function is called and your application's UI will automatically update accordingly.

Use flag values client-side in JavaScript

The example application shows two ways client-side flag values can be set within a Svelte component. Both ways use the client-side wrapper described above.

The first example, show-about-us, is a boolean flag that determines whether the "About Us" element appears within the navigation. It uses a basic setter function that is set to listen for changes to the flag on LaunchDarkly.

The second example, featured-category, is a string flag that is used both server-side and client-side to determine which blog post category is featured. On the client-side, this impacts the featured category navigation item. The featured-category example shows how the setter can also be used to massage the result returned from LaunchDarkly. In this case, the setter title-cases the returned featured category string.

Here are the examples:

<script>
import { browser } from "$app/environment";
import { getFlagValue } from "../launchdarkly/client";
let showAboutUs;
let featuredCategory;
if (browser) {
getFlagValue("show-about-us", setShowAboutUs).then(setShowAboutUs);
getFlagValue("featured-category", setFeaturedCategory).then(
setFeaturedCategory
);
}
function setShowAboutUs(val) {
showAboutUs = val;
}
function setFeaturedCategory(val) {
featuredCategory = val.charAt(0).toUpperCase() + val.slice(1);
}
</script>

Use client-side flag values in markup

You do not need complex markup or even the use of an await block. The values of the flag variables, showAboutUs and featuredCategory in the client-side portion of the example application, never exist as a pending Promise. The values are either undefined, while the SDK is initialized and the flag state is returned, or they are defined, in this case with either a boolean or string value, respectively. You can use both the string value and the boolean value in your markup.

Here's how:

<header>
<div class="nav">
<ul>
<li>
<a href="/">Home</a>
</li>
<li>
<a href={`/posts/${featuredCategory}`}>Posts from {featuredCategory}</a>
</li>
{#if showAboutUs}
<li class="about">
<a href="/about">About Us</a>
</li>
{/if}
</ul>
</div>
</header>

There are two important things to note here:

  • After values are defined, Svelte manages the DOM updates necessary for rendering. There is no need for any JavaScript code to update these values.
  • Values will update immediately on the client if a change is made in LaunchDarkly. As soon as a new value is received, Svelte updates the UI accordingly without requiring a page refresh. This is because change listeners exist for each flag key.

Conclusion

Svelte continues to grow in popularity, jumping from 8% usage in 2019 to 21% in 2022 while remaining the frontend framework JavaScript developers are most interested in, according to the latest State of JavaScript survey. As it becomes an integral part of more companies' website infrastructure, it becomes increasingly important that they can integrate feature management into Svelte web applications.

The great news is that, because Svelte is just JavaScript, you can use LaunchDarkly to succeed with Svelte today, without any specialized SDKs. This makes it easy to integrate LaunchDarkly however you prefer, either by including directly on a page using Svelte, or by wrapping the SDK in a JavaScript wrapper that centralizes all your application's interactions with feature flags.

Want to know more? Start a trial.

Your 14-day trial begins as soon as you sign up. Get started in minutes using the in-app Quickstart. You'll discover how easy it is to release, monitor, and optimize your software.

Want to try it out? Start a trial.