How to Create a Dark Mode Toggle with Tailwind 4 (Laravel Livewire)
Gothic mode... sorry, dark mode in Tailwind 4. How do we implement it? We're going to save the configuration directly to the session, i.e., the mode. For this, we'll use Laravel Livewire, but first, let's see what we need to do for Tailwind 4.
Toggle Dark/Light Mode in Tailwind 4
It works similarly to how we did with Tailwind 3, at least as far as CSS classes are concerned. However, there's a new consideration in Tailwind 4, which I'll explain below.
If you select any rule that uses the dark: prefix from the browser, you'll see something like this:
dark\:bg-gray-800 {
@media (prefers-color-scheme: dark) {
background-color: var(--color-gray-800);
}
}
In Tailwind 4, the default system uses @media (prefers-color-scheme), which detects the operating system or browser mode. This can't be controlled from JavaScript, so a toggle like ours wouldn't have any effect if we left the default settings.
We must override it with the following rule in our CSS where we load Tailwind 4:
resources\css\YOURCSS.css
@import 'tailwindcss';
+ @custom-variant dark (&:where(.dark, .dark *));
With this, we can easily switch between modes by changing the class of the HTML tag from the browser:
// document.documentElement.className = params[0].theme;
document.documentElement.classList.toggle('dark');
Step 2: Create the Theme Toggle Component in Laravel Livewire
Let's create a component that allows us to toggle between light mode and dark mode. It's also important to note that Flux handles its own logic for the dark theme.
$ php artisan make:livewire ThemeToggle
app\Livewire\ThemeToggle.php
<?php
namespace App\Livewire;
use Livewire\Component;
class ThemeToggle extends Component
{
public $theme;
public function mount()
{
$this->theme = session('theme', 'light');
}
public function toggleTheme()
{
$this->theme = $this->theme === 'light' ? 'dark' : 'light';
session(['theme' => $this->theme]);
$this->dispatch('theme-changed', ['theme' => $this->theme]);
}
public function render()
{
return view('livewire.theme-toggle');
}
}
If you search for "Flux dark mode," for example, you'll see that it has its own switcher (Dark/System/Light), but that's not what we're going to use here. I want to use a custom implementation, as I prefer to keep this website as lightweight as possible and without too many dependencies. I don't want to add unnecessary additional JavaScript for the end user.
I prefer something more controlled. Tailwind is already more than enough, and perhaps a few additional scripts or CSS, like Flux in this case for certain styles. But I don't want this course to be so dependent on external technologies that are constantly changing, as is the case with Laravel.
If no value is defined, we set a default value (in this case, light, although you can set dark if you prefer):
$this->theme = session('theme', 'light');
If the current mode is light, change to dark, and vice versa:
$this->theme = $this->theme === 'light' ? 'dark' : 'light';
Set the new value in the session and dispatch an event to the client to apply the changes.
We could also reload the page, although there are many ways to do this:
$this->dispatch('theme-changed', ['theme' => $this->theme]);
***
Livewire.on('theme-changed', (params) => {
// document.documentElement.className = params[0].theme;
document.documentElement.classList.toggle('dark');
})
The button's view is simple. We'll place a button "Change Mode":
<div>
<flux:button wire:click="toggleTheme">
{{ __('Change mode') }} {{ $theme === 'light' ? __('Dark'): __('Light') }}
</flux:button>
</div>
The button displays "Dark" or "Light" depending on the active mode, but remember that it must be the option you're switching to, not the current one. That is, if the current mode is light, the button should say Dark.
I agree to receive announcements of interest about this Blog.
I'll show you a few simple steps to use dark mode in Tailwind 4 and also in Laravel Livewire.
- Andrés Cruz