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

Tomato Themes Laravel Package

tomatophp/tomato-themes

Theme manager for TomatoPHP and Laravel applications: register, switch, and organize frontend themes with view and asset overrides, plus a simple API for building theme-aware admin panels and sites. Part of the TomatoPHP ecosystem.

View on GitHub
Deep Wiki
Context7

Getting Started

Confirm the stack before you install. This package is a presentation-layer add-on for the TomatoPHP/Filament admin ecosystem. It assumes Laravel 10/11, PHP 8.1+, and a Filament 3 panel. If your app is bespoke Blade/Inertia/Livewire with no Filament panel, stop here — the abstraction buys you nothing.

Install and publish.

composer require tomatophp/tomato-themes
php artisan vendor:publish --tag=tomato-themes-config
php artisan vendor:publish --tag=tomato-themes-assets   # tag name: verify in repo

Service provider registration is via Laravel auto-discovery; you should not need to touch config/app.php. Pin with a caret on the minor (^1.2) but commit composer.lock.

Where to look first, in this order:

  1. The published config file (config/tomato-themes.php or similarly named) — this is the single source of truth for the default/active theme and the directory conventions. Read it end to end before writing any theme code.
  2. The Artisan command list: php artisan list | grep -i theme. The description promises scaffolding and publishing commands; the exact signatures are not documented in the v1.2.2 notes, so treat the command help output as your API reference.
  3. The Filament plugin class (registered on your panel provider). This is the wiring point between the package and the admin UI.
  4. The service/registry class that resolves the active theme — this is where runtime switching happens and where you will extend if the built-in activation modes do not fit.

First use case — prove the resolution path end to end. Scaffold a throwaway theme, activate it, and confirm a known Blade marker renders on a real route. Something like:

php artisan theme:make demo        # confirm actual command name via `artisan list`
php artisan theme:publish demo

Put a unique string in the theme's layout (e.g. <!-- theme: demo -->), hit the page, and view-source. Do this before you build anything real. If the marker does not appear, the problem is activation, not your theme — and that distinction saves hours.

Ramp-up expectation: a Laravel developer is productive in under a day. The concepts (published views, asset registration, panel plugin) are idiomatic. The slow part is governance — deciding who adds themes and where they live — not the code.

Implementation Patterns

Keep theme definitions in your repository, not the package's published directory. The published theme directory is effectively a vendor artifact. If you let it become the home of your themes, a patch release that renames an asset path or view namespace silently overwrites or orphans your work. Treat the published directory as a template; copy what you need into resources/themes/ (or your chosen path) under version control, and point the config at it.

Fallback chain, written down. The value of a theme manager is the fallback: theme view → app view → package view. Establish and document which layer wins for each resource type. In practice this is Laravel's view finder path ordering, so the resolution rule is "whatever the package prepends." Verify it once with:

dump(app('view')->getFinder()->getPaths());
dump(app('view')->getFinder()->getHints());

Then write that ordering into your team docs. Ambiguity here is the number-one source of "why is my override not showing up."

Activation — pick one primary mechanism and layer the rest on top.

  • Config default — set the active theme in the config/env. Good for a single-theme deployment; zero runtime cost.
  • Middleware — switch per route group or URL segment. Good for a marketing site vs. an admin panel, or A/B at the route level. Register it explicitly and be mindful of ordering relative to anything that touches views.
  • Runtime API — call the package's setter early in the request (a service provider boot, a base controller, or middleware). This is what you use for user-preference or per-tenant switching.

Do not mix all three casually. Define the precedence in one place (ThemeResolver::resolve() wrapping the package) so you have a single seam to change later.

Per-tenant theming is not what this package does. It manages deployment-level themes. If per-tenant theming is a product feature, build a thin resolver layer on top of the runtime API and keep tenant→theme mapping in your own database. Do not expect the package to store that mapping.

Assets: publish, then cache-bust. Themes ship assets into public/. After every deploy that touches theme assets you need a publish step plus cache-busting. If the package's asset helper does not add a version query string, wrap it:

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

If your frontend is on Vite, assume the package's asset pipeline is not Vite-aware until you confirm otherwise. The safe pattern is to let Vite build into the theme's public directory and have the theme's layout reference the Vite manifest, rather than relying on the package's asset publishing for compiled CSS/JS.

Filament panel integration. Register the plugin on the panel in your PanelProvider. If the package offers theme presets for the panel itself, set them there, not in a service provider — panel configuration is evaluated at panel boot, and setting it elsewhere is a race you will lose intermittently.

Testing pattern — the cheapest insurance you can buy. One smoke test that asserts a known theme marker renders on a known route:

public function test_active_theme_renders(): void
{
    config(['tomato-themes.active' => 'demo']);
    $this->get('/')->assertSee('theme: demo', false);
}

A patch release that renames a view namespace or config key will make the theme fall back to defaults silently. Without this test, you find out during a demo.

Development loop. Symlink a working theme into the published directory while iterating, and keep the canonical copy in your repo. Delete the symlink before you cut a release so the deploy step is deterministic.

Gotchas and Tips

Stale view cache after switching is the most common failure. Laravel compiles Blade to disk; a theme switch does not necessarily invalidate it. In local development, run php artisan view:clear after any activation change. In production, make view:clear (and config:clear, before config:cache) an explicit deploy step.

config:cache in production interacts badly with runtime activation. If activation is read from config at boot, cached config freezes it. If activation is applied at runtime through the package's API, make sure that call happens after config is loaded and is not itself reading a runtime-mutated config value. Pin this down in a staging test; it is the difference between a working multi-theme deploy and a mysterious "always the default theme" bug.

View path precedence is decided at boot, not per request. Prepending a theme's view path is not free to redo mid-request. If you need per-route switching, do it in middleware that runs before the view is rendered, and do not expect to switch twice in one request.

Silent breakage on patch releases. The v1.2.2 release notes are a changelog link and nothing else — no summary, no breaking-change labels. A patch bump conventionally implies backward compatibility, but you cannot confirm that from the metadata. Before upgrading, read the raw compare diff and specifically check whether it touches: config keys, view namespaces, or asset manifest paths. Any of those three will break a live theme without erroring. Record the diff review in the PR so the next maintainer inherits the rationale.

Dependency drift with Filament. Theme packages accumulate implicit coupling to Filament majors. Run composer why-not tomatophp/tomato-themes <filament-version> before merging a Filament upgrade. If both an upgrade and a theme change are pending, land the Filament upgrade first and re-validate the theme package against it — coupling the two makes any failure unattributable.

Extension points: prefer wrapping to forking. Put your own ThemeResolver / ThemeManager service in front of the package. It gives you a place to add tenant scoping, logging, and fallback policy without touching vendor code. Only fork if you need to change how themes resolve, and decide to fork before you customize — vendoring under your own namespace after you have built on top is painful.

Debugging checklist when a theme "does not load":

  1. dump(app('view')->getFinder()->getPaths()) — is the theme path actually registered?
  2. Is config:cache stale? (php artisan config:clear)
  3. Is the view cache stale? (php artisan view:clear)
  4. Did the asset publish step run on this deploy? Check mtimes in public/.
  5. Is a middleware switching the theme after the point where you expected it?

Walk that list in order; it resolves the overwhelming majority of reports.

Watch container cold-start. If theme discovery happens at boot, measure it under PHP-FPM. It is unlikely to be a first-order bottleneck, but it is a boot-path cost, and boot-path costs compound with package count.

Support posture. Community-only, single maintainer, no SLA. Fine for internal admin tooling. If the panel is customer-facing, keep a fork contingency ready and do not let theme definitions live only inside the package's published directory — that is what makes abandonment recoverable rather than a migration project.

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