Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Laravel Webpush Kit Laravel Package

amjadiqbal/laravel-webpush-kit

A Laravel package for sending Web Push notifications to browsers, with helpers for managing push subscriptions and VAPID keys. Adds push support to your app without building the underlying flow yourself.

View on GitHub
Deep Wiki
Context7

Getting Started

This is the client-side half of Laravel web push: it pairs with laravel-notification-channels/webpush (which you presumably already use to send notifications) and supplies the pieces the channel deliberately leaves out — a subscribe button, a service worker, routes that persist subscriptions, and a test command.

Install and publish

composer require <vendor>/webpush-kit   # requires PHP 8.3+, Laravel 12/13, channel ^11|^12|^13
php artisan vendor:publish --tag=webpush-kit   # service worker (and manifest/config, if offered)

Then three edits to your layout/views:

{{-- layouts/app.blade.php, inside <head> --}}
<x-webpush-kit::head />
{{-- account settings / notification preferences --}}
<x-webpush-kit::button />

Make sure the channel itself is configured first — VAPID_PUBLIC_KEY, VAPID_PRIVATE_KEY, and VAPID_SUBJECT in .env. push:test will fail loudly if they're missing, which is the fastest sanity check.

Where to look first

  1. php artisan about — confirm the package registered and see its config summary.
  2. php artisan push:test — this is the single highest-value command in the package. It sends a real push and prints what each push service replied, so you learn immediately whether your VAPID setup is valid, whether the browser actually subscribed, and whether an endpoint is dead. Run it before writing any application code.
  3. The published service worker — read it once so you know what's handled (push, notificationclick, pushsubscriptionchange) before you consider customising.

First use case

Put <x-webpush-kit::button /> on your notification-settings page, click through the two-step prompt in Chrome, then run php artisan push:test. If a notification arrives, you're done with integration and can move straight to wiring an existing notification class's toWebPush() into your real feature.

Implementation Patterns

Respect the two-step prompt. The button asks on your page first, and only opens the browser's permission dialog after the user clicks Allow. Don't build your own "Enable notifications" CTA that calls Notification.requestPermission() directly — you'll lose the blocked/unsupported/insecure/iPhone state handling and burn the one-shot permission prompt on users who weren't ready.

Treat the button as the only subscribe entry point, but drive surrounding UI from the browser events it fires:

<div x-data="{ subscribed: false }"
     @webpush-kit:subscribed.window="subscribed = true"
     @webpush-kit:unsubscribed.window="subscribed = false">
    <x-webpush-kit::button />
    <p x-show="subscribed">You'll get push notifications on this device.</p>
</div>

Because these are window events on document, they survive Livewire re-renders and wire:navigate navigation without you re-registering listeners — which is exactly the scenario the release notes tested.

Livewire components. The button works inside a Livewire component, but keep the state that matters (a push_enabled boolean on the user, notification preferences) server-side and let the browser events be the trigger to persist it. Don't try to read subscription state from Alpine as the source of truth; the browser is authoritative for permission, your DB is authoritative for intent.

Rotate, don't duplicate. The package ships routes that save, delete and rotate subscriptions through the channel's updatePushSubscription() / deletePushSubscription(). Let those handle endpoint churn — including pushsubscriptionchange re-subscription from the service worker. If you've hand-rolled subscription saving from an older integration, delete it; two code paths writing the same push_subscriptions rows is how you get duplicate pushes to the same device.

Use the test command as a diagnostic, not just a smoke test. push:test surfaces dead subscriptions: a real 404 (Mozilla) or 410 (Google) response means the channel removed that row. If a user reports "I stopped getting notifications," run push:test before touching your notification classes — you'll usually find a stale endpoint that was already cleaned up, and the fix is asking them to re-subscribe via the button.

Declarative push. The service worker renders both WebPushMessage and DeclarativeWebPushMessage. If you're on the declarative path, the manifest and <x-webpush-kit::head /> become load-bearing rather than optional — the notification is constructed from data in the push payload, so the title/body/icon you set in toWebPush() is what shows; there's no service-worker code of yours in between to fix it up.

Importing into an existing service worker. The service worker is publishable or importable. If your app already has a service worker (offline caching, background sync), import rather than replace — but verify you haven't clobbered push, notificationclick and pushsubscriptionchange handlers, and check the resulting scope. A service worker served from a subpath only controls that subpath.

Gotchas and Tips

Click handling is untested in this release. The release notes are explicit: the click target (data.url / navigate) is set and verified inside Chrome, but the notification banner never appeared on the test machine, so the click itself was never exercised. Test this yourself early — a subscribe flow that works but a notification that opens the wrong URL is a bad first impression. Verify: click opens the right page, an already-open tab is focused rather than duplicated, and navigate behaves sanely when the SW scope doesn't cover the target.

Firefox and Safari are untested. Only Chrome against Google's push service was exercised end to end. Budget time for a manual pass on Firefox, macOS Safari, and iOS/iPadOS Home Screen apps before shipping to a broad audience. Assume nothing.

iOS/iPadOS Safari only works from a Home Screen app. This isn't a package limitation, it's WebKit: no Home Screen install, no push. The button handles the state and the manifest/head component exist to support it — but the user has to actually use Add to Home Screen, and the prompt is buried in the Share sheet. Your onboarding copy needs to say so explicitly; the button alone won't get them there.

Blocked is a one-way door in the UI. If a user dismisses the browser permission dialog, the package will show the blocked state — but nothing in JS can un-block it. Only the user, via site settings, can. Provide a support snippet telling them where that setting lives per browser; expect this to be your most common "push doesn't work" ticket.

VAPID_SUBJECT must be a valid mailto: or https: URL. Safari rejects a malformed subject and the failure mode is opaque. push:test warns about this specifically — heed the warning rather than assuming pushes are fine because Chrome accepted them.

Secure context is mandatory. Push requires HTTPS; localhost is the exception. Testing on http://192.168.x.x from a phone will show the unsupported/insecure state and you'll waste an hour blaming the package. Use a tunnel or valet share-style HTTPS for device testing.

Service worker caching will bite you during development. Chrome holds onto old service workers aggressively. Use DevTools → Application → Service Workers → Update on reload, and hard-reload after republishing the SW. A stale SW that lacks pushsubscriptionchange looks exactly like "rotation is broken."

pushsubscriptionchange re-subscription can silently fail if permission was revoked between the change and the re-subscribe attempt. The SW will try; the server won't get a new row; the user gets nothing. Handle this by prompting re-subscription when you notice a user who previously had a subscription now has none.

Dead subscriptions are removed automatically on 404/410. Don't build your own cleanup job for those codes — the channel does it. Do build one for the subscriptions that simply never get a 410 (users who cleared storage); prune on last_seen/updated_at staleness instead.

Pin the channel version deliberately. The package supports channel 11, 12 and 13, and the oldest supported version is in CI — so ^11 is genuinely tested, not merely declared. If you're mid-upgrade on the channel, you don't have to jump to 13 to adopt this.

Extension points to prefer: publish-then-edit the service worker rather than forking the package; listen to the browser events rather than patching the button's internals; use the shipped routes rather than adding your own subscription endpoints. There's very little here you should need to override, and every override is something you'll re-apply on the next release — which matters, since this is 0.1.0 and the API surface is not yet promised stable.

Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
uni-method/symfony-lib
tacticmedia/scim-server-bundle
neika-ai/symfony-bundle
mattleads/agent-publishing-bundle
glavweb/framework-bundle
glavweb/extra-bundle
glavweb/api-bundle
glavweb/admin-bundle
adachsoft/api-version-bundle
elsayed85/laravel-api-exceptions
ouhssini/laravel-demo-mode
daworks/ncloud-outbound-mailer-driver
gp247/cms
ignibyte/aic-gates
nexa-merchant/blog
novasa/laravel-language-center
opencage/geocode
adolfoholzer/parameters
livenetworks/ln-starter
shitoudev/permissionmanager