Setting a Theme in Svelte using Hooks
Tags: svelte, theme, javascript, tutorial
Published: 2023-03-19 17:56:22

This is a follow-up post to [Secure Authentication in Svelte using Hooks](https://dev.to/brewhousedigital/secure-authentication-in-svelte-using-hooks-k5j) to expand on our Hooks file. If you're looking for a way to implement authentication in your SvelteKit app, definitely check out that article.

If you've ever built a site with multiple themes, there is a chance you've run into the annoying problem of having the site flash before the theme value is calculated client side. In this tutorial, we'll setup the `hooks.server.js` file to allow for a dynamic theme to be processed _before_ sending it to the client.

## Initial Setup

If you don't already have a `hooks.server.js` file, the most basic version looks like this:

```js
// hooks.server.js
export const handle = async({event, resolve}) => {
  const response = await resolve(event);

  return response;
}
```

This is explained in the previous post, but here is a refresher for any new readers:

The `event` property contains all the information related to the file request. This includes user's cookies, the browser http headers, and the URL of the specific request. You can read more indepth docs here: [SvelteKit Hooks](https://kit.svelte.dev/docs/hooks).

The second item, `resolve`, is the function that creates the finished HTML.

Inside our `handle()` function is the `await resolve(event)` call. This is a SvelteKit thing that essentially tells the server that it is ready to build the HTML before sending it to the client. Returning that `response` value renders the page as normal.

In this tutorial, we'll be using a simple HTML data attribute to handle which CSS will be used. In your `app.html` page, go ahead and add this to your `<html>` tag like so:

```html
<html lang="en" data-theme="">
```

That is all the setup we need for the HTML page. Go ahead and close that and lets open up our `hooks.server.js` file.

Now we want to modify our `resolve()` function to look for the string `data-theme=""` and replace it with the user's theme cookie value. We can do that like this:

```js
// hooks.server.js
export const handle = async({event, resolve}) => {
  const response = await resolve(event, {
    // Processing will go here
  });

  return response;
}
```

First step is to add an object as the second property of `resolve()`. Inside here, we can call the Svelte object property `transformPageChunk`, and pass it a function. That will look like this:

```js
// hooks.server.js
export const handle = async({event, resolve}) => {
  const response = await resolve(event, {
    transformPageChunk: ({html}) => {
      // This section will modify the HTML 
      // before being returned to the client
    }
  });

  return response;
}
```

We want to first check if the user's theme cookie even exists. This could be a new user without a cookie, or an existing user with one. An easy way to do that is to check the `event.cookies` that is passed from the root `handle()` function

```js
// hooks.server.js
export const handle = async({event, resolve}) => {
  const response = await resolve(event, {
    transformPageChunk: ({html}) => {
      // This section will modify the HTML 
      // before being returned to the client
      let currentTheme = cookies.get("theme");

      // Make sure the cookie was found, if not, set it to dark
      if(!currentTheme) {
        currentTheme = "dark";
        cookies.set("theme", currentTheme)
      }
    }
  });

  return response;
}
```

This value will return null if it doesn't exist, so it is useful to add in a default. We can also use this as an opportunity to set a theme cookie for all new users. In the above code, we're setting the default to `dark`.

Now that we have our theme value ready, we can update the HTML before being sent to the client. The easiest way to do that is by using the `replace()` javascript function on the entire HTML. It is useful to note that `replace()` is non-destructive so if you want to do additional processing, you must save it into a new variable.

```js
// hooks.server.js
export const handle = async({event, resolve}) => {
  const response = await resolve(event, {
    transformPageChunk: ({html}) => {
      // This section will modify the HTML 
      // before being returned to the client
      let currentTheme = cookies.get("theme");

      // Make sure the cookie was found, if not, set it to dark
      if(!currentTheme) {
        currentTheme = "dark";
        cookies.set("theme", currentTheme)
      }

      return html.replace(`data-theme=""`, `data-theme="${currentTheme}"`);
    }
  });

  return response;
}
```

And just like that, your theme is now being processed before being sent to the client side. No more flashing!

## CSS Example

If you're curious how to use CSS to access this, you can utilize CSS variables like so:

```css
:root {
  --body: #fff;
  --text: #000;
}

[data-theme='dark']:root {
  --body: #000;
  --text: #fff;
}

body {
  background-color: var(--body);
  color: var(--text);
}
```