Skip to main content
Updating your package’s JavaScript or CSS does not automatically change files that have already been copied into the application’s public directory. To avoid a state where only the PHP code is on the new version while the browser keeps using the old assets, you need to decide who owns the published destination and how it gets updated. This page assumes you are familiar with the package development basics and organizes distribution and maintenance design around Laravel 13’s publishing process. The implementation was checked against laravel/framework v13.35.0.

Publishing is neither a build nor a sync

ServiceProvider::publishes() registers a source and a destination. The actual file copying is done by vendor:publish. It does not transpile JavaScript, build CSS, or add entries to the application’s Vite configuration. If your package distributes prebuilt files, use a structure like the following.
For the initial publish, specify the provider and tag explicitly.
In this example, public/vendor/courier/courier.css and courier.js are created. If your design distributes them as plain CSS and JavaScript, you can reference them from Blade as follows.
If you distribute ES modules or another format, adjust how they are loaded to match. asset() is a helper that generates URLs; it does not build, publish, or generate content-based file names.
Place only build artifacts that are safe to expose in the source directory. In this example, the destination is public, which is accessible from the web. Do not include configuration files or internal data in the same publish group.

Tags are not a provider-specific namespace

ServiceProvider registers publish paths in an array keyed by provider class and in an array keyed by tag. The tag array is shared across all providers, so using a generic tag such as public can also pull in other packages. When both are specified, pathsForProviderAndGroup() uses array_intersect_key() with source paths as keys. It is not a mechanism for switching to a different destination depending on the tag. Avoid designs that register the same source multiple times to give it separate destinations for different purposes. --tag can be specified multiple times, in which case each tag is published in turn. Because --all returns early at the start of the selection process, adding --provider or --tag alongside it does not narrow the selection.
Use a package-specific asset tag in update instructions so that users’ configuration and views are not overwritten. Specifying only the provider with --force can also target that provider’s configuration and views.

Choose the right republish option

File publishing and directory publishing in VendorPublishCommand decide whether to copy based on whether the destination file exists and which options are given. The following table describes the behavior for regular asset files that exist in the source. --existing is not an option that protects edits. It overwrites existing files, but does not publish files added in the new version. For updates where the JavaScript needs newly added files, --existing alone may leave the artifacts incomplete. If your contract is that the package manages the destination and users do not edit it directly, run the following after updating.
--force does not merge differences; it also overwrites users’ edits. Keep user-customizable CSS separate from package-managed artifacts, for example by loading it as a separate file. Use a different update policy from the one for configuration and view customizations.

Deleted files remain at the destination

moveManagedFiles() in directory publishing iterates over files in the source and writes them. There is no logic that looks for files that exist only at the destination and deletes them. Even --force does not perform a full directory sync. For example, if the new version removes legacy.js, public/vendor/courier/legacy.js remains if the old version was already published. The same applies to renamed files: the file with the old name remains, so record deleted and renamed files, along with changes to references, in your release notes. If you provide steps for removing old files, list specifically the files the package owns. Do not provide steps that delete an entire directory that may contain users’ own files.

Joining laravel-assets means agreeing to be overwritten

The official Laravel 13 application skeleton includes the following script in the post-update-cmd of composer.json.
This is an application-side script. Package auto-discovery itself does not update published files. In existing applications the script may have been changed or removed, so check the consuming application’s configuration. To join this update path, change the second argument of the earlier publishes() call to an array and register the same assets under two tags.
laravel-assets is not a tag with special copy behavior. Because the skeleton’s script publishes that tag with --force, files that join it become targets for overwriting on Composer updates. Do not register configuration or views that users edit.
Automatic updates depend on the application having the script, that event being run, and the provider registering the publish paths. So that updates work even in deployments that do not meet these conditions, document a republish command that uses your package-specific tag.

Keep PHP and asset versions aligned in deployments

config:cache and view:cache do not rewrite published JavaScript or CSS. If your design keeps serving files at the same URL after publishing, browser or CDN caches may serve stale content. Include the application’s delivery policy in your update steps, such as URLs that reflect the distributed version or cache invalidation. Check the following combinations for each release.
  • In an application where nothing has been published yet, all required prebuilt files are published.
  • With the old version already published, --force updates existing files and adds new ones.
  • Asset updates do not overwrite users’ configuration, views, or custom CSS.
  • Handling of deleted and renamed files is documented, and no references to the old version remain.
  • The new content is delivered at the actual delivery URL, and the PHP and browser-side logic work together.

Package auto-discovery internals

Understand the differences between Composer updates, provider discovery, and file publishing.

Overriding and updating package views

Review maintenance policies for templates that users customize.

Integrating package caches with optimize

Learn about package caches, which are managed separately from file publishing.

Package Version Compatibility Management

Treat changes to publish destinations and distribution formats as part of your compatibility contract.

Primary sources

Last modified on October 7, 2026