npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@slfomin/pwa-laravel

v0.1.0

Published

Vite plugin and client helpers for slfomin/pwa-laravel

Downloads

51

Readme

pwa-laravel

Latest Version on Packagist GitHub Tests Action Status GitHub Code Style Action Status PHPStan Total Downloads

Full PWA integration for Laravel 13 + Vite 7 with optional Inertia.js v3 support.

  • Static or dynamic Web App Manifest (per-locale, per-tenant via resolvers)
  • Service Worker registration via Blade directives
  • Automatic icon generation (standard, maskable, apple-touch, favicon) via intervention/image
  • Inertia v3 adapter: shared PWA props, middleware for correct SW/Inertia caching
  • JS companion package @slfomin/pwa-laravel wrapping vite-plugin-pwa
  • PHPStan level 8, PHP 8.4+

Requirements

| Dependency | Version | |---|---| | PHP | ^8.4 | | Laravel | ^13.0 | | Vite | ^6.0 || ^7.0 | | vite-plugin-pwa | ^1.0 | | Inertia.js (optional) | ^3.0 |

Installation

ddev composer require slfomin/pwa-laravel

Run the interactive installer:

ddev artisan pwa:install

This publishes config/pwa.php and prints the next steps.

Quick Start

1. Place your source icon

Put a square PNG (512×512 or larger) at resources/images/pwa-icon.png, then generate all sizes:

ddev artisan pwa:generate-icons

Icons are written to public/icons/ and cover standard, maskable, Apple Touch, and favicon sizes.

2. Add Blade directives to your layout

<!DOCTYPE html>
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    @pwaMeta
    @vite(['resources/css/app.css', 'resources/js/app.js'])
</head>
<body>
    {{ $slot }}
    @pwaRegisterSW
</body>
</html>

@pwaMeta renders the <link rel="manifest">, theme-color, apple-touch-icon, favicons, and other mobile meta tags.

@pwaRegisterSW injects a small inline script that registers the service worker on page load.

3. Install the JS companion

Via npm (recommended):

ddev npm install -D vite-plugin-pwa @slfomin/pwa-laravel

From the composer package (no npm required):

Pre-built files are shipped inside the composer package at vendor/slfomin/pwa-laravel/dist/. Import directly in vite.config.js:

import { laravelPwa } from '../../vendor/slfomin/pwa-laravel/dist/index.js';
// framework composables (optional):
// import { usePwa } from '../../vendor/slfomin/pwa-laravel/dist/vue.js';

vite-plugin-pwa is still a required peer dependency — install it with ddev npm install -D vite-plugin-pwa.

4. Configure Vite

// vite.config.js
import { defineConfig } from 'vite';
import laravel from 'laravel-vite-plugin';
import { laravelPwa } from '@slfomin/pwa-laravel';

export default defineConfig({
    plugins: [
        laravel({
            input: ['resources/css/app.css', 'resources/js/app.js'],
            refresh: true,
        }),
        laravelPwa({
            strategies: 'generateSW',
            manifest: false, // manifest is served by Laravel, not Vite
            workbox: {
                globPatterns: ['**/*.{js,css,html,ico,png,svg,webp,woff,woff2}'],
            },
        }),
    ],
});

5. Build

ddev npm run build

That's it — your Laravel app is now a PWA.


Inertia.js v3 Integration

Vite config

import { defineConfig } from 'vite';
import laravel from 'laravel-vite-plugin';
import inertia from '@inertiajs/vite';
import { laravelPwa } from '@slfomin/pwa-laravel';

export default defineConfig({
    plugins: [
        laravel({ input: ['resources/css/app.css', 'resources/js/app.js'], refresh: true }),
        inertia(),
        laravelPwa({
            inertia: true,          // sets navigateFallback: '/' and excludes API routes
            strategies: 'generateSW',
            manifest: false,
            devOptions: { enabled: false }, // must be off when inertia() plugin is active
        }),
    ],
});

Shared props

The package automatically calls Inertia::share('pwa', ...) if Inertia is installed and pwa.inertia.auto_detect is true (default). Every page receives:

{
    pwa: {
        manifest_url: string,
        sw: { url, scope, register_type, auto_register, available },
        navigate_fallback: string | null,
        is_ssr: boolean,
    }
}

usePwa() — Vue 3

import { usePwa } from '@slfomin/pwa-laravel/vue';

const { manifestUrl, swInfo, navigateFallback, isOffline } = usePwa();

usePwa() — React 19

import { usePwa } from '@slfomin/pwa-laravel/react';

const { manifestUrl, swInfo, navigateFallback, isOffline } = usePwa();

Middleware

Add pwa.inertia to your routes to set correct Vary and Cache-Control headers, preventing the service worker from caching Inertia XHR responses:

// routes/web.php
Route::middleware(['web', 'pwa.inertia'])->group(function () {
    // your Inertia routes
});

Dynamic Manifest

Switch the driver to dynamic to serve a manifest generated by Laravel on each request:

// config/pwa.php
'manifest' => [
    'driver' => 'dynamic',
    'dynamic' => [
        'resolver' => \App\Pwa\TenantManifestResolver::class,
        'cache' => true,
        'cache_ttl' => 3600,
    ],
],

Implement your resolver:

namespace App\Pwa;

use Illuminate\Http\Request;
use SlFomin\PwaLaravel\Contracts\ManifestResolver;
use SlFomin\PwaLaravel\Manifest\ManifestBuilder;

final class TenantManifestResolver implements ManifestResolver
{
    public function resolve(Request $request, ManifestBuilder $default): ManifestBuilder
    {
        $tenant = $request->user()?->tenant;

        return $tenant
            ? $default->name($tenant->name)->themeColor($tenant->brand_color)
            : $default;
    }

    public function cacheKey(Request $request): ?string
    {
        return 'tenant.'.($request->user()?->tenant_id ?? 'guest');
    }
}

Bind it in AppServiceProvider:

$this->app->bind(
    \SlFomin\PwaLaravel\Contracts\ManifestResolver::class,
    \App\Pwa\TenantManifestResolver::class,
);

Blade Directives

| Directive | Output | |---|---| | @pwaMeta | <link rel="manifest">, theme-color, apple-touch-icon, favicons, mobile meta | | @pwaRegisterSW | Inline <script> registering the service worker | | @pwaInstallButton('Install') | A hidden <button> that appears when the browser's install prompt fires |


Artisan Commands

| Command | Description | |---|---| | pwa:install | Interactive installer — publishes config and prints next steps | | pwa:generate-icons | Generate full icon set from source PNG | | pwa:publish-manifest | Write manifest.webmanifest from config (no Vite build required) |

pwa:generate-icons

pwa:generate-icons [source] [--output=] [--dry-run]

  source     Path to source PNG (≥512×512, square). Default: pwa.icons.source from config.
  --output   Output directory. Default: pwa.icons.output_path from config.
  --dry-run  Validate source without writing any files.

pwa:publish-manifest

pwa:publish-manifest [--path=] [--pretty]

  --path    Output file path. Default: pwa.manifest.static_path from config.
  --pretty  Pretty-print the JSON output.

Configuration Reference

All options live in config/pwa.php. Key .env variables:

| Variable | Default | Description | |---|---|---| | PWA_MANIFEST_DRIVER | static | static (Vite file) or dynamic (Laravel controller) | | PWA_MANIFEST_ROUTE | /manifest.webmanifest | URL the browser fetches | | APP_NAME | Laravel | PWA full name | | PWA_SHORT_NAME | APP_NAME | Short name for the home screen icon | | PWA_DESCRIPTION | `` | PWA description | | PWA_DISPLAY | standalone | Display mode (standalone, fullscreen, minimal-ui, browser) | | PWA_THEME_COLOR | #000000 | Theme / status-bar color | | PWA_BG_COLOR | #ffffff | Splash screen background color | | PWA_SW_STRATEGY | generateSW | generateSW or injectManifest | | PWA_SW_URL | /sw.js | Service worker registration URL | | PWA_SW_DEV | false | Enable service worker in local dev | | PWA_MANIFEST_CACHE | true | Cache dynamic manifest responses | | PWA_MANIFEST_CACHE_TTL | 3600 | Cache TTL in seconds |


Facade & Contracts

use SlFomin\PwaLaravel\Facades\Pwa;

Pwa::manifest();         // ManifestBuilder for the current request
Pwa::manifestUrl();      // string — URL of the manifest
Pwa::serviceWorkerUrl(); // string — URL of the service worker
Pwa::worker();           // WorkerManager instance
Pwa::driver();           // ManifestDriver instance

Extension points in SlFomin\PwaLaravel\Contracts:

| Contract | Purpose | |---|---| | ManifestDriver | Custom manifest delivery strategy | | ManifestResolver | Context-aware manifests (tenant, locale, user role) | | IconGenerator | Replace intervention/image with another library | | ServiceWorkerStrategy | Extend Vite plugin options |

Events

Hook into the PWA lifecycle with five Laravel events (ManifestResolving, ManifestResolved, ServiceWorkerRequested, IconsGenerated, ManifestPublished):

use SlFomin\PwaLaravel\Events\PwaEvents;
use SlFomin\PwaLaravel\Events\ManifestResolved;

PwaEvents::manifestResolved(function (ManifestResolved $event): void {
    // last-chance mutation before the JSON is serialized
    $event->manifest->name('My App — '.app()->getLocale());
});

See docs/events.md for the full reference.


Testing

ddev composer test      # Pest (119 tests)
ddev composer analyse   # PHPStan level 8
ddev composer format    # Laravel Pint
ddev composer ci        # all three in sequence

Changelog

Please see CHANGELOG for recent changes.

Contributing

Please see CONTRIBUTING for details.

Security

Please review our security policy to report vulnerabilities.

Credits

License

MIT. Please see License File for more information.