How to add dark mode in Laravel (with toggle button)

Klender Carrasco
4 min readMay 15, 2024
An image showing the difference between light and dark mode.
Laravel Breeze — Light and Dark

Welcome to my first post on Medium! As a die-hard fan of dark mode from day one, I’ll show you how to use it in your Laravel project with a button that lets you switch between light and dark mode. Let’s dive in!

Me being happy writing my first story!
Me being happy writing my first story!

Install Laravel Breeze

Why Breeze? In short, because it’s the easiest way to implement a strong authentication in your projects. Let Laravel do its job! 😎

Start by creating a new project:

composer create-project laravel/laravel:^10.0 DarkmodeExample

Now we’ll enter the directory and install Breeze:

cd DarkmodeExample
composer require laravel/breeze --dev
php artisan breeze:install

Then you’ll need to answer the following questions:

  • Which Breeze stack would you like to install? Blade with Alpine.
  • Would you like dark mode support? Yes (Obviously).
  • Which testing framework do you prefer? PHPUnit (not relevant now).

Before running the migrations, don’t forget to edit your .env file to connect the application with your database. I’ll use PostgreSQL for this example:

DB_CONNECTION=pgsql
DB_HOST=127.0.0.1
DB_PORT=5432
DB_DATABASE=test_db
DB_USERNAME=marcosklender
DB_PASSWORD=followme

Once it’s done, let’s run the application with the followings:

php artisan migrate
npm install

Hint: I like to duplicate the terminal tab to run these commands separately.

php artisan serve
npm run dev

If everything goes right we should have the project up and running. Let’s register ourselves to access the Dashboard View.

Screen capture of the dashboard view with the dark mode enabled.
Dashboard view with dark mode enabled.

Now, using our trusted IDE, we’ll edit the following files:

  • /tailwind.config.js
...
plugins: [forms],

darkMode: 'class' // Just add this line at the bottom.
};
...
  • /resources/js/app.js
import "./bootstrap";

import Alpine from "alpinejs";

window.Alpine = Alpine;

Alpine.start();

// DARK MODE TOGGLE BUTTON
var themeToggleDarkIcon = document.getElementById("theme-toggle-dark-icon");
var themeToggleLightIcon = document.getElementById("theme-toggle-light-icon");

if (
localStorage.getItem("color-theme") === "dark" ||
(!("color-theme" in localStorage) &&
window.matchMedia("(prefers-color-scheme: dark)").matches)
) {
themeToggleLightIcon.classList.remove("hidden");
} else {
themeToggleDarkIcon.classList.remove("hidden");
}

var themeToggleBtn = document.getElementById("theme-toggle");

themeToggleBtn.addEventListener("click", function () {
themeToggleDarkIcon.classList.toggle("hidden");
themeToggleLightIcon.classList.toggle("hidden");

if (localStorage.getItem("color-theme")) {
if (localStorage.getItem("color-theme") === "light") {
document.documentElement.classList.add("dark");
localStorage.setItem("color-theme", "dark");
} else {
document.documentElement.classList.remove("dark");
localStorage.setItem("color-theme", "light");
}
} else {
if (document.documentElement.classList.contains("dark")) {
document.documentElement.classList.remove("dark");
localStorage.setItem("color-theme", "light");
} else {
document.documentElement.classList.add("dark");
localStorage.setItem("color-theme", "dark");
}
}
});
  • /resources/views/layouts/navigation.blade.php
...
<!-- Settings Dropdown -->
<div class="hidden sm:flex sm:items-center sm:ms-6">

<!-- Add this button right here -->
<button id="theme-toggle" 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 id="theme-toggle-dark-icon" class="hidden 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 id="theme-toggle-light-icon" class="hidden 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>

<x-dropdown align="right" width="48">
<x-slot name="trigger">
...

Going back to our browser, you’ll find an icon that allows you to toggle between light/dark mode, remembering our selection for the entire app.

A capture (GIF) of the theme toggle button in use.
It’s really easy, isn’t it?

Thanks for reaching the end, people like you motivate me to keep sharing my experiences acquired along the way, see you next time! 👋🏻

Feel free to reach me through my LinkedIn. ✍🏻

--

--

Klender Carrasco
0 Followers

iOS & Web Developer | Systems and Computer Engineer | IT Security Analyst