# Laravel Package Tools
Laravel Package Tools is a streamlined toolkit for Laravel package developers that eliminates boilerplate code when creating packages. It provides a fluent API through a base `PackageServiceProvider` class that handles the registration and publishing of common package components like config files, migrations, views, commands, and routes. This package is built by Spatie and follows their opinionated but flexible approach to package structure, making it simple to create well-organized Laravel packages.
The package works by extending the provided `PackageServiceProvider` and using the fluent `Package` API to configure all aspects of your package. Under the hood, it handles all the necessary Laravel service provider boilerplate, file publishing configurations, and path registrations. It supports Laravel 9 through 12 and requires PHP 8.0 or higher, making it compatible with modern Laravel applications while maintaining broad version support.
## Package Configuration
### name() - Set Package Name
The `name()` method is mandatory and defines the unique identifier for your package. This name is used throughout the package for publishing tags, view namespaces, and command signatures. If your package name starts with "laravel-", that prefix is automatically stripped for the short name used in publishing commands.
```php
use Spatie\LaravelPackageTools\PackageServiceProvider;
use Spatie\LaravelPackageTools\Package;
class YourPackageServiceProvider extends PackageServiceProvider
{
public function configurePackage(Package $package): void
{
$package->name('your-package-name');
}
}
```
### hasConfigFile() - Register Configuration Files
Registers one or more config files from your package's `config/` directory, making them mergeable with the application config and publishable. The default config filename matches your package's short name, but you can specify different names.
```php
use Spatie\LaravelPackageTools\PackageServiceProvider;
use Spatie\LaravelPackageTools\Package;
class YourPackageServiceProvider extends PackageServiceProvider
{
public function configurePackage(Package $package): void
{
$package
->name('your-package-name')
->hasConfigFile() // Expects config/your-package-name.php
->hasConfigFile(['custom-config', 'another-config']); // Multiple configs
}
}
// Users can publish with:
// php artisan vendor:publish --tag=your-package-name-config
// Access in code:
$value = config('your-package-name.key');
```
## Views and Components
### hasViews() - Register Blade Views
Registers all Blade views from `resources/views/` directory with Laravel's view system and makes them publishable. You can optionally specify a custom namespace instead of using the package short name.
```php
use Spatie\LaravelPackageTools\PackageServiceProvider;
use Spatie\LaravelPackageTools\Package;
class YourPackageServiceProvider extends PackageServiceProvider
{
public function configurePackage(Package $package): void
{
$package
->name('your-package-name')
->hasViews() // Default namespace: your-package-name
->hasViews('custom-namespace'); // Custom namespace
}
}
// Use views in your package or app:
// resources/views/myView.blade.php
view('your-package-name::myView');
// resources/views/subdirectory/nested.blade.php
view('your-package-name::subdirectory.nested');
// Publish with:
// php artisan vendor:publish --tag=your-package-name-views
```
### hasViewComponent() - Register Blade Components
Registers Blade view components from `src/Components/` directory with a prefix, making them available as `` tags. Components are also publishable to the application.
```php
use Spatie\LaravelPackageTools\PackageServiceProvider;
use Spatie\LaravelPackageTools\Package;
use MyPackage\Components\Alert;
use MyPackage\Components\Button;
class YourPackageServiceProvider extends PackageServiceProvider
{
public function configurePackage(Package $package): void
{
$package
->name('your-package-name')
->hasViewComponent('spatie', Alert::class)
->hasViewComponent('spatie', Button::class);
}
}
// Use in Blade templates:
// Operation completed!
// Click Me
// Publish with:
// php artisan vendor:publish --tag=your-package-name-components
```
### hasViewComposer() - Register View Composers
Registers view composers that automatically pass data to specific views or all views. Accepts either a class name or a closure for flexible data binding.
```php
use Spatie\LaravelPackageTools\PackageServiceProvider;
use Spatie\LaravelPackageTools\Package;
use MyPackage\ViewComposers\NavigationComposer;
class YourPackageServiceProvider extends PackageServiceProvider
{
public function configurePackage(Package $package): void
{
$package
->name('your-package-name')
->hasViews()
->hasViewComposer('your-package-name::layout', NavigationComposer::class)
->hasViewComposer('*', function($view) {
$view->with('appVersion', '1.0.0');
});
}
}
// NavigationComposer class example:
namespace MyPackage\ViewComposers;
use Illuminate\View\View;
class NavigationComposer
{
public function compose(View $view)
{
$view->with('menuItems', [
['title' => 'Home', 'url' => '/'],
['title' => 'About', 'url' => '/about'],
]);
}
}
```
### sharesDataWithAllViews() - Share Global View Data
Shares data globally with all views in the application, making variables available without explicit passing.
```php
use Spatie\LaravelPackageTools\PackageServiceProvider;
use Spatie\LaravelPackageTools\Package;
class YourPackageServiceProvider extends PackageServiceProvider
{
public function configurePackage(Package $package): void
{
$package
->name('your-package-name')
->hasViews()
->sharesDataWithAllViews('companyName', 'Acme Corp')
->sharesDataWithAllViews('supportEmail', 'support@acme.com');
}
}
// Now all views can access these variables:
// {{ $companyName }} - outputs "Acme Corp"
// {{ $supportEmail }} - outputs "support@acme.com"
```
## Database Operations
### hasMigration() / hasMigrations() - Register Migrations
Registers migration files from `database/migrations/` directory, making them publishable with automatic timestamp prefixes. Migration files should have `.php.stub` extension in your package.
```php
use Spatie\LaravelPackageTools\PackageServiceProvider;
use Spatie\LaravelPackageTools\Package;
class YourPackageServiceProvider extends PackageServiceProvider
{
public function configurePackage(Package $package): void
{
$package
->name('your-package-name')
->hasMigration('create_package_tables')
->hasMigrations(['create_users_table', 'add_fields_to_posts']);
}
}
// database/migrations/create_package_tables.php.stub
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
public function up()
{
Schema::create('package_tables', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->timestamps();
});
}
};
// Users publish with:
// php artisan vendor:publish --tag=your-package-name-migrations
// Then run: php artisan migrate
```
### runsMigrations() - Auto-run Migrations
Configures migrations to run automatically when the package is installed, without requiring users to publish them first. Useful for packages that need their database structure immediately available.
```php
use Spatie\LaravelPackageTools\PackageServiceProvider;
use Spatie\LaravelPackageTools\Package;
class YourPackageServiceProvider extends PackageServiceProvider
{
public function configurePackage(Package $package): void
{
$package
->name('your-package-name')
->hasMigrations(['create_package_tables', 'create_logs_table'])
->runsMigrations(); // Migrations run automatically on install
}
}
// Migrations now run automatically when package is loaded
// No need for users to publish first
```
### discoversMigrations() - Auto-discover All Migrations
Automatically discovers and registers all migration files in the migrations directory, eliminating the need to manually specify each migration file name.
```php
use Spatie\LaravelPackageTools\PackageServiceProvider;
use Spatie\LaravelPackageTools\Package;
class YourPackageServiceProvider extends PackageServiceProvider
{
public function configurePackage(Package $package): void
{
$package
->name('your-package-name')
->discoversMigrations(); // Auto-finds all in database/migrations
// Or with custom path:
// ->discoversMigrations(path: '/custom/migrations/path');
}
}
// All .php.stub files in database/migrations/ are now publishable
// php artisan vendor:publish --tag=your-package-name-migrations
```
## Commands and CLI
### hasCommand() / hasCommands() - Register Artisan Commands
Registers one or more Artisan commands from your package, making them available in the application's command palette. Commands can be both callable and console commands.
```php
use Spatie\LaravelPackageTools\PackageServiceProvider;
use Spatie\LaravelPackageTools\Package;
use MyPackage\Commands\ExportDataCommand;
use MyPackage\Commands\ImportDataCommand;
use MyPackage\Commands\ProcessQueueCommand;
class YourPackageServiceProvider extends PackageServiceProvider
{
public function configurePackage(Package $package): void
{
$package
->name('your-package-name')
->hasCommand(ExportDataCommand::class)
->hasCommands([
ImportDataCommand::class,
ProcessQueueCommand::class,
]);
}
}
// Example command class:
namespace MyPackage\Commands;
use Illuminate\Console\Command;
class ExportDataCommand extends Command
{
protected $signature = 'package:export {--format=json}';
protected $description = 'Export package data';
public function handle()
{
$format = $this->option('format');
$this->info("Exporting data in {$format} format...");
// Export logic here
return 0;
}
}
// Users run: php artisan package:export --format=csv
```
### hasInstallCommand() - Create Installation Command
Generates an automated installation command that can publish configs, migrations, assets, and even ask to star the GitHub repo. This provides a one-command setup experience for package users.
```php
use Spatie\LaravelPackageTools\PackageServiceProvider;
use Spatie\LaravelPackageTools\Package;
use Spatie\LaravelPackageTools\Commands\InstallCommand;
class YourPackageServiceProvider extends PackageServiceProvider
{
public function configurePackage(Package $package): void
{
$package
->name('your-package-name')
->hasConfigFile()
->hasAssets()
->hasMigration('create_package_tables')
->publishesServiceProvider('MyServiceProviderName')
->hasInstallCommand(function(InstallCommand $command) {
$command
->startWith(function(InstallCommand $command) {
$command->info('Installing Your Package...');
})
->publishConfigFile()
->publishAssets()
->publishMigrations()
->askToRunMigrations()
->copyAndRegisterServiceProviderInApp()
->askToStarRepoOnGitHub('your-vendor/your-package')
->endWith(function(InstallCommand $command) {
$command->info('Installation complete! Check the config.');
});
});
}
}
// Users run one command to install everything:
// php artisan your-package-name:install
```
## Assets and Resources
### hasAssets() - Register Publishable Assets
Makes all assets in `resources/dist/` directory publishable to the application's `public/vendor/` directory, including CSS, JavaScript, images, and other static files.
```php
use Spatie\LaravelPackageTools\PackageServiceProvider;
use Spatie\LaravelPackageTools\Package;
class YourPackageServiceProvider extends PackageServiceProvider
{
public function configurePackage(Package $package): void
{
$package
->name('your-package-name')
->hasAssets();
}
}
// Package structure:
// resources/dist/css/styles.css
// resources/dist/js/app.js
// resources/dist/images/logo.png
// Users publish with:
// php artisan vendor:publish --tag=your-package-name-assets
// Published to:
// public/vendor/your-package-name/css/styles.css
// public/vendor/your-package-name/js/app.js
// public/vendor/your-package-name/images/logo.png
// Use in Blade:
//
//
```
### hasTranslations() - Register Language Files
Registers translation files from `resources/lang/` directory, supporting both PHP arrays and JSON string translations. Makes translations accessible and publishable.
```php
use Spatie\LaravelPackageTools\PackageServiceProvider;
use Spatie\LaravelPackageTools\Package;
class YourPackageServiceProvider extends PackageServiceProvider
{
public function configurePackage(Package $package): void
{
$package
->name('your-package-name')
->hasTranslations();
}
}
// resources/lang/en/messages.php
return [
'welcome' => 'Welcome to our package!',
'goodbye' => 'Thank you for using :name',
];
// resources/lang/es/messages.php
return [
'welcome' => '¡Bienvenido a nuestro paquete!',
'goodbye' => 'Gracias por usar :name',
];
// resources/lang/es.json
{
"Hello!": "¡Hola!",
"Goodbye!": "¡Adiós!"
}
// Use in code:
trans('your-package-name::messages.welcome'); // "Welcome to our package!"
trans('your-package-name::messages.goodbye', ['name' => 'MyApp']);
__('Hello!'); // "¡Hola!" when locale is 'es'
// Publish with:
// php artisan vendor:publish --tag=your-package-name-translations
```
### hasInertiaComponents() - Register Inertia Components
Registers Vue or JSX components from `resources/js/Pages/` for use with Inertia.js, making them publishable to the application for server-side rendering and client-side interactivity.
```php
use Spatie\LaravelPackageTools\PackageServiceProvider;
use Spatie\LaravelPackageTools\Package;
class YourPackageServiceProvider extends PackageServiceProvider
{
public function configurePackage(Package $package): void
{
$package
->name('your-package-name')
->hasInertiaComponents();
}
}
// resources/js/Pages/Dashboard.vue
// resources/js/Pages/Settings/Profile.vue
// Use in controllers:
use Inertia\Inertia;
class DashboardController extends Controller
{
public function index()
{
return Inertia::render('YourPackageName/Dashboard', [
'stats' => ['users' => 150, 'revenue' => 5000]
]);
}
public function profile()
{
return Inertia::render('YourPackageName/Settings/Profile', [
'user' => auth()->user()
]);
}
}
// Publish components:
// php artisan vendor:publish --tag=your-package-name-inertia-components
```
## Routing
### hasRoute() / hasRoutes() - Register Route Files
Registers route files from the `routes/` directory, automatically loading them into Laravel's routing system without requiring users to manually register them.
```php
use Spatie\LaravelPackageTools\PackageServiceProvider;
use Spatie\LaravelPackageTools\Package;
class YourPackageServiceProvider extends PackageServiceProvider
{
public function configurePackage(Package $package): void
{
$package
->name('your-package-name')
->hasRoute('web')
->hasRoutes(['api', 'admin']);
}
}
// routes/web.php
use Illuminate\Support\Facades\Route;
use MyPackage\Controllers\DashboardController;
Route::middleware('web')->group(function () {
Route::get('/package/dashboard', [DashboardController::class, 'index'])
->name('package.dashboard');
});
// routes/api.php
use Illuminate\Support\Facades\Route;
use MyPackage\Controllers\ApiController;
Route::middleware('api')->prefix('api/package')->group(function () {
Route::get('/data', [ApiController::class, 'getData']);
Route::post('/process', [ApiController::class, 'process']);
});
// Routes are automatically registered and available
// GET /package/dashboard
// GET /api/package/data
// POST /api/package/process
```
### publishesServiceProvider() - Publish Custom Service Provider
Publishes a stub service provider file from `resources/stubs/` to the application's `app/Providers/` directory, useful for packages that need user-customizable providers like Laravel Horizon.
```php
use Spatie\LaravelPackageTools\PackageServiceProvider;
use Spatie\LaravelPackageTools\Package;
class YourPackageServiceProvider extends PackageServiceProvider
{
public function configurePackage(Package $package): void
{
$package
->name('your-package-name')
->publishesServiceProvider('MyPackageServiceProvider');
}
}
// resources/stubs/MyPackageServiceProvider.php.stub
namespace App\Providers;
use Illuminate\Support\ServiceProvider;
class MyPackageServiceProvider extends ServiceProvider
{
public function boot()
{
// Users can customize this
config(['your-package-name.custom_setting' => true]);
}
}
// Publish with:
// php artisan vendor:publish --tag=your-package-name-provider
// Published to:
// app/Providers/MyPackageServiceProvider.php
```
## Lifecycle Hooks
### registeringPackage() / packageRegistered() - Registration Hooks
Lifecycle hooks that execute during the service provider's register phase, allowing custom logic before and after package registration.
```php
use Spatie\LaravelPackageTools\PackageServiceProvider;
use Spatie\LaravelPackageTools\Package;
use Illuminate\Support\Facades\Gate;
class YourPackageServiceProvider extends PackageServiceProvider
{
public function configurePackage(Package $package): void
{
$package
->name('your-package-name')
->hasConfigFile();
}
public function registeringPackage()
{
// Runs at the START of register()
// Good for: binding interfaces, setting up singletons
$this->app->singleton('package.service', function ($app) {
return new PackageService($app['config']);
});
}
public function packageRegistered()
{
// Runs at the END of register()
// Good for: registering policies, additional bindings
$this->app->bind('package.helper', function () {
return new PackageHelper();
});
}
}
// Access registered services:
$service = app('package.service');
$helper = app('package.helper');
```
### bootingPackage() / packageBooted() - Boot Hooks
Lifecycle hooks that execute during the service provider's boot phase, perfect for registering gates, event listeners, or performing initialization that requires all services to be registered.
```php
use Spatie\LaravelPackageTools\PackageServiceProvider;
use Spatie\LaravelPackageTools\Package;
use Illuminate\Support\Facades\Gate;
use Illuminate\Support\Facades\Event;
use MyPackage\Events\DataProcessed;
use MyPackage\Listeners\SendNotification;
class YourPackageServiceProvider extends PackageServiceProvider
{
public function configurePackage(Package $package): void
{
$package
->name('your-package-name')
->hasConfigFile()
->hasViews();
}
public function bootingPackage()
{
// Runs at the START of boot()
// Good for: registering gates, validating environment
Gate::define('use-package-admin', function ($user) {
return $user->hasRole('admin');
});
if (! config('your-package-name.enabled')) {
return;
}
}
public function packageBooted()
{
// Runs at the END of boot()
// Good for: event listeners, model observers, final setup
Event::listen(DataProcessed::class, SendNotification::class);
if ($this->app->runningInConsole()) {
$this->publishes([
__DIR__ . '/../docs' => base_path('docs/package'),
], 'docs');
}
}
}
// Gates and events are now registered
// Can use: Gate::allows('use-package-admin')
// Can fire: event(new DataProcessed($data));
```
## Summary
Laravel Package Tools dramatically simplifies Laravel package development by providing a fluent, chainable API that handles all the tedious service provider boilerplate. The main use cases include creating packages with publishable configs, migrations, and views; building packages with custom Artisan commands and routes; developing UI component libraries with Blade or Inertia components; and creating full-featured packages with automated installation workflows. The package is particularly valuable for developers who create multiple Laravel packages and want consistency across their ecosystem.
The integration pattern is straightforward: extend `PackageServiceProvider`, implement `configurePackage()`, and chain the appropriate methods based on your package's needs. The package automatically handles file discovery, publishing tags, path resolution, and namespace registration. For complex packages, lifecycle hooks provide fine-grained control over the registration and boot process. Whether you're building a simple utility package or a comprehensive Laravel extension, this toolkit ensures your package follows Laravel conventions while minimizing the code you need to write and maintain.