alizharb/laravel-themer
Enterprise-grade theme management for Laravel. Create, clone, activate, and safely delete themes with per-theme Vite builds, NPM workspaces, asset shortcuts, view overrides, and Livewire 4 support. Includes metadata, wizards, and fast production caching.
Laravel Themer extends Blade's view system with theme-aware resolution and custom directives.
When a theme is active, Laravel Themer prepends theme view paths to Laravel's view finder. This means views can be referenced with or without the theme:: namespace prefix.
Resolution Order:
themes/{active-theme}/resources/views/{view}themes/{parent-theme}/resources/views/{view} (if parent exists)resources/views/{view} (application default)Laravel Themer provides multiple ways to reference theme views:
Theme paths are automatically prepended, so standard Laravel view syntax works:
{{-- Automatically resolves to active theme first --}}
[@include](https://github.com/include)('welcome')
[@extends](https://github.com/extends)('layouts.app')
Use theme:: for explicit theme references:
{{-- Explicitly reference theme namespace --}}
[@include](https://github.com/include)('theme::welcome')
[@extends](https://github.com/extends)('theme::layouts.app')
Both approaches work identically. The theme path is checked first in either case.
Configured in config/themer.php:
'auto_namespaces' => [
'layouts' => 'resources/views/layouts',
'pages' => 'resources/views/livewire/pages',
],
Usage:
{{-- Resolves to active theme's layouts directory --}}
<x-layouts::app>
[@yield](https://github.com/yield)('content')
</x-layouts::app>
{{-- Resolves to active theme's pages directory --}}
[@include](https://github.com/include)('pages::home')
[@theme_include](https://github.com/theme_include)Include a view with automatic theme fallback:
[@theme_include](https://github.com/theme_include)('partials.header')
Equivalent to:
[@include](https://github.com/include)('theme::partials.header')
php artisan livewire:layout --theme=mytheme
Example Layout (themes/mytheme/resources/views/layouts/app.blade.php):
<!DOCTYPE html>
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{{ $title ?? 'My Application' }}</title>
[@vite](https://github.com/vite)(['resources/assets/css/app.css', 'resources/assets/js/app.js'], 'themes/mytheme')
</head>
<body>
<x-layouts::navigation />
<main>
{{ $slot }}
</main>
<x-layouts::footer />
</body>
</html>
<x-layouts::app>
<x-slot:title>
Home Page
</x-slot:title>
<h1>Welcome to My Theme</h1>
</x-layouts::app>
php artisan make:component button --theme=mytheme
Creates:
themes/mytheme/app/View/Components/Button.phpthemes/mytheme/resources/views/components/button.blade.phpCreate anonymous components directly in the views directory:
themes/mytheme/resources/views/components/
├── button.blade.php
├── card.blade.php
└── alert.blade.php
Usage:
<x-theme::button variant="primary">
Click Me
</x-theme::button>
Child themes can override parent theme components:
base-theme/resources/views/components/button.blade.php
corporate-theme/resources/views/components/button.blade.php ← Overrides
resources/views/
├── partials/
│ ├── header.blade.php
│ ├── footer.blade.php
│ ├── navigation.blade.php
│ └── sidebar.blade.php
[@include](https://github.com/include)('theme::partials.header')
<main>
[@yield](https://github.com/yield)('content')
</main>
[@include](https://github.com/include)('theme::partials.footer')
Register view composers in your theme's service provider:
use Illuminate\Support\Facades\View;
public function boot(): void
{
View::composer('theme::layouts.app', function ($view) {
$view->with('siteName', config('app.name'));
$view->with('currentYear', date('Y'));
});
}
<h1>{{ __('theme::messages.welcome', ['name' => $user->name]) }}</h1>
<nav>
<a href="/">{{ __('theme::navigation.home') }}</a>
<a href="/about">{{ __('theme::navigation.about') }}</a>
</nav>
themes/mytheme/lang/en/messages.php:
<?php
return [
'welcome' => 'Welcome, :name!',
'navigation' => [
'home' => 'Home',
'about' => 'About',
'contact' => 'Contact',
],
];
[@if](https://github.com/if)(is_theme_active('mytheme'))
<div class="theme-specific-feature">
<!-- Only shown when mytheme is active -->
</div>
[@endif](https://github.com/endif)
[@php](https://github.com/php)
$theme = get_active_theme();
[@endphp](https://github.com/endphp)
<div class="theme-{{ $theme->slug }}">
<p>Current theme: {{ $theme->name }}</p>
<p>Version: {{ $theme->version }}</p>
</div>
[@vite](https://github.com/vite)(['resources/assets/css/app.css', 'resources/assets/js/app.js'], 'themes/mytheme')
<img src="{{ theme_asset('images/logo.png') }}" alt="Logo">
<link rel="stylesheet" href="{{ theme_asset('css/custom.css') }}">
<script src="{{ theme_asset('js/custom.js') }}"></script>
Both direct and namespaced references work - pick one style and be consistent:
✅ [@extends](https://github.com/extends)('layouts.app') // Direct (cleaner)
✅ [@extends](https://github.com/extends)('theme::layouts.app') // Explicit (clearer intent)
❌ Mixing both styles inconsistently
Design base themes for reusability:
base-theme/
└── layouts/app.blade.php (foundation)
corporate-theme/
└── layouts/app.blade.php (extends base, adds branding)
Avoid hardcoded paths:
✅ <img src="{{ theme_asset('logo.png') }}">
❌ <img src="/themes/mytheme/assets/logo.png">
Break down complex views into reusable components:
<x-theme::card>
<x-slot:header>
<h2>{{ $title }}</h2>
</x-slot:header>
{{ $content }}
<x-slot:footer>
<x-theme::button>Learn More</x-theme::button>
</x-slot:footer>
</x-theme::card>
How can I help you explore Laravel packages today?