Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
composer require spomky-labs/pwa-bundlepwa: ~<!DOCTYPE html>
<html lang="en">
<head>
{{ pwa() }}
</head>
<body>
...
</body>
</html><!DOCTYPE html>
<html lang="en">
<head>
...
<link rel="manifest" href="/manifest.json">
</head>
<body>
...
</body>
</html>
{
"name": "My Progressive Web App",
"short_name": "PWA",
"description": "An example Progressive Web App",
"start_url": "/index.html",
"display": "standalone",
"background_color": "#ffffff",
"theme_color": "#4285f4",
"icons": [
{
"src": "/icons/icon-192x192.png",
"sizes": "192x192",
"type": "image/png"
},
{
"src": "/icons/icon-512x512.png",
"sizes": "512x512",
"type": "image/png"
}
]
}self.addEventListener('install', (event) => {
event.waitUntil(
caches.open('my-cache').then((cache) => {
return cache.addAll([
'/',
'/index.html',
'/styles/main.css',
'/scripts/main.js',
'/images/logo.png'
]);
})
);
});
self.addEventListener('fetch', (event) => {
event.respondWith(
caches.match(event.request).then((response) => {
return response || fetch(event.request);
})
);
});<!DOCTYPE html>
<html lang="en">
<head>
<script defer>
if ('serviceWorker' in navigator) {
navigator.serviceWorker.register('/service-worker.js')
.then(function(registration) {
console.log('Service Worker registered with scope:', registration.scope);
})
.catch(function(error) {
console.error('Service Worker registration failed:', error);
});
}
</script>
</head>
<body>
...
</body>
</html>








<!DOCTYPE html>
<html lang="en">
<head>
{{ pwa(swAttributes= {nonce: csp_nonce('script')}) }}
</head>
<body>
...
</body>
</html>pwa:
manifest:
enabled: true
description: "This application helps you donate to worthy causes."pwa:
serviceworker:
enabled: true
src: "sw.js"
workbox:
offline_fallback:
page: 'app_offline_page'
image: 'images/offline.svg'
font: 'fonts/normal.ttf'/\.(css|js|json|xml|txt|map|ico|png|jpe?g|gif|svg|webp|bmp)$/pwa:
serviceworker:
enabled: true
src: "sw.js"
workbox:
asset_cache:
enabled: true
regex: '/\.(css|jsx?)$/'pwa:
manifest:
enabled: true
file_handlers:
- action: "/open"
accept:
- "image/png": [".png"]
- "image/jpeg": [".jpg", ".jpeg"]pwa:
manifest:
enabled: true
id: "/?homescreen=1"pwa:
manifest:
enabled: true
orientation: "landscape"symfony console asset-map:compilepwa:
serviceworker:
enabled: true
workbox:
clear_cache: false # Default to truepwa:
manifest:
enabled: true
edge_side_panel: ~pwa:
manifest:
enabled: true
display: "browser"
edge_side_panel:
preferred_width: 400pwa:
manifest:
enabled: true
name: "app.name"
short_name: "app.short_name"
start_url: "/index.html"
displa": "standalone"
background_color: "#ffffff"
theme_color: "#4285f4"
shortcuts:
- name: "app.feature1.shorcut.name"
short_name: "app.feature1.shorcut.short_name"
description: "app.feature1.shorcut.description"
url: "/start-chat"
icons":
- src: "icons/feature1-96x96.png"
sizes: [96]pwa:
manifest:
enabled: true
iarc_rating_id: "e.g. IARC certificate code"self.addEventListener('push', async function (event) {
if (!(self.Notification && self.Notification.permission === 'granted')) {
return;
}
const {title, options}= event.data.json();
event.waitUntil(self.registration.showNotification(title, options));
});/\.(ico|png|jpe?g|gif|svg|webp|bmp)$/pwa:
serviceworker:
enabled: true
src: "sw.js"
workbox:
image_cache:
enabled: true
max_age: 30 days
max_entries: 200
regex: '/\.(png|jpe?g|svg|webp)$/'<html dir="ltr" lang="en">
</html>pwa:
manifest:
enabled: true
dir: "ltr"
lang: "en"pwa:
serviceworker: "sw.js"pwa:
manifest:
prefer_related_applications: true
related_applications:
- platform: "play"
url: "https://play.google.com/store/apps/details?id=com.example.app1"
id: "com.example.app1"
- platform: "itunes"
url: "https://itunes.apple.com/app/example-app1/id123456789"
- platform: "windows"
url: "https://apps.microsoft.com/store/detail/example-app1/id123456789"pwa:
manifest:
enabled: true
protocol_handlers:
- protocol: "mailto"
url: "/compose?to=%s"
title: "Compose Email"
- protocol: "web+custom"
url:
path: "app_feature1"
params:
foo: "bar"
title: "Open with Feature #1"pwa:
serviceworker:
enabled: true
src: "sw.js"
workbox:
use_cdn: true
version: 6.5.4<link rel="prefetch" href="/article-2">
<link rel="prefetch" href="/article-3">
<link rel="prefetch" href="/author-18">pwa:
serviceworker:
workbox:
cache_manifest: truepwa:
manifest:
enabled: true
launch_handler:
client_mode: "focus-existing"pwa:
serviceworker:
enabled: true
src: "sw.js"pwa:
serviceworker:
enabled: true
src: "sw.js"
dest: "/foo/service-worker.js"symfony console importmap:require workbox-windowpwa:
serviceworker:
enabled: true
src: "sw.js"
dest: "/foo/service-worker.js"
scope: "/"
use_cache: true
skip_waiting: falsepwa:
manifest:
enabled: true
name: "My PWA"
short_name: "PWA"
start_url: "/index.html"
display: "standalone"
background_color: "#ffffff"
theme_color: "#4285f4"pwa:
manifest:
enabled: true
categories: ['games', 'kids']<section {{ stimulus_controller('@pwa/prefetch-on-demand') }}>
<main>
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.
Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur.
Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
</main>
<aside {{ stimulus_action('@pwa/prefetch-on-demand', 'mouseover', 'prefetch', {urls: ['/author-18', '/article-2', '/article-3']}) }}>
Author: <a href="/author-18">John Doe</a>
Other articles: <a href="/article-2">How to Foo</a>
Other articles: <a href="/article-3">How to Bar</a>
</aside>
</section><div class="mx-auto max-w-screen-xl text-center px-4" {{ stimulus_controller('@pwa/connection-status') }}>
<div
{{ stimulus_target('@pwa/connection-status', 'attribute') }}
class="flex items-center p-4 mb-4 text-sm border rounded-lg online:text-green-800 online:bg-green-50 online:dark:bg-gray-800 online:dark:text-green-400 offline:text-yellow-800 offline:bg-yellow-50 offline:dark:bg-gray-800 offline:dark:text-yellow-300"
role="alert"
>
<div>
<span class="font-medium">
Connection status
</span>:
<span {{ stimulus_target('@pwa/connection-status', 'message') }}>
We are trying to guess what is the current status of your Internet connection
</span>
</div>
</div>
</div>pwa:
serviceworker:
enabled: true
src: "sw.js"
workbox:
enabled: false/\.(ttf|eot|otf|woff2)$/pwa:
serviceworker:
enabled: true
src: "sw.js"
workbox:
font_cache:
enabled: true
max_entries: 10
max_age: 30 dayssymfony console pwa:create:screenshot https://example.compwa:
image_processor: 'pwa.image_processor.imagick'symfony console pwa:create:icons /path/to/the/image.pngpwa:
manifest:
icons:
- src: icons/icon-16x16.jpg
sizes:
- 16
type: image/png
- ...symfony console pwa:create:icons /path/to/the/image.png --format="jpg" 48 96 256symfony console pwa:create:icons /path/to/the/image.png --output="/foo/bar"pwa:
serviceworker:
enabled: true
src: "sw.js"
workbox:
google_fonts:
enabled: true
cache_prefix: 'goolge-fonts'
max_entries: 20
max_age: 1 daycomposer require --dev symfony/panther
composer require --dev dbrekelmans/bdi && vendor/bin/bdi detect driverspwa:
manifest:
screenshots:
- src: screenshots/screenshot-1200x1100.png
width: 1200
height: 1100
type: image/png
- ...symfony console pwa:create:screenshot https://example.com --format="webp" --width=750 --height=1334symfony console pwa:create:screenshot https://example.com --output="/foo/bar"POST request is ideally replied with an HTTP 303 See Other redirect to avoid multiple POST requests from being submitted if a page refresh was initiated by the user, for example.pwa:
manifest:
enabled: true
share_target:
action: "/share-target"
method: "POST"
enctype: "multipart/form-data"
params:
files:
- name: "file",
accept: ["image/*"]pwa:
serviceworker:
enabled: true
src: "sw.js"
workbox:
background_sync:
- queue_name: 'api'
match_callback: startsWith: /api/ # All requests starting with /api/
method: POST
max_retention_time: 7_200 # 5 days in minutes
force_sync_fallback: true #Optional
broadcast_channel: 'api-list'
- queue_name: 'contact'
regex: /\/contact-form\// # All requests starting with /contact-form/
method: POST
max_retention_time: 120 # 2 hours in minutes


pwa:
manifest:
enabled: true
screenshots:
- "images/screenshot-feature1.png"
-
src: "images/screenshot-feature2.png"
platform: "android"
type: "image/png"
-
src: "images/screenshot-feature3.png"
label: "Feature #3 in action"
form_factor: "narrow"label: "Main dashboard view"platform: "android"form_factor: "narrow"pwa:
manifest:
enabled: true
shortcuts:
- name: "Start New Conversation"
short_name: "New Chat"
description: "Create a new conversation."
url: "/start-chat"
icons":
- src: "icons/feature1-96x96.png"
sizes: [96]
- name: "View Unread Messages"
short_name: "Unread"
description: "View unread messages."
url: "/unread-messages"
icons:
- src: "icons/feature2-96x96.png"
sizes: [96]pwa:
manifest:
shortcuts:
- name: "Feature #1"
short_name: "feature-1"
url: "app_feature1" # route name
- name: "Feature #2"
short_name: "feature-2"
url:
path: "app_feature2" # route name
params: # route parameters
key: "value2"
- name: "Feature #3"
short_name: "feature-3"
url: "/feature/3" # relative URL
- name: "Feature #4"
short_name: "feature-4"
url: "https://foo.com/bar/feature-4" # absolute URL
- name: "Feature #5"
short_name: "feature-5"
url:
path: "app_feature5" # route name
path_type_reference: 3 # Network URL
pwa:
serviceworker:
enabled: true
src: "sw.js"
workbox:
resource_caches:
- match_callback: 'startsWith: /pages/'
cache_name: 'static-pages'
strategy: 'NetworkFirst'
network_timeout: 2 # Wait only 2 seconds (only when strategy is networkFirst or NetworkOnly)
preload_urls: # List of URLs to preload. The URLs shall match the value in match_callback option
- 'page_tos'
- 'page_legal'
- match_callback: 'regex: \/articles\/.*$'
cache_name: 'articles'
strategy: 'StaleWhileRevalidate'
broadcast: true # Broadcast changes only when strategy = staleWhileRevalidate
preload_urls: # List of URLs to precache. The URL shall be comprised within the regex
- 'app_articles'
- path: 'app_top_articles'
params:
display: 5<?php
declare(strict_types=1);
use SpomkyLabs\PwaBundle\MatchCallbackHandler\MatchCallbackHandler;
namespace Acme\MatchCallbackHandler;
final readonly class MyVideosMatchCallbackHandler implements MatchCallbackHandler
{
public function supports(string $matchCallback): bool
{
return $matchCallback === 'my-videos';
}
public function handle(string $matchCallback): string
{
return sprintf("({url, request}) => (url.origin === 'videos.s3.storage.com' && request.destination === 'video');
}
}navigator.serviceWorker.addEventListener('message', async event => {
if (event.data.meta === 'workbox-broadcast-update') {
const {updatedURL} = event.data.payload;
if (updatedURL === window.location.href) {
const toast = document.getElementById('toast-refresh');
toast.classList.remove('hidden');
setTimeout(() => {
toast.classList.add('hidden');
}, 5000);
}
}
});pwa:
serviceworker:
workbox:
resource_caches:
- ...
broadcast_headers:
- 'X-App-Cache'pwa:
serviceworker:
enabled: true
src: "sw.js"
workbox:
resource_caches:
- match_callback: 'startsWith: /pages/'
cacheable_response_headers:
'X-Is-Cacheable': 'true'
cacheable_response_statuses: [200]pwa:
manifest:
enabled: true
display: "standalone"
display_override: ["fullscreen", "minimal-ui"]pwa:
manifest:
enabled: true
background_color: "#ffffff"
theme_color: "#212529"
name: 'My Awesome Application'
short_name: 'awesome-app'
id: '/?manifest=1'
description: 'With application will help you to change the world'
orientation: "any"
display: "standalone"
scope: "/"
display_override: ['fullscreen', 'minimal-ui', 'window-controls-overlay']
id: "/"
start_url: "./"
icons:
- src: "images/favicon.ico"
sizes: [48]
- src: "images/favicon-512x512.png"
sizes: [512]
- src: "images/favicon.svg"
sizes: [0]
- src: "images/favicon.svg"
purpose: 'maskable'
sizes: [0]
screenshots:
- "images/screenshots/homepage-1303x1718.png"
- src: "images/screenshots/feature1-1303x1718.png"
label: "Feature 1 in action"
- src: "images/screenshots/feature2-2056x1080.png"
label: "Feature 2 and available options"
- src: "images/screenshots/feature3-2056x1080.png"
label: "Feature 3 at its best"
categories: ['utility', 'productivity']
shortcuts:
- name: "Feature 1"
short_name: "feature1"
description: "See Feature #1 in live action"
url: "app_feature1"
icons:
- src: "images/feature1.svg"
sizes: [0]
- src: "images/feature1-96x96.png"
sizes: [96]
pwa:
manifest:
enabled: true
icons:
- src: "icons/icon-192x192.png"
sizes: [192]
- src: "icons/icon-192x192.png"
sizes: [192]
purpose: "maskable"
- src: "icons/icon.svg"
sizes: 0pwa:
manifest:
icons:
- src: "icons/icon-48x48.png"
sizes: [48]
- src: "/home/project/foo/bar/icon-48x48.png"
sizes: [48]
- src: "src/resources/data/icon-48x48.png"
sizes: [48]pwa:
manifest:
icons:
- src: "icons/icon-192x192.png"
sizes: [48, 96, 192]
- src: "icons/icon.svg"
sizes: 0<?php
declare(strict_types=1);
namespace Acme;
use SpomkyLabs\PwaBundle\ServiceWorkerRule\ServiceWorkerRule;
final readonly class MyCustomRule implements ServiceWorkerRule
{
private Workbox $workbox;
public function __construct(
ServiceWorker $serviceWorker,
) {
$this->workbox = $serviceWorker->workbox;
}
public function process(bool $debug = false): string
{
return <<<HELLO_WORLD
// This will be added to the Service Worker
console.log('FOO-BAR from the Service Worker!');
HELLO_WORLD;
}
}