Skip to content

Publishing and Overriding Package Files ​

Evolve ships as one Composer package, racerfish/evolve. A project changes how a site looks or behaves by putting its own copy of a package file where Laravel looks first: a block view in the theme, a blueprint in app/Blueprints, a config file in config/. This page explains which files can be copied, where each copy has to go, how the package finds it, and how to keep copies in line when you upgrade.

The rule behind every section: a copy never updates itself. From the moment a file is in your project, the package can change the original and your site keeps using the old version. Copy only what you need to change, and check your copies on every upgrade.

Package and project ​

Lives in the package (vendor/racerfish/evolve/)Lives in the project
Panel views, Livewire components, controllers, models, migrations, routes, translationsconfig/*.php files the starter ships or you publish
The theme:: views (head, consentMode, consentPlatform, trackingConfig, structuredData, ...) in theme/views/The theme in theme/ (published once with theme:publish, then yours)
Blueprint sources in core/app-publish/Blueprints and <module>/app-publish/BlueprintsBlueprint classes in app/Blueprints
Module block and frontend views in <module>/views/Copies of single views under theme/views/
The default config of every module and of the coreAssets in public/vendor/evolve (republished by Composer on every install and update)

A fresh project from evolve-starter already carries config/evolve.php, image.php, fortify.php, translatable.php, llm.php, livewire.php and config/view.php. It has no theme/ directory and no app/Blueprints yet: the starter README publishes both during setup (see Installation). Everything else in this page is published on demand.

Where Laravel looks for views ​

Evolve registers its views under namespaces: evolve:: for the panel (core/views), theme:: for the head and consent snippets (theme/views in the package), and one namespace per module (blog::, company::, faq::, ...). For every namespace, Laravel first checks vendor/<namespace>/ inside each directory listed in view.paths, then falls back to the package.

The starter's config/view.php lists one directory:

php
'paths' => [
    base_path('theme/views'),
],

So in a project built from the starter, copies go under theme/views/vendor/<namespace>/:

ViewPackage fileProject copy
evolve::components.form.inputvendor/racerfish/evolve/core/views/components/form/input.blade.phptheme/views/vendor/evolve/components/form/input.blade.php
theme::headvendor/racerfish/evolve/theme/views/head.blade.phptheme/views/vendor/theme/head.blade.php
blog::frontend.postvendor/racerfish/evolve/blog/views/frontend/post.blade.phptheme/views/vendor/blog/frontend/post.blade.php

resources/views/vendor/<namespace>/ works only in projects whose config/view.php also lists resource_path('views'), which is the Laravel default and still the case in some older projects. Run php artisan config:show view when in doubt.

WARNING

The relative path inside the namespace has to match the package file exactly. A copy with a typo in its path is ignored without an error, and the package view keeps rendering.

There is no publish tag for panel views. Copy the single file you need by hand. Before you do, check whether a blueprint option, a config value, a setting or CSS in the theme can do the job: every panel view copy is an upgrade task for as long as it exists.

Blocks: blueprint and view ​

A block has two halves, and they are overridden separately. The internals are in Blueprints and blocks.

Blueprints ​

A blueprint is a PHP class. Evolve discovers blueprints only in the directories of evolve.blueprints.paths, which defaults to App\Blueprints in app/Blueprints. In a real install the package does not read its own app-publish/Blueprints directories (only the workbench does), so a block exists in the block picker once its blueprint class is in your project. The email module is the one exception: it adds its own path for the campaign blocks, which a project does not publish.

Publish the core blueprints and those of the modules you use:

bash
php artisan vendor:publish --tag=evolve.blueprints
php artisan vendor:publish --tag=blog.blueprints --tag=company.blueprints --tag=event.blueprints

From then on the files in app/Blueprints are yours. To change the fields of the hero block, edit app/Blueprints/Hero.php; there is nothing to override. When two classes declare the same component ID (cms.hero), the one read last wins, and a class always wins over a legacy YAML definition in config/components/ with the same ID. See Blueprints for writing your own.

Block views ​

The theme renders each block through theme/views/components/common/blocks.blade.php, which tries these in order:

  1. theme/views/components/blocks/<namespace>/<name>.blade.php (cms.hero becomes blocks/cms/hero.blade.php)
  2. a Livewire block: a view theme/views/livewire/blocks/<namespace>/<name>.blade.php or a Livewire component the module registered
  3. the module's own view, <module>::blocks.<name> (since 2.6)
  4. nothing: a red placeholder locally, an <!-- evolve:missing-block --> comment in production

The core block views come with the theme, so theme/views/components/blocks/cms/ is already in your project after theme:publish. Edit them in place.

A module block renders from the package (step 3) until you put a copy at step 1. To change the company members block, copy that one view:

bash
mkdir -p theme/views/components/blocks/company
cp vendor/racerfish/evolve/company/views/blocks/members.blade.php \
   theme/views/components/blocks/company/members.blade.php

php artisan vendor:publish --tag=company.components does the same for every company view at once, plus the YAML definitions and the app-publish overlay (next section). Prefer the single copy when you only need one view.

Module publish tags ​

Each module offers the tags below. The <module>.config and <module>.components tags come from ModuleServiceProvider, the rest from the module's own provider.

TagCopies
<module>.configconfig/<module>.php
<module>.blueprintsthe module's app-publish/Blueprints to app/Blueprints (ai-chatbot, blog, company, event, scout24)
<module>.componentsthe module's YAML definitions to config/components/, the whole app-publish/ overlay to app/, and its frontend, block and Livewire views into theme/views/ (paths per module below)
ai-chatbot.viewsblock view to theme/views/components/blocks/cms
email.viewscampaign render templates to theme/views/vendor/email/render
shopify.assetspublic/vendor/shopify/cart-store.js

Where <module>.components puts views:

ModuleViews go to
blogtheme/views/vendor/blog/frontend, theme/views/livewire/blocks/blog
companytheme/views/vendor/company/frontend, theme/views/components/blocks/company
eventtheme/views/vendor/event/frontend, theme/views/components/blocks/events
faqtheme/views/vendor/faq/faq, theme/views/vendor/faq/frontend, theme/views/components/blocks/faq
locatortheme/views/vendor/locator/frontend, theme/views/components/blocks/locator, theme/views/livewire/blocks/locator
projecttheme/views/vendor/project/project, theme/views/vendor/project/frontend
scout24theme/views/vendor/scout24/vehicle, theme/views/components/blocks/scout24
shopifytheme/views/vendor/shopify/frontend, theme/views/components/blocks/shopify

The tag list of an install is also available from php artisan vendor:publish without arguments, which asks which provider or tag to publish. Module tags only exist while the module is enabled.

The app-publish overlays ​

app-publish/ in a module mirrors the project's app/ directory. It holds the blueprint classes and, for two modules, a Livewire block class:

Overlay filePublished toEffect
blog/app-publish/Livewire/Blocks/Blog/Posts.phpapp/Livewire/Blocks/Blog/Posts.phpThe blog registers App\Livewire\Blocks\Blog\Posts for blocks.blog.posts when that class exists, otherwise its own.
locator/app-publish/Livewire/Blocks/Locator/Locations.phpapp/Livewire/Blocks/Locator/Locations.phpSame for the store finder.

The overlay is copied when you run <module>.blueprints (blueprints only) or <module>.components (everything). It is never copied on composer update. Once the Livewire class is in app/, it replaces the package class completely, so it misses every fix the package makes to the original. Publish it only to change the component's logic, and compare it with vendor/racerfish/evolve/<module>/app-publish/ after each upgrade.

Core and theme publish tags ​

TagCopies
evolve.configconfig/evolve.php, image.php, fortify.php, translatable.php
evolve.blueprints (alias evolve.components)core/app-publish/Blueprints to app/Blueprints
evolve.legacy-componentsthe core YAML block definitions to config/components/
evolve.image, evolve.translatable, evolve.llmconfig/image.php, config/translatable.php, config/llm.php
evolve.assetsthe panel build to public/vendor/evolve (the starter republishes it with --force after every composer install and composer update)
evolve.deploy-excludedeploy_exclude.txt
evolve.claude.skill, evolve.claude.starterClaude Code files, see MCP Server
themethe whole starter theme to theme/ and tsconfig.json; use php artisan theme:publish
theme.configconfig/theme.php
theme.viewsthe theme:: views to resources/views/vendor/theme

theme.views targets resources/views, which a starter project does not read (see Where Laravel looks for views). In such a project, copy the single theme:: view you need to theme/views/vendor/theme/ instead.

vendor:publish skips files that already exist. Running a tag again after an upgrade therefore only adds new files, for example a blueprint a release introduced. --force overwrites, and with it every change you made to the copies. php artisan theme:publish --force does that for the whole theme.

Config files ​

The package merges its own config under yours. Laravel's merge works on the top level only: a key you have in config/evolve.php replaces the package value for that key as a whole. A new top level key in a release arrives without you doing anything. A new nested key under a top level key your file already has does not, because your array replaces the package array.

config/services.php is the exception: Evolve merges it recursively, and the Shadowed config health check reports package values a project file empties. Details in Package layout and boot.

Publish a config only to change a value that has no .env key or setting. config/health.php and config/tracking.php have no publish tag. The package merges them, and their values come from .env keys and, for many of them, the Settings Store.

What not to copy ​

  • Controllers, models, Livewire panel components, services. Copying the file does nothing, because the package loads its own classes by namespace. Extend the class and swap it in the container, see Extending Evolve.
  • Migrations. The package loads them from vendor/ and has no publish tag for them. Leave them there.
  • Translations. Override single keys in lang/vendor/<namespace>/ rather than copying the whole file.
  • The whole panel. Copy single views, never a directory.

Keeping copies in sync on upgrade ​

php artisan evolve:view-overrides lists your copies of evolve:: and theme:: views and the package changes each copy has to follow. It looks in every vendor/evolve and vendor/theme directory under your view paths, so it finds copies in theme/views/vendor/ as well as in resources/views/vendor/. Run it with the version you upgrade from:

bash
php artisan evolve:view-overrides --since=2.18.0

The table shows each copy with the number of changes released after that version, or none known. Below the table every affected view gets its changes spelled out. --strict exits with 1 while a copy has changes to follow, for a deploy script or CI job.

The changes come from a registry in the package, core/src/Support/ViewOverrides.php:

  • CHANGES: per view, the version and what a copy has to change. Every release that changes a view a project might have copied adds an entry there.
  • REMOVED: views the package deleted. A copy of one is reported as safe to delete.
  • PATTERNS: markup that breaks in any panel view copy, whatever its name (for example Font Awesome tags since 2.13). Frontend, mail and theme:: views are not checked against them.

Module views (blog::, company::, ...), block views in theme/views/components/blocks/ and the app-publish overlays are not covered. Compare them by hand after each upgrade:

bash
diff vendor/racerfish/evolve/company/views/blocks/members.blade.php \
     theme/views/components/blocks/company/members.blade.php

The upgrade guide has the full workflow and the log of every change so far in Overridden Panel Views.