mirror of
				https://github.com/Unleash/unleash.git
				synced 2025-10-27 11:02:16 +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.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## 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).
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Next, create a feature flag called `maxHabitsIncreased`.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					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',
 | 
					                    label: 'Next.js',
 | 
				
			||||||
                    id: 'feature-flag-tutorials/nextjs/implementing-feature-flags',
 | 
					                    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"
 | 
					  resolved "https://registry.yarnpkg.com/prism-react-renderer/-/prism-react-renderer-1.3.5.tgz#786bb69aa6f73c32ba1ee813fbe17a0115435085"
 | 
				
			||||||
  integrity sha512-IJ+MSwBWKG+SM3b2SUfdrhC+gu01QkV2KmRQgREThBfSQRoufqRfxfHUxpG1WcaFjP+kojcFyO9Qqtpgt3qLCg==
 | 
					  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:
 | 
					prismjs@^1.28.0:
 | 
				
			||||||
  version "1.29.0"
 | 
					  version "1.29.0"
 | 
				
			||||||
  resolved "https://registry.yarnpkg.com/prismjs/-/prismjs-1.29.0.tgz#f113555a8fa9b57c35e637bba27509dcf802dd12"
 | 
					  resolved "https://registry.yarnpkg.com/prismjs/-/prismjs-1.29.0.tgz#f113555a8fa9b57c35e637bba27509dcf802dd12"
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
		Reference in New Issue
	
	Block a user