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 Theme Installer Laravel Package

hexadog/laravel-theme-installer

Composer installer for Laravel theme packages that automatically places themes in the correct themes directory. Integrates with hexadog/laravel-themes, simplifying theme distribution and installation without manual publishing or path configuration.

Deep Wiki
Context7

Getting Started

The installer is a Composer plugin, not a runtime library. It changes where Composer puts theme packages: any package whose composer.json declares "type": "laravel-theme" is installed into themes/ in your project root instead of vendor/. Nothing it does is visible at request time; if you are debugging a theme at runtime, you are debugging hexadog/laravel-theme, not this package.

Minimal setup, in order:

composer require hexadog/laravel-theme-installer

Composer 2.2+ will prompt you to allow the plugin (or refuse it in non-interactive mode). Accept it, or add it to composer.json up front:

{
    "config": {
        "allow-plugins": {
            "hexadog/laravel-theme-installer": true
        }
    }
}

Then pull in a theme:

composer require hexadog/laravel-theme-<name>
ls themes/          # the theme lives here, not in vendor/

Where to look first:

  1. composer.json → config.allow-plugins (the single most common reason "it didn't work").
  2. The theme package's own composer.json → confirm "type": "laravel-theme". If the type is library, the plugin ignores it and it lands in vendor/.
  3. Your app's theme config (config/themes.php in the host hexadog/laravel-theme package) → the path the runtime expects must match the path the installer used.

First real use case: you want to edit a theme's Blade views, SCSS and assets without fighting vendor/ (which is gitignored, wiped on composer install, and never meant to be edited). Install this plugin, and themes become first-class project files you can commit, diff and review.

Implementation Patterns

Treat themes/ as source, vendor/ as build output. This is the whole point of the package. Once a theme is installed outside vendor/, commit themes/ and keep vendor/ ignored. Your deploy diff then shows theme changes like any other code change.

Customise by forking the theme, not by patching it in place. Two viable patterns:

  • Vendored fork: fork the upstream theme repo, point composer.json at your fork ("repositories": [{ "type": "vcs", "url": "[email protected]:you/theme-x.git" }]), and composer require you/theme-x. Upstream updates arrive via merge, not via composer update clobbering your edits.
  • Subclass/override: install the theme as-is and override views from your app's resources/views/vendor/<theme> (the standard Laravel package override convention). Nothing in themes/ is edited, so composer update stays safe.

Pick one per theme and be consistent — mixing them is how teams lose customisations.

Pin the theme, float the installer. Theme packages are effectively application code once they live in your repo; pin them with exact versions or a narrow constraint. The installer is tooling — a caret constraint is fine:

"require": {
    "hexadog/laravel-theme-installer": "^1.0",
    "hexadog/laravel-theme-adminlte": "2.3.*"
}

CI and containers. The plugin must run during composer install, so:

COPY composer.json composer.lock ./
RUN composer install --no-dev --no-interaction --no-scripts --prefer-dist

Do not add --no-plugins here. If your base image or a security policy globally disables plugins, whitelist this one explicitly. Also ensure the build user can write to the project root (themes/ is created there), not just to vendor/.

Deployment ordering. Because the install target is inside the release directory and outside vendor/, a zero-downtime deploy that symlinks vendor/ from a shared directory will not share themes/. Either build themes into each release (normal composer install per release) or explicitly symlink themes/ alongside vendor/ — but then remember theme updates require a composer install in the shared location.

Multi-theme / multi-tenant. Installing several theme packages is fine — each gets its own directory under themes/. Activate per environment or per tenant via the host package's config or a runtime Theme::set(). Keep theme selection in config/env, never in committed code paths.

Local path repositories during theme development. While actively developing a theme, use a path repository so themes/ symlinks to your working copy:

"repositories": [
    { "type": "path", "url": "../my-theme", "options": { "symlink": true } }
]

The installer will still place it under themes/, which keeps your app's view resolution identical between dev and prod.

Gotchas and Tips

allow-plugins is the #1 failure. Symptom: composer require succeeds, exit code 0, but the package is in vendor/ and themes/ was never created. Composer silently falls back to the default installer when a plugin isn't allowed. Run with -vvv and look for "plugin ... was not allowed" or the Installing <pkg> (type: laravel-theme) line — if you see it going to vendor/, this is why.

--no-plugins breaks everything, quietly. Any composer install --no-plugins, --no-scripts+plugin-disabled combo, or a hardened CI image that strips plugins will install themes into vendor/ and leave themes/ stale or absent. The autoloader may still resolve the old path, so you get a mix of old and new theme files rather than a clean error. Grep your build scripts for --no-plugins.

Package type is the contract. The plugin only intercepts packages with "type": "laravel-theme". If you fork a theme and drop or rename the type field in the fork's composer.json, it silently reverts to vendor/. Same if upstream renames the type in a major release — pin and read the changelog.

Changing the install path later is not a no-op. The path is baked into composer.lock's install metadata and into the generated autoloader. After changing the configured theme directory (or upgrading the installer across a path-affecting release), do a clean cycle:

rm -rf vendor themes composer.lock
composer install

Otherwise Composer believes the package is already installed at the old location and skips the move.

composer remove leaves the directory behind. Uninstalling a theme removes the lock entry but frequently leaves themes/<name>/ on disk, because the plugin's uninstall path handling can't safely distinguish "installed by me" from "you edited files here". Delete manually and commit the removal — don't let orphaned theme directories sit in the repo.

.gitignore audit. Most Laravel .gitignore files are fine, but check for a broad themes/ or /themes entry inherited from a starter kit, and for *.css/*.js ignores under public/ that would swallow built theme assets. Conversely, if you don't want to commit themes, make that an explicit decision — the package's value proposition assumes you do.

Autoloading is the plugin's problem, until it isn't. Composer generates autoload maps from the install path, so a correctly-run plugin gives you working PSR-4 for themes outside vendor/. If class-not-found errors appear right after install, the usual culprit is a stale autoloader: composer dump-autoload (with plugins enabled). Running dump-autoload --no-plugins can regenerate the map against the default vendor/ path and break resolution.

Path resolution is relative to the Composer invocation directory. Running composer install from a subdirectory or with COMPOSER=<path> moves the effective project root; themes/ may be created somewhere unexpected. Always run Composer from the directory containing composer.json.

Case sensitivity and Windows. Theme directories are derived from the package name (vendor/name → directory name), and the mapping has historically been inconsistent between releases. On case-insensitive filesystems this works locally and fails on Linux CI. Verify the actual directory name after install rather than assuming.

Config key drift. The override for the install directory lives in the root composer.json's extra block, but the exact key has changed between releases. Copy the snippet from the README matching your installed version rather than from a blog post, and confirm with composer config extra / composer show hexadog/laravel-theme-installer.

Debugging recipe. When a theme ends up in the wrong place:

composer show hexadog/laravel-theme-<name>   # shows path + type
composer install -vvv 2>&1 | grep -i -E 'theme|installer|plugin'
composer diagnose

composer show printing a path under vendor/ is definitive: the plugin did not run or did not match the type.

Extension point. The plugin is intentionally tiny — its only real surface is the install path and the package type it responds to. If you need custom placement logic (per-tenant theme directories, external asset volumes), don't fight it; install normally and move/symlink in your deploy step.

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.
uni-method/symfony-lib
tacticmedia/scim-server-bundle
neika-ai/symfony-bundle
mattleads/agent-publishing-bundle
glavweb/framework-bundle
glavweb/extra-bundle
glavweb/api-bundle
glavweb/admin-bundle
adachsoft/api-version-bundle
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