Skip to main content

What is a package?

In Laravel, a package is a Composer package that adds functionality to an application. There are two broad categories:
  • Standalone packages — General-purpose PHP libraries with no Laravel dependency (e.g., Carbon, Pest)
  • Laravel packages — Packages with routes, controllers, views, config, and other features tightly integrated with Laravel
This guide covers the latter: packages built specifically for Laravel. Package development requires a deep understanding of Laravel internals, including service providers, facades, and config publishing.
When writing tests for a package, use Orchestra Testbench. It lets you write package tests just as you would tests inside a normal Laravel application.

Package auto-discovery

When a package is installed, Laravel reads the extra.laravel section of composer.json and automatically registers service providers and facades.
With this configuration in place, users do not need to manually edit bootstrap/providers.php — the package is loaded automatically.
Package auto-discovery internals explains how this auto-discovery is implemented and when the cache is rebuilt.

Disabling auto-discovery

Users who want to disable auto-discovery for a specific package can do so in their application’s composer.json.

The role of service providers

A service provider is the entry point for a package. It is where you centralize the registration of views, config, migrations, routes, and other resources with Laravel. Service providers extend Illuminate\Support\ServiceProvider and have two methods: register and boot.
Do not register event listeners, routes, or views inside the register method. You might accidentally use a service from another service provider that has not yet been loaded. Always perform any work beyond bindings in the boot method.

Publishing config files

publishes() — publish files

Calling publishes() in the boot method allows users to copy config files into their application with the vendor:publish command.
Once published, config values are accessible the usual way.

mergeConfigFrom() — merge with defaults

Using mergeConfigFrom() in the register method ensures that the package’s default values are used even when users have not published the config file.
mergeConfigFrom() does not perform a deep merge for nested arrays. When a config has multi-dimensional arrays, options not defined by the user will not be merged from deeper levels.

Organizing with publish tags

Pass a tag as the second argument to publishes() so users can choose which resources to publish.

Registering routes

Use loadRoutesFrom() to load a routes file. It is automatically skipped when the application’s route cache is active.
Reference your package’s controllers inside the routes file.

Publishing migrations

Use publishesMigrations() to publish migration files. Laravel automatically updates the timestamps when the files are published.

Publishing views

loadViewsFrom() — register views

Register a view directory with loadViewsFrom(). Use the namespace passed as the second argument to reference views with the package::view syntax.
After registration, reference views using the package namespace.
Laravel looks for views in two locations. It first checks resources/views/vendor/courier in the application; if nothing is found there, it falls back to the package’s view directory. This lets users customize views.

Publishing views

Registering Blade components

To include components in your package, register them in the boot method.
You can also register components in bulk using a component namespace.

Publishing translation files

Register translation files with loadTranslationsFrom(). Reference translations using the package::file.key syntax.
For JSON translation files, use loadJsonTranslationsFrom().

Registering commands

Register Artisan commands for your package using the commands() method. It is common practice to register them only in a console environment.

Integrating with the optimize command

If your package has its own cache, use the optimizes() method to integrate with php artisan optimize and php artisan optimize:clear.

Adding information to the about command

Use AboutCommand::add() to include package information in the output of php artisan about.

Creating a facade

A facade lets you call service container bindings as if they were static methods.
1

Create the service class

2

Create the facade class

Extend Illuminate\Support\Facades\Facade and return the service container binding key from getFacadeAccessor().
3

Bind it in the service provider

4

Register it in composer.json

Adding @method PHPDoc annotations to the facade enables IDE auto-completion.

DeferrableProvider — implementing deferred loading

Providers that only perform service container bindings can implement the DeferrableProvider interface to achieve deferred loading. The provider is not loaded until the service is actually needed, improving application performance.
Laravel compiles and stores the list of services provided by deferred providers. The provider is loaded only when one of the services listed in provides() is resolved.
Do not use DeferrableProvider for providers that need to register resources (views, routes, event listeners, etc.). If the provider is deferred, those resources will never be registered.

Testing a package

Use Orchestra Testbench to test your package in isolation. It lets you write tests as if you were inside a normal Laravel application.
Override getPackageProviders() in your test case to register the package’s service provider.

Publishing to Composer

Best practices for publishing your package to Packagist. Basic composer.json configuration
Depending on illuminate/support rather than illuminate/framework keeps the dependency tree smaller by pulling in only the Laravel components you actually need.
Example directory structure

Service providers

Learn about the register and boot methods of service providers and the details of deferred providers.

Package auto-discovery internals

Learn how extra.laravel is collected, cached, and loaded through PackageManifest.

Package version compatibility

Learn how to manage Laravel and PHP version compatibility for your packages, including GitHub Actions test matrix configuration.
Last modified on July 19, 2026