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

Theme Bundle Laravel Package

liip/theme-bundle

LiipThemeBundle adds flexible theme support to Symfony apps. Define multiple themes with template and asset fallbacks, switch themes per request or environment, and reuse a default theme. It simplifies building branded, multi-theme sites while preserving Symfony’s standard rendering and asset workfl...

View on GitHub
Deep Wiki
Context7

Getting Started

First, the framework reality check: liip/theme-bundle is a Symfony bundle, not a Composer package you can drop into a Laravel app. There is no service provider, no facade, no config/liip_theme.php. If you composer require liip/theme-bundle in Laravel you get an inert vendor directory — the Liip\ThemeBundle\LiipThemeBundle class only does anything when a Symfony Kernel registers it. So treat this assessment as "steal the architecture, not the package."

If you're inheriting a Symfony app that uses it:

  1. composer require liip/theme-bundle (last tag is 2019-era, so pin it and expect PHP 7.x / Symfony 2.8–3.4 idioms — XML/YAML service definitions, AppKernel::registerBundles()).
  2. Register the bundle in app/AppKernel.php and add a liip_theme block to app/config/config.yml:
liip_theme:
    themes: ['web', 'tablet', 'mobile']
    active_theme: 'web'
    autodetect_theme: false
    cookie:
        name: liip_theme
  1. Look first at Resources/doc/index.md in the vendor dir, then DependencyInjection/Configuration.php — that file is the authoritative list of config keys for the version you installed, and it's the fastest way to settle "is this key themes or available_themes?" questions.
  2. First use case: create app/Resources/themes/mobile/AcmeBundle/Default/index.html.twig, set active_theme: mobile, and reload. Your controller still returns AcmeBundle:Default:index.html.twig — the bundle swaps the resolved path underneath you. That "logical template name, physical theme path" trick is the entire value proposition.

If you're building this in Laravel: don't. You can reproduce 90% of it with View::prependNamespace() / View::addLocation() in a service provider plus a middleware that resolves the active theme. That's roughly 40 lines and no archived dependency.

Implementation Patterns

Directory convention and fallback chain. Templates live under app/Resources/themes/<theme>/<Bundle>/<Controller>/<template>.html.twig. Resolution is: active theme path → default theme path → original bundle path. The fallback is the feature, not a bug — you only override the 5 templates that actually differ, and everything else keeps rendering from the bundle. In practice you build a theme by copying one template, not the whole tree.

Runtime theme switching via the service. Inject liip_theme.active_theme (the ActiveTheme service) and call setActiveTheme('mobile') before rendering. This is how you do per-user themes after authentication:

public function dashboardAction()
{
    $this->get('liip_theme.active_theme')->setActiveTheme($user->getPreferredTheme());
    return ['template' => 'AcmeBundle:Dashboard:index.html.twig'];
}

Order matters: the bundle's request listener sets the theme early (from cookie/autodetect), so anything you set in a controller overrides it for that request only.

Autodetection service. autodetect_theme accepts a service id. Implement a small class that returns a theme name from the request (User-Agent, host, subdomain, Accept-Language) and register it. Keep it pure and fast — it runs on every request via a kernel.request listener. Persist the result in the cookie rather than re-deriving it everywhere.

Cookie persistence + a switch endpoint. The bundle's cookie config lets a /theme/{theme} route set the preference and redirect back. Guard it: validate the submitted theme against themes before persisting, or you'll inject an arbitrary path segment into the template locator.

Twig stays dumb. Because {% extends %} and {% include %} use logical bundle names, adding a fourth theme requires zero template edits — you only add files under the new theme directory. Resist the urge to write {% include 'themes/' ~ liip_theme.activeTheme ~ '/...' %}; that defeats the locator and breaks fallback.

Assets are not the bundle's problem. LiipThemeBundle only touches templates. Theme-specific CSS/JS needs Assetic/webpack output dirs keyed per theme, or use the active-theme value exposed to Twig (check the Twig extension in your installed version) to build the path.

The Laravel translation of the pattern:

// ThemeServiceProvider::boot()
View::prependNamespace('theme', resource_path('themes/'.app(Theme::class)->active()));
// then view('theme::dashboard.index') falls back to view('dashboard.index')

Pair that with a ResolveTheme middleware (cookie → user pref → header → default), a container-scoped Theme singleton, and a theme() helper. That is structurally identical to ActiveTheme + ThemeRequestListener + the file locator, and it's what I'd actually ship.

Gotchas and Tips

  • Archived since 2019. No Symfony Flex recipe, no Symfony 4/5+ support, no PHP 8, no maintained security backports. On a modern Symfony app it will fight you on service definitions alone. Fork it or reimplement. The low opportunity score (16.6) reflects exactly this: it solved a real problem for one ecosystem and then stopped.
  • Config validation is strict and the error messages are unhelpful. active_theme must appear in themes, or you get an exception at container compile time, not at request time. Same for a theme name set at runtime — validate before setActiveTheme().
  • Silent fallback is the #1 debugging trap. A typo'd directory name (themes/Mobile vs themes/mobile) doesn't error — you just silently get the default theme's template and wonder why your override never applies. Confirm the resolved path with the Symfony profiler / Twig debug, or grep the compiled template cache. Add a temporary {{ dump() }} of the active theme value.
  • Cache clearing is mandatory. Adding a theme directory, changing themes, or editing the locator config all require cache:clear. The template locator and Twig's compiled cache both need to be rebuilt; a stale cache looks exactly like "the bundle is broken."
  • HTTP caching will bite you. Theme chosen from a cookie + a shared cache (Varnish, Cloudflare, Symfony HttpCache) = mobile markup served to desktop. Either put the theme in the URL path, or send Vary: Cookie, or key the cache on the theme cookie explicitly.
  • Listener priority vs. auth. The theme request listener fires before the security firewall finishes resolving the user in some configurations. If your autodetect service needs tokenStorage, you'll get null. Prefer cookie/header detection at request time and override from the controller once the user is known.
  • Extension points worth knowing: swap the template locator service, decorate liip_theme.active_theme, or point autodetect_theme at your own service. All three are plain Symfony service overrides — no bundle surgery needed, which is why the bundle survived as long as it did.
  • Verdict for new work: do not adopt. On Symfony, use a small custom locator or symfony/ux-era templating; on Laravel, a service provider + middleware + View::prependNamespace() gives you the same override-with-fallback semantics in an afternoon, with no archived dependency and no framework mismatch.
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.
elsayed85/laravel-api-exceptions
ouhssini/laravel-demo-mode
daworks/ncloud-outbound-mailer-driver
gp247/cms
ignibyte/aic-gates
nexa-merchant/blog
novasa/laravel-language-center
opencage/geocode
adolfoholzer/parameters
livenetworks/ln-starter
shitoudev/permissionmanager
johannesclimacus/laravel-tui
hocvt/seotools
vitalybaev/sypexgeo
amphibee/themosis-ignition
gito-me/fast-excel
frankyso/laravel-ipaymu
geolize-studio/laravel-chile
boomerangui/chat
cgv/localized-resource