Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.

secondman's avatar

Livewire 3 Theme Switcher

Does anyone have code for a Livewire 3 dark mode theme switcher?

It's pretty trivial to build a class based changer via alpine, but also getting it to honor system media at the same time has proved exceptionally challenging as I'm a new Livewire user.

I'm not wrapping my head around how to get the javascript and PHP to talk to each other. I see that alpine/persist is included by default, so my html tag looks like so:

<html x-data="{ darkMode: $persist(false) }" :class="{'dark': darkMode }">

Then my normal Laravel blade component (x-theme-toggle) looks like so:

<button type="button" x-on:click="darkMode=!darkMode" class="rounded-lg p-2.5 text-sm text-gray-300 hover:text-lime-500 focus:outline-none focus:ring-0 dark:text-gray-400 dark:hover:text-lime-500" aria-label="Change Theme">
  <template x-if="darkMode">
    <svg>removed for brevity</svg>
  </template>
  <template x-if="!darkMode">
    <svg>removed for brevity</svg>
  </template>
</button>

This by itself works great to toggle the html class to dark, and remove it, and persist it to localStorage, but getting some js code into app.js that will manage the system media, saving it to localStorage, and dispatching it to the x-data just isn't working.

I normally use ChatGPT when I get stuck, but because Livewire 3 and Volt are so new, GPT does nothing but spit out old code that doesn't work.

Anyone that can help I'd greatly appreciate it.

1 like
3 replies
Joemires's avatar
<!DOCTYPE html>
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}" x-data="{ darkMode: false }" x-bind:class="{'dark' : darkMode === true}"  x-init="
    if (!('darkMode' in localStorage) && window.matchMedia('(prefers-color-scheme: dark)').matches) {
        localStorage.setItem('darkMode', JSON.stringify(true));
    }
    darkMode = JSON.parse(localStorage.getItem('darkMode'));
    $watch('darkMode', value => localStorage.setItem('darkMode', JSON.stringify(value)))">

</html>
<!-- Darkmode Toggler -->
<button x-on:click="darkMode = !darkMode" type="button" class="text-gray-500 dark:text-gray-400 hover:bg-gray-100 dark:hover:bg-gray-700 focus:outline-none focus:ring-4 focus:ring-gray-200 dark:focus:ring-gray-700 rounded-lg text-sm p-2.5">
    <svg x-show="! darkMode" class="w-5 h-5" fill="currentColor" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg"><path d="M17.293 13.293A8 8 0 016.707 2.707a8.001 8.001 0 1010.586 10.586z"></path></svg>
    <svg x-show="darkMode" class="w-5 h-5" fill="currentColor" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg"><path d="M10 2a1 1 0 011 1v1a1 1 0 11-2 0V3a1 1 0 011-1zm4 8a4 4 0 11-8 0 4 4 0 018 0zm-.464 4.95l.707.707a1 1 0 001.414-1.414l-.707-.707a1 1 0 00-1.414 1.414zm2.12-10.607a1 1 0 010 1.414l-.706.707a1 1 0 11-1.414-1.414l.707-.707a1 1 0 011.414 0zM17 11a1 1 0 100-2h-1a1 1 0 100 2h1zm-7 4a1 1 0 011 1v1a1 1 0 11-2 0v-1a1 1 0 011-1zM5.05 6.464A1 1 0 106.465 5.05l-.708-.707a1 1 0 00-1.414 1.414l.707.707zm1.414 8.486l-.707.707a1 1 0 01-1.414-1.414l.707-.707a1 1 0 011.414 1.414zM4 11a1 1 0 100-2H3a1 1 0 000 2h1z" fill-rule="evenodd" clip-rule="evenodd"></path></svg>
</button>

The above should work for you properly

6 likes

Please or to participate in this conversation.