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

Laravel Themes Manager Laravel Package

hexadog/laravel-themes-manager

Laravel Themes Manager lets you add multiple themes to a Laravel app, each with its own views, assets, translations, config and routes. Switch themes at runtime, scaffold new themes with Artisan commands, and keep theme files isolated and publishable.

View on GitHub
Deep Wiki
Context7

Getting Started

This package gives Laravel a first-class notion of a "theme": a self-contained directory holding views, translations, assets, and optionally config, which the manager can enumerate and activate at runtime.

  1. Install and publish configuration:
    composer require hexadog/laravel-themes-manager
    php artisan vendor:publish --provider="Hexadog\ThemesManager\ThemesManagerServiceProvider"
    
    The release you're on (v1.15.0) adds Laravel 12.x and 13.x support, so verify your framework constraint in composer.json before upgrading; the underlying API surface is unchanged from v1.14.
  2. Open the published config/themes.php first. That file is the contract for everything else: where themes live on disk (path, typically base_path('themes') or resources/themes), the default/fallback theme, asset handling, and cache behaviour. Read it before writing a single line of code.
  3. Scaffold a theme with the package's artisan command (php artisan theme:list to see what's registered, php artisan theme:make <name> to generate the skeleton). Inspect the generated directory — the folder layout is the real documentation.
  4. First real use case: override one blade view. Drop resources/views/welcome.blade.php into your theme, activate the theme, and confirm the render picks it up. That single loop (create → activate → verify) is the whole package in miniature.

Implementation Patterns

Resolve the active theme in one place. Don't scatter ThemesManager::set() calls. Pick a single resolution strategy and put it in a middleware or a small service:

// app/Http/Middleware/ResolveTheme.php
public function handle($request, Closure $next)
{
    $theme = $request->user()?->theme
        ?? config('themes.default');

    themes()->set($theme);

    return $next($request);
}

Register it globally or on the web group, and keep it near the top of the stack so downstream middleware and controllers see the right theme.

Per-tenant / per-domain theming. A very common production shape: map hostname or tenant model to a theme name, resolve in middleware, and let the package handle the rest. Because activation is just container state, everything downstream — view(), trans(), asset() — automatically follows.

Layered view fallback. Treat the theme as an overlay, not a replacement. Keep shared layouts in resources/views and only theme-specific overrides in the theme directory. Configure the view paths so the theme's views are searched first and Laravel falls back to the base app. This keeps 90% of a theme shared and makes new themes cheap.

Config-driven theme registry. If you need a curated list (paid plans, feature flags), gate themes in your own config/themes.php extension or a dedicated config file rather than checking file_exists at runtime. The manager will happily report a theme as available just because the folder exists — the policy decision belongs to your app.

Assets: symlink, don't copy. Wire the theme's public assets into public/ via the package's publish/symlink mechanism and run it as part of your deploy pipeline. In local dev, re-run after adding files; in CI/CD, run it as a deploy step, not a request-time operation.

Testing. Use Orchestra Testbench for package-level work (v1.15.0 bumps the testbench dependency, so re-run your suite after upgrading). In application tests, either set the theme explicitly in setUp() or assert on the resolved theme in your middleware test. Snapshot-testing rendered output per theme catches override regressions fast.

Composition with translations. Theme-level lang/ directories are the reason not to hand-roll i18n per theme. Keep translation keys namespaced (theme::messages.welcome) so fallback to the app's base translations stays predictable.

Gotchas and Tips

  • Config caching is the #1 source of "my new theme doesn't exist." php artisan config:cache freezes the theme path and default. Any deploy that caches config must rebuild after theme directories are in place, and php artisan config:clear should be your first debugging step.
  • View cache is a close second. Blade compiles by absolute path. Renaming a theme directory or switching paths leaves stale compiled views; run php artisan view:clear whenever theme structure changes.
  • Asset symlinks break on Windows and in containerised deploys where public/ isn't shared with the build stage. Run the symlink step in the entrypoint, and prefer relative URLs over absolute ones so CDN/domain swaps don't strand theme assets.
  • Middleware ordering matters. If anything earlier in the stack renders a view (maintenance mode, error handlers, auth redirects), it will render with the previous or default theme. Set the theme as early as you reasonably can, and accept that exception pages are usually default-themed.
  • themes()->set() is request-scoped state, not persistence. It does not write to session or DB. If the user picks a theme, persist that choice yourself and re-resolve it on each request — otherwise it silently resets.
  • Folder naming is the identity. Theme names are derived from directory names; avoid renaming in production, or you'll need a migration/alias layer for anything stored in the database.
  • Fallback theme is a safety net, not a strategy. Confirm the configured fallback actually has a full view set, otherwise a missing theme produces confusing "view not found" errors rather than a clean degradation.
  • Debugging checklist, in order: theme:list (is it discovered?) → config('themes.path') (right root?) → view:clear + config:clear → middleware trace (did set() actually run?) → php artisan about for cached config state.
  • Version pinning. v1.15.0 is a compatibility release (Laravel 12/13, updated testbench) with no documented API changes, so upgrading from v1.14.0 should be low risk — but pin the version and re-run your test suite, since the Laravel 13 support implies a wider framework matrix than previously tested.
  • Extension point to remember: the manager is bound in the service container and exposed via a facade. If you need behaviour the package doesn't offer (theme inheritance chains, remote theme registries), bind your own decorator rather than forking — the surface is small enough that wrapping is cheaper than maintaining a fork across Laravel majors.
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.
socialdept/atp-orm
cboxdk/laravel-assistant-client
socialdept/atp-resolver
socialdept/tenant-domains
tautvydasr/blade-class-names
socialdept/color-scales
maherelgamil/controller
coleus/widgets
coleus/table
coleus/users
coleus/music
coleus/health
coleus/calendar
coleus/notes
coleus/support
coleus/settings
coleus/apps
seampact/laravel
emmyconceptng/laravel-ng-postcode
jjsquady/round-robin