mirror of
https://github.com/Unleash/unleash.git
synced 2025-01-20 00:08:02 +01:00
Sveltekit tutorial (#6538)
Simple Sveltekit tutorial --------- Co-authored-by: Nnenna Ndukwe <nnenna.s.ndukwe@gmail.com>
This commit is contained in:
parent
3fc8a4f9f4
commit
3539f3db02
BIN
website/docs/feature-flag-tutorials/sveltekit/20240202174256.png
Normal file
BIN
website/docs/feature-flag-tutorials/sveltekit/20240202174256.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 61 KiB |
BIN
website/docs/feature-flag-tutorials/sveltekit/feat.png
Normal file
BIN
website/docs/feature-flag-tutorials/sveltekit/feat.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 408 KiB |
@ -0,0 +1,273 @@
|
||||
---
|
||||
title: How to Implement Feature Flags in SvelteKit
|
||||
description: "How to use Unleash feature flags with SvelteKit."
|
||||
---
|
||||
|
||||
Hello and welcome to another tutorial. This is about adding feature flags to an app made with [SvelteKit](https://kit.svelte.dev/), [Unleash](https://www.getunleash.io/) and the official [Unleash Svelte SDK](https://docs.getunleash.io/reference/sdks/svelte).
|
||||
|
||||
We'll make a paired-down habits app to keep track of your new year's resolutions. The feature flag will be used to change the number of habits a user can add.
|
||||
|
||||
While this is not meant to be a complete product, we can leverage feature flags using a full stack framework like Next.js or SvelteKit. The completed code for this implementation is available in [a Github repository](https://github.com/alvinometric/unleash-sveltekit).
|
||||
|
||||
- [Setup](#setup)
|
||||
- [Create a basic habits app](#create-a-basic-habits-app)
|
||||
- [Adding habits and premium features](#adding-habits-and-premium-features)
|
||||
- [Showing a different component based on the feature flag](#showing-a-different-component-based-on-the-feature-flag)
|
||||
- [Conclusion](#conclusion)
|
||||
|
||||
## Setup
|
||||
|
||||
Create a skeleton SvelteKit project named "habits".
|
||||
|
||||
```sh
|
||||
npm create svelte@latest habits
|
||||
```
|
||||
|
||||
We'll need a few more dependencies. You can install these in one command below:
|
||||
|
||||
```sh
|
||||
npm i date-fns @unleash/proxy-client-svelte
|
||||
```
|
||||
|
||||
## Create a basic habits app
|
||||
|
||||
We'll use Svelte stores to keep track of a global array of habits. For the sake of simplicity, we won't store these habits anywhere yet (feel free to add localStorage or a database). Our basic habit app will only consist of 3 files.
|
||||
|
||||
First, a global store that will contain our habits and their completion dates. Just JavaScript, no Svelte yet.
|
||||
|
||||
```js
|
||||
// src/lib/stores.js
|
||||
import { writable } from "svelte/store";
|
||||
|
||||
export const habitStore = writable([
|
||||
{
|
||||
id: 1,
|
||||
name: "Walk 10k steps",
|
||||
completedDays: [],
|
||||
},
|
||||
]);
|
||||
```
|
||||
|
||||
Then, we'll create an `App.svelte` file for our main logic.
|
||||
|
||||
```svelte
|
||||
<script>
|
||||
// src/lib/App.svelte
|
||||
import { format, addDays } from 'date-fns';
|
||||
import Habit from '$lib/Habit.svelte';
|
||||
import { habitStore } from '$lib/stores.js';
|
||||
import AddHabit from '../lib/AddHabit.svelte';
|
||||
let maxHabits = 2;
|
||||
|
||||
// go back 5 days
|
||||
const dates = new Array(5).fill(0).map((_, i) => {
|
||||
let today = new Date();
|
||||
return addDays(today, -i);
|
||||
});
|
||||
</script>
|
||||
|
||||
<AddHabit {maxHabits} />
|
||||
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Habit</th>
|
||||
{#each dates as date}
|
||||
<th>{format(date, 'MMM do')}</th>
|
||||
{/each}
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
<tbody>
|
||||
{#each $habitStore as habit}
|
||||
<Habit {habit} {dates} />
|
||||
{/each}
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
```
|
||||
|
||||
Next, update the `+page.svelte` file (our index route) to include our app.
|
||||
|
||||
```svelte
|
||||
<script>
|
||||
// src/routes/+page.svelte
|
||||
import App from '../lib/App.svelte';
|
||||
</script>
|
||||
|
||||
<App />
|
||||
```
|
||||
|
||||
To complete the basic setup of the app, add a component for each habit that be checked on and off using this code snippet:
|
||||
|
||||
```svelte
|
||||
<script>
|
||||
// src/lib/Habit.svelte
|
||||
import { habitStore } from '$lib/stores.js';
|
||||
import { format } from 'date-fns';
|
||||
|
||||
export let habit;
|
||||
export let dates;
|
||||
|
||||
function toggleDay(day) {
|
||||
let updatedDays = [...habit.completedDays];
|
||||
|
||||
const index = updatedDays.indexOf(day);
|
||||
if (index !== -1) {
|
||||
updatedDays.splice(index, 1);
|
||||
} else {
|
||||
updatedDays.push(day);
|
||||
}
|
||||
|
||||
habitStore.update((items) => {
|
||||
return items.map((item) => {
|
||||
if (item.id === habit.id) {
|
||||
return { ...item, completedDays: updatedDays };
|
||||
}
|
||||
return item;
|
||||
});
|
||||
});
|
||||
}
|
||||
</script>
|
||||
|
||||
<tr>
|
||||
<td>{habit.name}</td>
|
||||
|
||||
{#each dates as date}
|
||||
<td>
|
||||
<input
|
||||
type="checkbox"
|
||||
on:click={() => toggleDay(date)}
|
||||
checked={habit.completedDays.includes(date)}
|
||||
/>
|
||||
{format(date, 'MMM do')}
|
||||
</td>
|
||||
{/each}
|
||||
</tr>
|
||||
```
|
||||
|
||||
Now we have a fully functioning Svelte app in all its glory! Essentially, it's a table with checkboxes.
|
||||
|
||||
![Our habits app, a table with each habit as a row](./20240202174256.png)
|
||||
|
||||
## Adding habits and premium features
|
||||
|
||||
We have the basics of the app set up, but we could make it more user-friendly. Let's add some more functionality:
|
||||
|
||||
- Add the ability for users create their own habits
|
||||
- Limit the number of habits a user can create to a certain amount so we can turn this into a commercial product.
|
||||
|
||||
Let's do all of this in another component named `AddHabit.svelte`.
|
||||
|
||||
```svelte
|
||||
<script>
|
||||
// src/lib/AddHabit.svelte
|
||||
import { habitStore } from '$lib/stores.js';
|
||||
|
||||
export let maxHabits = 3;
|
||||
|
||||
let habitsFull = false;
|
||||
|
||||
function addHabit(e) {
|
||||
let numHabits = $habitStore.length;
|
||||
|
||||
if (numHabits === maxHabits) {
|
||||
habitsFull = true;
|
||||
} else {
|
||||
let form = e.target;
|
||||
const formData = new FormData(e.target);
|
||||
|
||||
habitStore.update((items) => {
|
||||
items.push({ id: items.length + 1, name: formData.get('name'), completedDays: [] });
|
||||
return items;
|
||||
});
|
||||
|
||||
// reset the form
|
||||
form.reset();
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<dialog open={habitsFull}>
|
||||
<h2>❌ Maximum Habits Reached</h2>
|
||||
<p>You can only have up to {maxHabits} on the free tier. Purchase a premium version to unlock more.</p>
|
||||
<form method="dialog">
|
||||
<button>OK</button>
|
||||
</form>
|
||||
</dialog>
|
||||
|
||||
<form on:submit|preventDefault={addHabit}>
|
||||
<input type="text" name="name" />
|
||||
<button type="submit"> Add new habit </button>
|
||||
</form>
|
||||
```
|
||||
|
||||
What's happening here? A few things:
|
||||
|
||||
- An input and a button to add new habits to the store, until an arbitrary limit is reached
|
||||
- A `maxHabits` prop is used to determine that limit
|
||||
- When this maximum limit is reached, a modal dialog opens
|
||||
- We reset the form after submission to clear the input
|
||||
|
||||
<video
|
||||
width="600px"
|
||||
preload="metadata"
|
||||
autoplay
|
||||
loop
|
||||
title="A maximum number of habits on the free tier"
|
||||
src="/media/habits.mp4"
|
||||
controls> </video>
|
||||
|
||||
## Showing a different component based on the feature flag
|
||||
|
||||
On to the main topic, adding feature flags.
|
||||
|
||||
Go to your Unleash dashboard, and create new project (you're welcome to use the default project here).
|
||||
![Create a new project in Unleash](./proj.png)
|
||||
|
||||
Next, create a feature flag called `maxHabitsIncreased`.
|
||||
|
||||
![create a feature flag called "maxHabitsIncreased"](./feat.png)
|
||||
|
||||
Based on whether this flag is enabled or not, we'll set the `maxHabits` value to either 6 or 2. You could set this directly in a flag value if you wanted as well.
|
||||
|
||||
### Basic toggle
|
||||
|
||||
We'll use the Svelte SDK to wrap a context provider around `App.svelte` like so:
|
||||
|
||||
```svelte
|
||||
<script>
|
||||
// src/routes/+page.svelte
|
||||
import App from '../lib/App.svelte';
|
||||
import { FlagProvider } from '@unleash/proxy-client-svelte';
|
||||
|
||||
const config = {
|
||||
url: 'https://eu.app.unleash-hosted.com/jdfkdjfkd/api/frontend', // Your Front-end API
|
||||
clientKey: '', // Front-end API token (or proxy client key)
|
||||
appName: 'habits'
|
||||
};
|
||||
</script>
|
||||
|
||||
<FlagProvider {config}>
|
||||
<App />
|
||||
</FlagProvider>
|
||||
```
|
||||
|
||||
Note that I’m using the URL and API key directly in the code right now, but you’d want to put these in an env file.
|
||||
|
||||
Now that our SDK is setup, we can modify our `App.svelte` to set the value of the variable based on the feature flag.
|
||||
|
||||
```diff
|
||||
+ import { useFlag } from '@unleash/proxy-client-svelte';
|
||||
+ const maxHabitsIncreased = useFlag('maxHabitsIncreased');
|
||||
+ let maxHabits = $maxHabitsIncreased ? 6 : 2;
|
||||
- lex maxHabits = 3;
|
||||
```
|
||||
|
||||
## Conclusion
|
||||
|
||||
You now have a SvelteKit app with feature flags. More precisely, you've learned:
|
||||
|
||||
- How to make a habit tracking app with SvelteKit
|
||||
- How to add a feature flag to a full stack app using Unleash
|
||||
- The different approaches to feature flagging on a static vs SSR context
|
BIN
website/docs/feature-flag-tutorials/sveltekit/proj.png
Normal file
BIN
website/docs/feature-flag-tutorials/sveltekit/proj.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 85 KiB |
@ -151,6 +151,11 @@ module.exports = {
|
||||
label: 'Next.js',
|
||||
id: 'feature-flag-tutorials/nextjs/implementing-feature-flags',
|
||||
},
|
||||
{
|
||||
type: 'doc',
|
||||
label: 'Sveltekit',
|
||||
id: 'feature-flag-tutorials/sveltekit/feature-flags-sveltekit',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
|
@ -9445,6 +9445,11 @@ prism-react-renderer@^1.3.1, prism-react-renderer@^1.3.5:
|
||||
resolved "https://registry.yarnpkg.com/prism-react-renderer/-/prism-react-renderer-1.3.5.tgz#786bb69aa6f73c32ba1ee813fbe17a0115435085"
|
||||
integrity sha512-IJ+MSwBWKG+SM3b2SUfdrhC+gu01QkV2KmRQgREThBfSQRoufqRfxfHUxpG1WcaFjP+kojcFyO9Qqtpgt3qLCg==
|
||||
|
||||
prism-svelte@^0.5.0:
|
||||
version "0.5.0"
|
||||
resolved "https://registry.yarnpkg.com/prism-svelte/-/prism-svelte-0.5.0.tgz#c4aeffeaddb179cfef213aab91ee785b66d22992"
|
||||
integrity sha512-db91Bf3pRGKDPz1lAqLFSJXeW13mulUJxhycysFpfXV5MIK7RgWWK2E5aPAa71s8TCzQUXxF5JOV42/iOs6QkA==
|
||||
|
||||
prismjs@^1.28.0:
|
||||
version "1.29.0"
|
||||
resolved "https://registry.yarnpkg.com/prismjs/-/prismjs-1.29.0.tgz#f113555a8fa9b57c35e637bba27509dcf802dd12"
|
||||
|
Loading…
Reference in New Issue
Block a user