Skip to content

Open-source Laravel packages

Laravel packages
by Amjad.

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, webhook replay, feature flags, a notification inbox, browser push and Redis health checks. Each one is MIT licensed, installed with one command and driven from PHP.

16
Packages, all MIT
16
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 bound to the signed-in user or guest session and valid for 720 minutes; the 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.2 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
  • Secrets masked in payloads, headers and URLs, and inside exception messages, stack traces and failed_jobs text
  • 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.2 MIT
$ composer require amjadiqbal/laravel-webhook-console
13 Feature flags · MIT

Laravel Pennant Dashboard

amjadiqbal/laravel-pennant-dashboard

laravel/pennant has no UI. The existing ones are a Filament plugin and a Nova tool, so an app on neither has nothing but tinker to see who gets a feature, switch it for one user, or roll it out to a share of users.

An unofficial, independent dashboard for laravel/pennant (not made, endorsed or supported by Laravel). It is plain Blade with forms, so it needs no Filament, Nova or Livewire: install it, run the migration, define a gate and open /pennant.

  • Lists every feature in a Pennant store: closures, class-based features in app/Features, and stale stored features
  • Turn a feature on or off for one user, team, any Eloquent model, Feature::globally() or a raw scope string
  • Turn a feature on or off for everyone with Pennant's own activateForEveryone()
  • Sticky percentage rollouts: each scope gets a fixed bucket, so raising 25% to 50% only adds scopes
Laravel 12 and 13 PHP 8.3+ v1.0.2 MIT
$ composer require amjadiqbal/laravel-pennant-dashboard
14 Notifications · MIT

Laravel Notification Inbox

amjadiqbal/laravel-notification-inbox

Mailpit catches your mail, but push, Slack, Teams, Telegram, Discord and SMS notifications in development are either really sent or not seen at all. And Notification::fake() never calls your toX() methods, so a broken Slack block or a missing chat id still passes your tests.

In the environments you allow, the package cancels each non-mail notification at Laravel's NotificationSending event, builds the message with the channel's own toX() method and route, and stores it for an inbox with look-alike previews. NotificationInbox::fake() does the same in memory and gives your tests assertions on the real payloads.

  • Catches web push, FCM, APNs, Expo, OneSignal, Pushover, Slack, Teams, Telegram, Discord, SMS channels and broadcast; mail and database pass through
  • Look-alike previews: phone push alert, Slack Block Kit, Teams Adaptive Card, Telegram bubble, Discord embed, SMS thread with segment count
  • Builds each message with the real toX() method, so a notification that would fail to send fails here first
  • NotificationInbox::fake() with assertions such as assertCaptured, assertCapturedOn, assertCapturedFor and assertNoCaptureErrors
Laravel 12 and 13 PHP 8.3+ v0.1.3 MIT
$ composer require amjadiqbal/laravel-notification-inbox --dev
15 Notifications · MIT

Laravel WebPush Kit

amjadiqbal/laravel-webpush-kit

laravel-notification-channels/webpush sends web push from Laravel, but everything in the browser is left to you: the service worker, the Push API subscribe and unsubscribe calls, the permission prompt, saving the subscription for the signed-in user, and keeping it alive when the browser rotates it. Ask for permission on page load and many users click Block, and a blocked site cannot ask again.

This package builds on laravel-notification-channels/webpush; it is not made or endorsed by the laravel-notification-channels maintainers. It adds a Blade component that renders a small web component: the page asks first, and only Allow opens the browser's own permission dialog. The subscription is saved and deleted through the channel's own updatePushSubscription() and deletePushSubscription(), and a service worker served from the site root shows the channel's normal and declarative messages. php artisan push:test sends a real push and shows what each push service answered.

  • Blade subscribe button for signed-in users with a two-step prompt: a card you write first, the browser dialog only after Allow
  • States for subscribed (with Turn off), blocked, no push support, no HTTPS, and an Add to Home Screen note for iPhone and iPad (untested)
  • Service worker at /webpush-sw.js that shows WebPushMessage and DeclarativeWebPushMessage pushes and sends the click to data.url or navigate
  • Saves, deletes and rotates subscriptions through the channel's updatePushSubscription() and deletePushSubscription()
Laravel 12 and 13 PHP 8.3+ v0.1.0 MIT
$ composer require amjadiqbal/laravel-webpush-kit
16 Database · MIT

Laravel Redis Advisor

amjadiqbal/laravel-redis-advisor

When a Redis queue shares a server with your cache under maxmemory-policy allkeys-lru, Redis can delete the list that holds your queued jobs. No exception, no failed job, nothing in the log. php artisan cache:clear runs FLUSHDB, so a cache in the same database as your queue empties both. Memory graphs show none of this, because they do not know what your Laravel app keeps where.

The advisor reads your cache, queue, session and Horizon config to learn which Redis connection holds what, then reads INFO, a SCAN sample of keys and a few queue lengths, and reports each problem with its severity, the numbers behind it, and the fix as Redis config or a command and in Laravel terms. It never runs a fix: every command passes one guard that only allows reads.

  • Queue checks: jobs open to an allkeys-* eviction policy, backlog and the oldest job's wait, expired reservations, and Horizon trim settings and its shared default prefix
  • Key checks: queues or sessions that cache:clear would delete, colliding or default key prefixes, keys without a TTL, and big keys
  • Server checks: persistence off under queues or sessions, a failed last save, no maxmemory or a server close to it, fragmentation and swap, connections that do not answer
  • Session check: session TTLs longer than SESSION_LIFETIME, sessions that never expire, and sessions open to eviction
Laravel 12 and 13 PHP 8.3+ v0.1.1 MIT
$ composer require amjadiqbal/laravel-redis-advisor

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