Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Themevel Laravel Package

shipu/themevel

Themevel is a Laravel package for managing multiple themes with ease. It provides theme activation, view and asset resolution, CLI commands, and middleware support, helping you organize and switch frontend themes without touching core application code.

View on GitHub
Deep Wiki
Context7

Getting Started

composer require shipu/themevel

On Laravel 5.5+ the service provider and Theme facade are auto-discovered. On older versions register them manually in config/app.php:

'providers' => [Shipu\Themevel\ThemeServiceProvider::class],
'aliases'   => ['Theme' => Shipu\Themevel\Facades\Theme::class],

Publish the config file (confirm the exact provider class from the package's composer.json extra.laravel.providers if the alias below doesn't resolve):

php artisan vendor:publish --provider="Shipu\Themevel\ThemeServiceProvider"

You should now have config/themevel.php. Read it before doing anything else — it defines the default theme slug, the themes_path, the public asset path, and any caching switches. This file is your primary reference, along with vendor/shipu/themevel/src/ for the facade's available methods.

Create your first theme as a directory tree next to your app:

themes/
└── admin/
    ├── views/
    │   ├── layouts/app.blade.php
    │   └── welcome.blade.php
    └── assets/
        └── css/app.css

Then switch and render inside a service provider, middleware, or controller:

Theme::set('admin');

return theme_view('welcome', ['user' => $user]);

And reference theme assets in Blade:

<link rel="stylesheet" href="{{ theme_asset('css/app.css') }}">

Assets live in the theme's assets/ folder but are served from public/, so publish them once so the URL resolves:

php artisan theme:publish        # list/inspect any options first via: php artisan list | grep theme

First realistic use case: a public "site" theme plus an "admin" theme, both extending your existing layouts, switched by route group. That exercises view resolution, asset resolution, and theme switching in one afternoon.

Implementation Patterns

Per-request theme switching via middleware. This is the pattern you'll reach for 90% of the time. Register it after StartSession/Authenticate so $request->user() is populated:

class SetTheme
{
    public function handle($request, Closure $next)
    {
        $slug = $request->user()?->theme
            ?? $request->route('theme')
            ?? config('themevel.theme', 'default');

        if (Theme::exists($slug)) {   // guard against arbitrary user input
            Theme::set($slug);
        }

        return $next($request);
    }
}

Only call Theme::set() in one place per request. Scattering it across controllers makes the active theme unpredictable when you add a second switch point later.

Route-group theming. For a stable public/admin split, apply the middleware to the group and hardcode the slug there rather than resolving from config each time:

Route::middleware(['web', 'theme:admin'])->prefix('admin')->group(...);

Multi-tenant / per-domain theming. Resolve the slug from the hostname or tenant model in a singleton service and set it once in middleware. Cache the resolved slug alongside the tenant record — hitting the DB on every request just to pick a folder is wasted latency.

Fallback / layout sharing. Keep truly shared partials in the default theme and duplicate the layout wrapper per theme. Relying on deep theme inheritance is brittle and hard to debug; a slightly duplicated layouts/app.blade.php per theme is the pragmatic choice. Verify whether your version falls back to resources/views when a view is absent from the active theme — if it does, use that for shared components; if not, don't assume it.

Asset pipeline. Build your CSS/JS into themes/{slug}/assets/ (Vite/Mix output), then run theme:publish as part of deploy. Add cache-busting yourself, since the package doesn't do it:

<link rel="stylesheet" href="{{ theme_asset('css/app.css') }}?v={{ config('app.version') }}">

Testing. Theme state leaks between tests if you set it in one test and not the next. Set Theme::set('default') in setUp() and, if it holds static state, reset in tearDown(). Assert on the rendered HTML rather than on Theme::get() alone.

Single call site. If you want to swap themes later without rewriting every view call, define your own theme_view() wrapper and use it everywhere. Then a future migration to Laravel's View::addNamespace() or a different package touches one file.

Gotchas and Tips

  • Assets 404 in production. theme_asset() points at the published public path, not themes/{slug}/assets/. Editing the source file does nothing until theme:publish re-runs. Add it to your deploy script and remember it needs --force (or equivalent) if it skips existing files.
  • Front-end frameworks bypass it entirely. Inertia, Livewire (mostly), Vue, and React SPAs don't go through Blade view resolution, so themevel's view swapping has no effect. It's a Blade-only tool — plan around that before adopting it for a headless build.
  • config:cache interaction. Avoid re-reading config('themevel.*') after boot; set the theme explicitly with Theme::set(). Runtime config([...]) mutations are exactly what config:cache is designed to prevent from being useful.
  • Stale compiled views. When jumping between themes during development, php artisan view:clear is the first thing to try if you're seeing the wrong markup. Blade caches are path-keyed, so this usually self-resolves, but the confusion is real.
  • Case sensitivity. Theme slugs must match directory names exactly. macOS is case-insensitive, Linux CI is not — a theme that works locally can 404 in production. Normalize slugs to lowercase.
  • Path normalization. Set themes_path to base_path('themes') without a trailing slash; trailing slashes cause double-slash paths that fail inconsistently across filesystems.
  • Package age. Last release was 2023-08 and the opportunity score is low, meaning alternatives are actively maintained. Verify compatibility on Laravel 11/12 early — it hooks into the view finder and asset internals that shift between major versions. Pin the version and smoke-test before committing.
  • Arbitrary input safety. Never pass $request->input('theme') straight to Theme::set() without checking it exists; you'd be letting users probe your directory structure.
  • Debugging. dd(Theme::get()) for the active slug, and whatever the facade exposes for enumerating discovered themes (check the facade class — it's typically Theme::all() or Theme::list()). If a theme isn't listed, your folder is missing views/ or is outside themes_path.
  • Extension points. You can override the facade binding in your own service provider to swap in a custom implementation, or simply publish the config and add your own middleware around it. Don't fork the package for small behavior changes — wrap it.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
jinas/dhivatar
store-doc/laravel-storage-driver
plus-it/laravel-translation-manager
diephp/perhaps
diephp/telegraph
osmaviation/spreadsheet
microsoft/fluentui-system-icons
mmccook/php-json-canonicalization-scheme
middlewares/firewall
qubiqx/montapacking
nyoncode/laravel-package-console
pnscripts/filament-broadquill-lite
pnscripts/laravel-sentfold-lite
vleap/mx-sdk-laravel-internal
twidpay/utils
htetmyathlaing/scout-elasticsearch-driver
artisan-toolbox/front-awesome
ls/client-assistant
users-au/laravel-client
cgv/localized-resource