This is a quick tutorial on saving users preferences in SvelteKit.
There are 2 ways one might approach this. First is implementing an auth system. But, that might be overkill so another way is to save it locally. Let’s go with that.
So something like this... (I’ll be using this package to simplify the code)
let name : string
const saveName = () => {
Cookie.set("name", name)
}
<input bind:value={name}/>
<button on:click={saveName}>save</button>
Well, that was easy.
But a small problem arises when we want to display it.
onMount(() => {
name = Cookie.get("name")
})
<p>{name}</p>
This works, but since we have to wait for document
to load, we need to use onMount()
. That means, there will be a split second after the page loads where name = undefined
. This won’t be a big problem in this case, but if it were saving user’s light/dark theme preference, it’ll lead to a quite negative UX. This will also happen if we rely on something like Firebase auth, since it also has a dependency on window
/document
.
To solve this, we can read the cookie in the server before the page fully loads.
First, let’s read the cookie with hooks. This handle()
function runs every time SvelteKit receives a request. We’ll use the cookie package to make cookie-parsing easier.
import * as cookie from 'cookie';
export const handle : Handle = async ({ event, resolve }) => {
const { name } = cookie.parse(event.request.headers.get('cookie') || '') as Partial<{ name: string }>;
if (name) {
event.locals = { name };
}
return await resolve(event)
}
Next, we have to send this to the frontend. One way of doing it, is to use the session object, which can be read in the load function. We can set the session object using getSession(). Since event
first passed the handle function, it includes name
in locals
.
export const getSession : GetSession = async (event) => {
const { name } = event.locals as Partial<{ name: string }>;
if (!name) return {};
return { name };
}
Finally, we can get the session
object in the load function like below.
export const load : Load = async ({ session }) => {
const { name } = session as Partial<{ name: string }>
return {
props: {
name
}
}
}
Here’s a simple project of mine that implements this:
Top comments (6)
Dude, how do we deal with two user preferences such as background-color and background-image ? The example code work on just one user preference, and I add the second one the codes not working, I think it has to do with cookie settings, how do we execute multiple preferences with the example, could you please help ? 🙏
Either save 2 cookies with different names, or save the user's preferences as an object, convert to a JSON string, and save that as a cookie value.
This is my codes
hooks/index.js
import * as cookie from "cookie";
export const handle = async ({ event, resolve }) => {
const { bgColor, imageURL } = cookie.parse(event.request.headers.get('cookie') || '')
if (bgColor) event.locals = { bgColor }
if (imageURL) event.locals = { imageURL }
return await resolve(event)
}
export const getSession = async (event) => {
const { bgColor, imageURL } = event.locals
if (!bgColor) return {}
if (!imageURL) return {}
return { bgColor, imageURL }
}
routes/index.svelte
import { bgColor, imageURL } from "$lib/store";
export const load = ({ session }) => {
const locals = session;
const bgColor_preference = locals.bgColor;
const imageURL_preference = locals.imageURL;
};
store/index.js
export const bgColor = writable('#e5ddd5')
export const imageURL = writable('https://.......')
Dude, how do we set cookie headers such as 'Expire/Max-Age', 'Secure', 'SameSite', 'HttpOnly', etc ?
Depends on where you want to set your cookie.
In endpoints, you can return something like this:
If you want to set it inside hooks (
handle()
):Thank you so much for this post, it helps a lot 🙏