Photo by Kira auf der Heide on Unsplash
Creating Composer package for Laravel from scratch
Step-by-step instructions for those wanting to understand every little detail by actually doing it.
Let me guess. You are reading this piece because you have something reusable in your Laravel project, and you want to create a Composer package and actually reuse it in other projects. If so, hang on, you are in the right place.
Example project
In a project of mine, I’ve been using Hero Icons. For each icon I used, I created a Blade component, and rendered it wherever I needed that icon, like this:
# resources/views/components/heroicons/outline/bell.blade.php
<svg {{ $attributes }} xmlns="<http://www.w3.org/2000/svg>" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-6 h-6">
<path stroke-linecap="round" stroke-linejoin="round" d="M14.857 17.082a23.848 23.848 0 005.454-1.31A8.967 8.967 0 0118 9.75v-.7V9A6 6 0 006 9v.75a8.967 8.967 0 01-2.312 6.022c1.733.64 3.56 1.085 5.455 1.31m5.714 0a24.255 24.255 0 01-5.714 0m5.714 0a3 3 0 11-5.714 0" />
</svg>
# template using the icon
<x-hero-icons.outline.bell class="h-6 w-6" />
It worked pretty well, and I wanted to use the same approach in other projects. I found that just copying Hero icon components from project to project is pretty dumb, so I decided to extract them into a Composer package.
There are good tools that make package creation easier, but I decided to create this package manually, mainly to learn nuts and bolts by doing it.
Plan of attack
There is the best practice to follow:
Create a package inside the project, extract reusable code there, and make the project use it.
Only after everything works and is “good enough” (documented and unit-tested), move the package into its own Git repository and publish it.
In this article, I only cover the first step that takes place inside the project codebase and only the minimum steps needed by my example project. I will:
Create an empty Composer package.
Create a service provider class for the package that auto-registers itself in the project.
Move the Blade components into the package.
Register the package Blade component directory in the service provider.
Reference the Blade components in the templates that use Heroicons.
Creating an empty Composer package
Create the
packages/osmianski/laravel-heroicons/composer.json
:{ "name": "osmianski/laravel-heroicons", "description": "Heroicons as Laravel Blade components", "license": "MIT", "authors": [ { "name": "Vlad Osmianski", "email": "vlaosm@gmail.com", "homepage": "<https://osmianski.com/>" } ], "keywords": ["Laravel", "Blade", "Heroicons"], "require": { "illuminate/support": "~10" }, "autoload": { "psr-4": { "Osmianski\\\\Heroicons\\\\": "src/" } } }
Reference it in the project’s
composer.json
:{ ... "require": { ... "osmianski/laravel-heroicons": "@dev" }, ... "repositories": { "osmianski/laravel-heroicons": { "type": "path", "url": "packages/osmianski/laravel-heroicons", "options": { "symlink": true } } } }
Run:
composer update
Notice that the package name, osmianski/laravel-heroicons
, differs from the root PHP namespace, Osmianski\\Heroicons\\
. It’s on purpose.
The laravel-
prefix tells you that this package is intended to be used in Laravel projects. If I had a framework-agnostic package (the one that works with any framework), it’s would have no prefix.
Creating the service provider
Create the
src\\HeroiconServiceProvider.php
in the package directory:<?php namespace Osmianski\\Heroicons; use Illuminate\\Support\\ServiceProvider; class HeroiconServiceProvider extends ServiceProvider { }
Make the service provider class auto-discoverable by the Laravel project:
{ ... "extra": { "laravel": { "providers": [ "Osmianski\\\\Heroicons\\\\HeroiconServiceProvider" ] } } }
Run:
composer update
Moving the Blade templates
Move the Balde templates from the
resources/views/components/heroicons
project directory to theresources/views/components
package directory.Register the Blade templates in the
HeroiconServiceProvider::boot()
method:public function boot(): void { $this->loadViewsFrom(__DIR__.'/../resources/views', 'heroicons'); $this->publishes([ __DIR__.'/../resources/views' => resource_path('views/vendor/heroicons'), ]); }
Reference the package Blade templates in the project:
<x-heroicons::outline.bell class="h-6 w-6" />
Here, x-heroicons
matches the namespace specified in the second parameter of the loadViewsFrom()
call.
Useful links
And it works!
For more information on creating Composer packages for Laravel, check these resources:
https://github.com/osmianski/blue-puffin/commit/82601aaaa5633a966ffa12de7de1d4a51026593a
https://laracasts.com/series/building-laracasts/episodes/3?autoplay=true
https://laracasts.com/series/building-laracasts/episodes/4?autoplay=true
https://packages.tools/testbench/getting-started/introduction.html