Hi, Sveltian today I would like to share you how to stop theme flickering after we refreshing a page while we using Daisy UI with Sveltekit
New in Daisy UI 4.0
Now with Daisy UI 4.0 has been release with new component call Theme Controller it bring us to a more easier way to change between two theme in Daisy UI , for more info about what change and what new in Daisy UI 4.0 let check this link
Let see what Theme Controller can do
Now I expected you has been installing a new Sveltekit project (with a Skeleton Project option selected)
Tailwind CSS and Daisy UI , then we will starting our code
- checking you tailwind.config.js , need to have at least 2 of any themes (how to setup themes)
I have set 2 customize theme name 'light' and 'dark'
- adding attribute
data-theme
onhtml
tag insrc/app.html
page
for me will set it as 'light' theme by default while page is loaded
- build svelte component name 'ModeSwitcher' (this name is up to your decide) and in that svelte component put below code into it and using this component on
+layout.svelte
I has using Theme Controller using a swap component from DaisyUI and set it value to 'dark' as it is my dark mode theme name
<label class="swap swap-rotate">
<!-- this hidden checkbox controls the state -->
<input
type="checkbox"
class="theme-controller"
value="dark"
/>
<!-- sun icon -->
<svg class="swap-on fill-current w-10 h-10" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
<path d="M5.64,17l-.71.71a1,1,0,0,0,0,1.41,1,1,0,0,0,1.41,0l.71-.71A1,1,0,0,0,5.64,17ZM5,12a1,1,0,0,0-1-1H3a1,1,0,0,0,0,2H4A1,1,0,0,0,5,12Zm7-7a1,1,0,0,0,1-1V3a1,1,0,0,0-2,0V4A1,1,0,0,0,12,5ZM5.64,7.05a1,1,0,0,0,.7.29,1,1,0,0,0,.71-.29,1,1,0,0,0,0-1.41l-.71-.71A1,1,0,0,0,4.93,6.34Zm12,.29a1,1,0,0,0,.7-.29l.71-.71a1,1,0,1,0-1.41-1.41L17,5.64a1,1,0,0,0,0,1.41A1,1,0,0,0,17.66,7.34ZM21,11H20a1,1,0,0,0,0,2h1a1,1,0,0,0,0-2Zm-9,8a1,1,0,0,0-1,1v1a1,1,0,0,0,2,0V20A1,1,0,0,0,12,19ZM18.36,17A1,1,0,0,0,17,18.36l.71.71a1,1,0,0,0,1.41,0,1,1,0,0,0,0-1.41ZM12,6.5A5.5,5.5,0,1,0,17.5,12,5.51,5.51,0,0,0,12,6.5Zm0,9A3.5,3.5,0,1,1,15.5,12,3.5,3.5,0,0,1,12,15.5Z" />
</svg>
<!-- moon icon -->
<svg class="swap-off fill-current w-10 h-10" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
<path d="M21.64,13a1,1,0,0,0-1.05-.14,8.05,8.05,0,0,1-3.37.73A8.15,8.15,0,0,1,9.08,5.49a8.59,8.59,0,0,1,.25-2A1,1,0,0,0,8,2.36,10.14,10.14,0,1,0,22,14.05,1,1,0,0,0,21.64,13Zm-9.5,6.69A8.14,8.14,0,0,1,7.08,5.22v.27A10.15,10.15,0,0,0,17.22,15.63a9.79,9.79,0,0,0,2.1-.22A8.11,8.11,0,0,1,12.14,19.73Z" />
</svg>
</label>
now you can running your dev server and using the that input checkbox (or now it is a sun & moon button) to changing between two themes , for me it will change between theme name 'light' and 'dark'
BUT!! you will notice that while we reload or refresh the page from a dark theme it will be reloaded back to a default theme that we has been set in html
tag from previously
Made theme persistent
firstly I will introduce you to a basically way to persist a theme by using cookie and onMount
function of svelte if self
let copy a code below and paste it in your current component
<script>
import { onMount } from 'svelte';
let isDark = false;
onMount(() => {
let decodedCookie = decodeURIComponent(document.cookie);
let ca = decodedCookie.split(';');
const cookiesObject = ca.map((cookie) => {
const [name, value] = cookie.split('=');
return { name, value };
});
const cookieIsDark = cookiesObject.find(cookie=> cookie.name==='isDark');
isDark = JSON.parse(cookieIsDark ? cookieIsDark.value : "false") ;
});
function setModeCookie() {
const expirationDate = new Date();
expirationDate.setDate(expirationDate.getDate() + 365);
document.cookie = `isDark=${isDark}; expires=${expirationDate.toUTCString()}`;
}
</script>
<label class="swap swap-rotate">
<!-- this hidden checkbox controls the state -->
<input
type="checkbox"
class="theme-controller"
value="dark"
/>
<!-- sun icon -->
<svg class="swap-on fill-current w-10 h-10" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
<path d="M5.64,17l-.71.71a1,1,0,0,0,0,1.41,1,1,0,0,0,1.41,0l.71-.71A1,1,0,0,0,5.64,17ZM5,12a1,1,0,0,0-1-1H3a1,1,0,0,0,0,2H4A1,1,0,0,0,5,12Zm7-7a1,1,0,0,0,1-1V3a1,1,0,0,0-2,0V4A1,1,0,0,0,12,5ZM5.64,7.05a1,1,0,0,0,.7.29,1,1,0,0,0,.71-.29,1,1,0,0,0,0-1.41l-.71-.71A1,1,0,0,0,4.93,6.34Zm12,.29a1,1,0,0,0,.7-.29l.71-.71a1,1,0,1,0-1.41-1.41L17,5.64a1,1,0,0,0,0,1.41A1,1,0,0,0,17.66,7.34ZM21,11H20a1,1,0,0,0,0,2h1a1,1,0,0,0,0-2Zm-9,8a1,1,0,0,0-1,1v1a1,1,0,0,0,2,0V20A1,1,0,0,0,12,19ZM18.36,17A1,1,0,0,0,17,18.36l.71.71a1,1,0,0,0,1.41,0,1,1,0,0,0,0-1.41ZM12,6.5A5.5,5.5,0,1,0,17.5,12,5.51,5.51,0,0,0,12,6.5Zm0,9A3.5,3.5,0,1,1,15.5,12,3.5,3.5,0,0,1,12,15.5Z" />
</svg>
<!-- moon icon -->
<svg class="swap-off fill-current w-10 h-10" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
<path d="M21.64,13a1,1,0,0,0-1.05-.14,8.05,8.05,0,0,1-3.37.73A8.15,8.15,0,0,1,9.08,5.49a8.59,8.59,0,0,1,.25-2A1,1,0,0,0,8,2.36,10.14,10.14,0,1,0,22,14.05,1,1,0,0,0,21.64,13Zm-9.5,6.69A8.14,8.14,0,0,1,7.08,5.22v.27A10.15,10.15,0,0,0,17.22,15.63a9.79,9.79,0,0,0,2.1-.22A8.11,8.11,0,0,1,12.14,19.73Z" />
</svg>
</label>
In the code while the component is onMount
state it will decoding and get a value of cookie name 'isDark' that data type is boolean and will tell us the page currently in dark mode or not and it value will binding with input
checked state
for a first time it will be set to false because page didn't have any cookie yet
and after we changing a theme with our theme controller, it will calling setModeCookie
function that will set a cookie name 'isDark' with value of our 'isDark' variable (that suppose to be true by now because we clicked a theme controller and change a page's theme to dark)
now if you try to refresh or reload the page, the theme will be back into a theme that we has been using before we refresh or reload the page
BUT AGAIN!! you will notice some flickering effect after we refresh or reload the page while on 'dark' theme that because the page will be rendered from server side as a 'light' theme and then after 'onMount' is complete the page will get a cookies and set it back to our theme controller to made the page to be in 'dark' theme again
Stop theme flickering effect!!!
So far we has been using cookie to set a value that telling us, is currently page is in dark mode or not then in Sveltekit we can accessing that cookie on server side too !!
then what we need to do is we will calling that cookie in server side and passing it value back to our page and the page will rendered on value of that passing from server side
let create new file in src
folder and name it +layout.server.js
and in that file put below code into it
/** @type {import('./$types').LayoutServerLoad} */
export async function load({ cookies }) {
const isDark = cookies.get('isDark') ?? "false";
return { isDark };
}
and put this code below into ModeSwitcher component
<script>
import { page } from '$app/stores';
let isDark = JSON.parse($page.data.isDark);
function setModeCookie() {
const expirationDate = new Date();
expirationDate.setDate(expirationDate.getDate() + 365);
document.cookie = `isDark=${isDark}; expires=${expirationDate.toUTCString()};`;
}
</script>
<label class="swap swap-rotate">
<!-- this hidden checkbox controls the state -->
<input
type="checkbox"
class="theme-controller"
value="dark"
/>
<!-- sun icon -->
<svg class="swap-on fill-current w-10 h-10" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
<path d="M5.64,17l-.71.71a1,1,0,0,0,0,1.41,1,1,0,0,0,1.41,0l.71-.71A1,1,0,0,0,5.64,17ZM5,12a1,1,0,0,0-1-1H3a1,1,0,0,0,0,2H4A1,1,0,0,0,5,12Zm7-7a1,1,0,0,0,1-1V3a1,1,0,0,0-2,0V4A1,1,0,0,0,12,5ZM5.64,7.05a1,1,0,0,0,.7.29,1,1,0,0,0,.71-.29,1,1,0,0,0,0-1.41l-.71-.71A1,1,0,0,0,4.93,6.34Zm12,.29a1,1,0,0,0,.7-.29l.71-.71a1,1,0,1,0-1.41-1.41L17,5.64a1,1,0,0,0,0,1.41A1,1,0,0,0,17.66,7.34ZM21,11H20a1,1,0,0,0,0,2h1a1,1,0,0,0,0-2Zm-9,8a1,1,0,0,0-1,1v1a1,1,0,0,0,2,0V20A1,1,0,0,0,12,19ZM18.36,17A1,1,0,0,0,17,18.36l.71.71a1,1,0,0,0,1.41,0,1,1,0,0,0,0-1.41ZM12,6.5A5.5,5.5,0,1,0,17.5,12,5.51,5.51,0,0,0,12,6.5Zm0,9A3.5,3.5,0,1,1,15.5,12,3.5,3.5,0,0,1,12,15.5Z" />
</svg>
<!-- moon icon -->
<svg class="swap-off fill-current w-10 h-10" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
<path d="M21.64,13a1,1,0,0,0-1.05-.14,8.05,8.05,0,0,1-3.37.73A8.15,8.15,0,0,1,9.08,5.49a8.59,8.59,0,0,1,.25-2A1,1,0,0,0,8,2.36,10.14,10.14,0,1,0,22,14.05,1,1,0,0,0,21.64,13Zm-9.5,6.69A8.14,8.14,0,0,1,7.08,5.22v.27A10.15,10.15,0,0,0,17.22,15.63a9.79,9.79,0,0,0,2.1-.22A8.11,8.11,0,0,1,12.14,19.73Z" />
</svg>
</label>
then if you see a code in +layout.server.js
we will try to get a value of cookie with name 'isDark' and if it not exist just passing "false" as a string and made our ModeSwitcher component receiving that value by using import { page } from '$app/stores';
this will get a store of page that we will need to subscribe and call it data by $page.data.isDark
, last but not less we will set that value back to ModeSwitcher component 's variable name 'isDark' and that variable is got binding on our input checkbox of Theme Controller
that mean while page is rendered, a 'isDark' value will set into a value of our cookie that passing by server side and then it will rendered to correctly theme that suppose to be show in the page with out flickering effect at all
Top comments (1)
Great stuff, thank you for explaining the logic! The code snippets are incomplete but it's not too hard to fix.
I just needed to bind isDark to the input's checked attribute and also add an on:click event to the input to call the setModeCookie() function