Skip to content

Open-source Laravel packages

Laravel packages
by Amjad Iqbal.

Small, focused Composer packages for the jobs every Laravel app ends up needing: product tours, toasts, dialogs, a command palette, data tables, sliders and private media viewing, plus developer tools for local package work, faster test databases, OPcache on deploy, MySQL health checks and webhook replay. Each one is MIT licensed, installed with one command and driven from PHP.

12
Packages, all MIT
12
Support Laravel 13
01 Onboarding · MIT

Laravel Driver.js

amjadiqbal/laravel-driver-js

Adding Driver.js to a Laravel app usually means hand-writing a script for each page, remembering who has already seen the tour, and wiring it to Livewire yourself.

You define tours and steps in PHP, put <x-driver-js> in the view, and start a tour from a button, Alpine, Livewire or JavaScript. once() shows a tour only the first time a visitor sees it, with no JavaScript to write and no npm build step.

  • Tours and steps built in PHP with Driver.js's own option names
  • Invalid sides, alignments, buttons and hook names rejected before they reach the browser
  • once() auto-starts a tour only the first time a visitor sees it
  • Start a tour from a button, Alpine, a Livewire component or JavaScript
Laravel 12 and 13 PHP 8.3+ v1.0.0 MIT
$ composer require amjadiqbal/laravel-driver-js
02 Notifications · MIT

Laravel Sonner

amjadiqbal/laravel-sonner

Sonner is the toast library behind shadcn/ui, but it is a React component, so a Blade or Livewire app cannot use it directly.

Call toast() in a controller or Livewire action and put <x-sonner /> in your layout. A plain JavaScript port of Sonner's Toaster shows the toasts with Sonner's own stylesheet, with no npm install and no build step.

  • toast() helper and Sonner facade that flash toasts to the session
  • Sonner's stacking, animations, swipe-to-dismiss and themes
  • Toasts from Livewire actions appear in the same response
  • Browser API with toast(), toast.success(), toast.promise(), toast.loading() and toast.dismiss()
Laravel 12 and 13 PHP 8.3+ v1.0.0 MIT
$ composer require amjadiqbal/laravel-sonner
03 Command palette · MIT

Laravel Ninja Keys

amjadiqbal/laravel-ninja-keys

Adding the ninja-keys command palette to a Laravel app by hand means writing a script per layout, building the action list in JavaScript, and tracking which actions each user is allowed to see.

You register actions in PHP, hide the ones a user may not use with your Gate abilities, and render the palette with <x-ninja-keys />. ninja-keys 1.2.2 is bundled and self-hosted, so there is no JavaScript to write and no npm build step.

  • Actions built in PHP, with route() and href() for navigation
  • ->can() runs Gate abilities on the server, so hidden actions never reach the browser
  • define() callbacks run at render time with the current user and request
  • Submenus from parent and children ids, or from nested arrays
Laravel 12 and 13 PHP 8.3+ v1.0.0 MIT
$ composer require amjadiqbal/laravel-ninja-keys
04 Dialogs · MIT

Laravel SweetAlert2

amjadiqbal/laravel-sweetalert2

Showing a popup after a controller redirects, or asking for confirmation before a delete button runs, usually means writing your own JavaScript and passing data through the session by hand.

Queue popups from a controller and they show on the next page, even after more than one redirect. A data-swal-confirm attribute puts a confirm dialog in front of any link, button or form, and Livewire and Alpine can show popups without a page load.

  • Flash one or several popups from a controller; they show in order on the next page
  • Popups survive more than one redirect
  • data-swal-confirm on links, buttons, forms, wire:click and wire:submit
  • $this->swal() in Livewire and a sweetalert2 event in Alpine, with wire:navigate support
Laravel 12 and 13 PHP 8.3+ v1.0.0 MIT
$ composer require amjadiqbal/laravel-sweetalert2
05 Data tables · MIT

Laravel Grid.js

amjadiqbal/laravel-gridjs

A data table over a large Eloquent query needs search, sort and pagination on the server, plus a data endpoint that must not let the browser choose which model or columns to read. Writing that route, controller and JavaScript for every table is repetitive and easy to get wrong.

You build the grid from your own query in PHP and echo it in Blade. Grid.js fetches one page at a time from a built-in endpoint that only runs the query and columns sealed in an encrypted token, with no route, controller or JavaScript to write.

  • Keeps your query: where clauses, scopes, joins, eager loads and default order
  • Search (LIKE, relation columns included), sort and LIMIT/OFFSET pagination run in the database
  • Encrypted grid token, so the data endpoint never takes a model or column name from the browser
  • Built-in date, datetime, number, currency, boolean and link formats, all escaped
Laravel 12 and 13 PHP 8.3+ v1.0.0 MIT
$ composer require amjadiqbal/laravel-gridjs
07 Testing · MIT

Refresher

amjadiqbal/refresher

RefreshDatabase runs migrate:fresh, plus your seeders if you seed, once in every test process: every single test you run while you work, every --parallel worker and every CI job. On a real app that is hundreds of CREATE TABLE statements before the first assertion.

The first test process migrates and seeds as usual and saves the database as a snapshot. Every process after that restores the snapshot, until a migration, seeder or factory changes. Your tests, factories and assertions do not change.

  • Drop-in traits for RefreshDatabase and LazilyRefreshDatabase, for PHPUnit and Pest
  • Seeders run once and the seeded database is what gets saved
  • Snapshot key covers migrations (including package paths), seeders and factories, the driver and the migrate:fresh options
  • The key uses file names and contents, so a snapshot cached in CI matches on every runner
Laravel 12 and 13 PHP 8.3+ v1.1.0 MIT
$ composer require amjadiqbal/refresher --dev
08 Deployment · MIT

Kiln

amjadiqbal/kiln

With opcache.validate_timestamps=0, the recommended production setting, a Laravel app keeps serving stale bytecode after a deploy until the process pool restarts. Resetting OPcache from php artisan does not fix it, because the CLI and the FPM worker pool do not share the same OPcache.

Kiln adds kiln:clear, kiln:warm, kiln:status and kiln:config, plus an optional route that runs the clear or warm inside an FPM worker. The route only accepts temporary signed URLs, and clear and warm are POST-only.

  • Four Artisan commands: kiln:clear, kiln:warm, kiln:status and kiln:config
  • kiln:warm compiles files with opcache_compile_file(), which does not run their top-level code
  • Optional HTTP route that clears or warms OPcache inside an FPM worker
  • The route refuses any signed URL without an expiry, even if the signature is valid
Laravel 12 and 13 PHP 8.3+ v1.0.0 MIT
$ composer require amjadiqbal/kiln
09 Media viewer · MIT

Laravel Media Viewer

amjadiqbal/laravel-media-viewer

Showing an uploaded file in Laravel usually means a compromise. storage:link makes the file public to anyone with the URL, a plain download route breaks video seeking and makes large PDFs load in full, and the browser's own PDF viewer looks and behaves differently in every browser.

Files stay on a private disk. Blade and Livewire components render a pdf.js viewer and a Video.js player, and each file is served through a signed URL that expires, answers Range requests and, when someone is signed in, only works for that user.

  • pdf.js viewer with page navigation, zoom, text search, full screen and an optional download button
  • Lazy PDF thumbnails: through Range requests the browser fetches only what each page needs
  • Video.js player for video and audio, with seeking over Range requests
  • HLS from a private disk: playlists are rewritten so every segment and key is a signed URL
Laravel 12 and 13 PHP 8.3+ v0.1.1 MIT
$ composer require amjadiqbal/laravel-media-viewer
10 Carousels · MIT

Laravel Swiper

amjadiqbal/laravel-swiper

Dropping Swiper into a Laravel app is quick, but a Livewire update morphs its markup back to the server HTML: arrows double up, the slide jumps back to the start, and after wire:navigate it never starts. The images also come from storage or models, not a hard-coded array, and carousel accessibility is easy to get wrong.

Navigation, pagination, thumbs, autoplay and effects are typed props on <x-swiper>. Slides come from your own markup, a collection of models or Spatie Media items, or a folder on a storage disk, and a carousel is rebuilt only when its content changed, on the same slide.

  • Typed, validated props for navigation, pagination, thumbs, autoplay, effects and breakpoints
  • Unchanged carousels survive Livewire updates; changed ones are rebuilt on the same slide
  • Starts again after wire:navigate, with no duplicate scripts
  • Image slides from paths, URLs, Eloquent models, Spatie Media items or a storage disk folder, private disks included
Laravel 12 and 13 PHP 8.3+ v1.0.1 MIT
$ composer require amjadiqbal/laravel-swiper
11 Database · MIT

Laravel MySQL Advisor

amjadiqbal/laravel-mysql-advisor

MySQL already records which indexes are never read, which statements scan whole tables and which tiny query runs thousands of times, but nothing in a Laravel app reads that for you. db:show lists sizes and db:monitor counts connections; neither tells you which index to drop or which relation to eager-load.

The advisor reads information_schema, performance_schema and server status, and reports each problem with its severity, the numbers behind it and, where one applies, the fix as SQL and as a Laravel migration or Eloquent snippet. It never runs a fix: every statement it sends is a read.

  • Index checks: unused, duplicate and left-prefix indexes, and *_id columns or foreign keys with no index
  • Query checks from performance_schema: full table scans, top statements by total time, and N+1 suspects
  • Table and server checks: missing primary keys, large and fragmented tables, stale statistics, buffer pool, disk temp tables and connection headroom
  • Each fix shown as SQL and as a Laravel migration; unused indexes are hidden first (INVISIBLE on MySQL, IGNORED on MariaDB)
Laravel 12 and 13 PHP 8.3+ v0.1.0 MIT
$ composer require amjadiqbal/laravel-mysql-advisor
12 Webhooks · MIT

Laravel Webhook Console

amjadiqbal/laravel-webhook-console

spatie/laravel-webhook-client stores every webhook your app accepts in the webhook_calls table, but has no screen to look at them. When your process job throws on a real queue worker, the call keeps no exception and the failure only exists in failed_jobs, with nothing that points back to the webhook.

An unofficial, independent dashboard for spatie/laravel-webhook-client (not made or endorsed by Spatie). It lists every stored call with its status, links failed_jobs rows back to their calls, shows payloads and headers with secrets masked, and replays one call or every failure from the dashboard or artisan.

  • List and filter stored calls by config, status, date range, or a search inside the payload and URL
  • Failures include process jobs that failed on a queue worker, linked from failed_jobs without unserializing them
  • Payload keys, header names and URL parameters such as password, secret, token or signature are shown masked
  • Replay one call, the selected calls, or every failure: the call's process_webhook_job is dispatched again
Laravel 12 and 13 PHP 8.3+ v1.0.0 MIT
$ composer require amjadiqbal/laravel-webhook-console

Laravel development

Need one of these built into your app, or something custom?

Integration, a custom package, or a whole Laravel feature, built and tested the same way as these. Tell me what you need and get a plan and a quote, free.

Need something built?

Free quote in 60 seconds