{"id":13826566,"url":"https://github.com/jfadev/jfa-pwa-toolkit","last_synced_at":"2025-07-09T00:34:07.900Z","repository":{"id":57280632,"uuid":"165089801","full_name":"jfadev/jfa-pwa-toolkit","owner":"jfadev","description":"⚡️ PWA Features to Any Website (very Fast \u0026 Easy)","archived":false,"fork":false,"pushed_at":"2019-09-02T03:11:24.000Z","size":3789,"stargazers_count":270,"open_issues_count":10,"forks_count":37,"subscribers_count":9,"default_branch":"master","last_synced_at":"2025-06-19T13:07:36.120Z","etag":null,"topics":["javascript","javascript-library","manifest","manifest-json","offline","offline-first","precache","progressive-web-app","progressive-web-applications","progressive-web-apps","push-notifications","pwa","pwa-toolkit","service-worker","service-workers","sw-precache","sw-toolbox","toolkit","web-push","web-push-notifications"],"latest_commit_sha":null,"homepage":"","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/jfadev.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2019-01-10T16:05:59.000Z","updated_at":"2025-06-10T21:58:44.000Z","dependencies_parsed_at":"2022-09-02T22:22:17.684Z","dependency_job_id":null,"html_url":"https://github.com/jfadev/jfa-pwa-toolkit","commit_stats":null,"previous_names":[],"tags_count":1,"template":false,"template_full_name":null,"purl":"pkg:github/jfadev/jfa-pwa-toolkit","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jfadev%2Fjfa-pwa-toolkit","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jfadev%2Fjfa-pwa-toolkit/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jfadev%2Fjfa-pwa-toolkit/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jfadev%2Fjfa-pwa-toolkit/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/jfadev","download_url":"https://codeload.github.com/jfadev/jfa-pwa-toolkit/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jfadev%2Fjfa-pwa-toolkit/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":264370882,"owners_count":23597665,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["javascript","javascript-library","manifest","manifest-json","offline","offline-first","precache","progressive-web-app","progressive-web-applications","progressive-web-apps","push-notifications","pwa","pwa-toolkit","service-worker","service-workers","sw-precache","sw-toolbox","toolkit","web-push","web-push-notifications"],"created_at":"2024-08-04T09:01:40.406Z","updated_at":"2025-07-09T00:34:07.247Z","avatar_url":"https://github.com/jfadev.png","language":"JavaScript","funding_links":[],"categories":["JavaScript"],"sub_categories":[],"readme":"# jfa-pwa-toolkit\nAdd Progressive Web Apps (PWA) Features to Any website very Easy and Fast ⚡️\n\n\nSet a simple configuration file or exploit more features with the easy-to-use library in your js code.\n\nNote: This project is **Beta**\n\n## Features\n* Web App Manifest\n* Icons Structure Files\n* Add to Home Screen (A2HS)\n* Offline Work Mode\n* Precaching\n* Caching Strategies\n* Push Notifications\n\n## Demo\n* [Live Demo](https://pwa-toolkit-demo.jordifernandes.com)\n* [Demo Repository](https://github.com/jfadev/jfa-pwa-toolkit-demo)\n\n\u003c!-- ## Examples\n[See our demo here](demo-app/src/example/Example.react.js) --\u003e\n\n## Requirements\n* [Workbox](https://github.com/GoogleChrome/workbox)\n\nThis package uses Workbox internally but you do not need to import it.\n\n## Starting with an Example\n* 🇬🇧 [Example in English](https://jordifernandes.com/jfa-pwa-toolkit/)\n* 🇪🇸 [Ejemplo en Español](https://jordifernandes.com/es/jfa-pwa-toolkit/)\n* 🇫🇷 [Exemple en Français](https://jordifernandes.com/fr/jfa-pwa-toolkit/)\n* 🇧🇷 [Exemplo em Português](https://jordifernandes.com/pt/jfa-pwa-toolkit/)\n\n## Getting Started\n\nClone the repository\n```console\n$ git clone git@github.com:jfadev/jfa-pwa-toolkit.git\n```\nCreate the app icons manually or with the tool [PWA Image Generator](https://www.pwabuilder.com/imageGenerator) and replace files in `/pwa/icons/`.\n\nIf you have moved the `pwa` folder somewhere else in your project, edit the `PWA_ROOT` variable in your `/sw.js` file. It is important that the file `sw.js` is in the root of your site.\n\nEdit the template of your project to include the metatags and scripts needed as in the following example.\n**Attention** with the paths if you decided to move the `pwa` folder\n```html\n\u003c!DOCTYPE html\u003e\n\u003chtml lang=\"en-us\"\u003e\n\u003chead\u003e\n    \u003cmeta charset=\"utf-8\"\u003e\n    \u003cmeta http-equiv=\"X-UA-Compatible\" content=\"IE=edge\"\u003e\n    \u003cmeta content=\"width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no\" name=\"viewport\"\u003e\n\n    \u003c!-- Theme color --\u003e\n    \u003cmeta name=\"theme-color\" content=\"#000\" /\u003e\n\n    \u003c!-- PWA Manifest --\u003e\n    \u003clink rel=\"manifest\" href=\"/pwa/manifest.json\"\u003e\u003c/link\u003e\n\n    \u003c!-- Favicon --\u003e\n    \u003clink rel=\"icon\" type=\"image/png\" href=\"/pwa/icons/chrome/chrome-favicon-16-16.png\"\u003e\n\n    \u003c!-- Only for iOS: Configs --\u003e\n    \u003cmeta name=\"apple-mobile-web-app-capable\" content=\"yes\"\u003e\n    \u003cmeta name=\"apple-mobile-web-app-status-bar-style\" content=\"black\"\u003e\n    \u003cmeta name=\"apple-mobile-web-app-title\" content=\"jfaPWAtk\"\u003e\n    \u003c!-- Icons --\u003e\n    \u003clink rel=\"apple-touch-icon\" href=\"/pwa/icons/ios/ios-appicon-76-76.png\"\u003e\n    \u003clink rel=\"apple-touch-icon\" href=\"/pwa/icons/ios/ios-appicon-120-120.png\" sizes=\"120x120\"\u003e\n    \u003clink rel=\"apple-touch-icon\" href=\"/pwa/icons/ios/ios-appicon-152-152.png\" sizes=\"152x152\"\u003e\n    \u003clink rel=\"apple-touch-icon\" href=\"/pwa/icons/ios/ios-appicon-180-180.png\" sizes=\"180x180\"\u003e\n    \u003clink rel=\"apple-touch-icon\" href=\"/pwa/icons/ios/ios-appicon-1024-1024.png\" sizes=\"1024x1024\"\u003e\n    \u003c!-- Splash Screen --\u003e\n    \u003clink rel=\"apple-touch-startup-image\" href=\"/pwa/icons/ios/ios-launchimage-640-960.png\" sizes=\"640x960\"\u003e\n    \u003clink rel=\"apple-touch-startup-image\" href=\"/pwa/icons/ios/ios-launchimage-640-1136.png\" sizes=\"640x1136\"\u003e\n    \u003clink rel=\"apple-touch-startup-image\" href=\"/pwa/icons/ios/ios-launchimage-750-1334.png\" sizes=\"750x1334\"\u003e\n    \u003clink rel=\"apple-touch-startup-image\" href=\"/pwa/icons/ios/ios-launchimage-768-1024.png\" sizes=\"768x1024\"\u003e\n    \u003clink rel=\"apple-touch-startup-image\" href=\"/pwa/icons/ios/ios-launchimage-1024-768.png\" sizes=\"1024x768\"\u003e\n    \u003clink rel=\"apple-touch-startup-image\" href=\"/pwa/icons/ios/ios-launchimage-1242-2208.png\" sizes=\"1242x2208\"\u003e\n    \u003clink rel=\"apple-touch-startup-image\" href=\"/pwa/icons/ios/ios-launchimage-1334-750.png\" sizes=\"1334x750\"\u003e\n    \u003clink rel=\"apple-touch-startup-image\" href=\"/pwa/icons/ios/ios-launchimage-1536-2048.png\" sizes=\"1536x2048\"\u003e\n    \u003clink rel=\"apple-touch-startup-image\" href=\"/pwa/icons/ios/ios-launchimage-2048-1536.png\" sizes=\"2048x1536\"\u003e\n    \u003clink rel=\"apple-touch-startup-image\" href=\"/pwa/icons/ios/ios-launchimage-2208-1242.png\" sizes=\"2208x1242\"\u003e\n\n    \u003ctitle\u003eJFA PWA Toolkit Example\u003c/title\u003e\n\n\u003c/head\u003e\n\u003cbody\u003e\n\n    \u003c!-- Load JFA PWA Toolkit Configs --\u003e\n    \u003cscript type=\"text/javascript\" src=\"/pwa/config.js\"\u003e\u003c/script\u003e\n\n    \u003c!-- Load JFA PWA Toolkit Lib --\u003e\n    \u003cscript type=\"text/javascript\" src=\"/pwa/pwa.js\"\u003e\u003c/script\u003e\n\n    \u003c!-- Register principal Service Worker --\u003e\n    \u003cscript type=\"text/javascript\"\u003e\n        PWA.ServiceWorker.register();\n    \u003c/script\u003e\n\n\u003c/body\u003e\n\u003c/html\u003e\n```\n\nEdit the manifest file `/pwa/manifest.json`. \nYou can rename `manifest.json` to `app.webmanifest` if you prefer [Reccomended by W3C](https://w3c.github.io/manifest/#media-type-registration).\n**Attention** with the paths of icons if you decided to move the `pwa` folder\n```json\n{\n    \"name\": \"Your App Name\",\n\n    \"short_name\": \"Your App Short Name\",\n    \"description\": \"Your App Description.\",\n    \"orientation\": \"any\",\n    \"theme_color\": \"#000\",\n    \"background_color\": \"#000\",\n\n    \"icons\": [{\n            \"src\": \"/pwa/icons/windows10/Square71x71Logo.scale-400.png\",\n            \"sizes\": \"284x284\"\n        },\n    ]\n}\n```\n\nEdit the config file `/pwa/config.js`.\n**Attention** with the paths of icons if you decided to move the `pwa` folder\n```javascript\nconst PWA_CONFIG = {\n\n    // App config\n    app: {\n        // App name\n        name: 'your-app-name',\n        // App version\n        version: 'v1',\n    },\n\n    // Service Worker config\n    sw: {\n        // Main service worker filepath (always root of project)\n        filepath: '/sw.js',\n        // Route of offline page\n        offline_route: '/pwa/errors/offline/',\n    },\n\n    // Push manager config\n    push: {\n        // Enable/disable push notifications\n        active: true,\n        // Server config\n        server: {\n            // API public key\n            public_key: 'YOURAPIPUBLICKEY',\n            // Subscription API endpoint\n            endpoint: '/api/push/subscription/',\n        },\n        // Notification config\n        notification: {\n            // Title of notifications from the server\n            title: 'Your App Name',\n            // Options object same that showNotification() options\n            // (https://developer.mozilla.org/es/docs/Web/API/ServiceWorkerRegistration/showNotification)\n            options: {\n                // A string representing an extra content to display within the notification\n                body: '',\n                // he URL of an image to be used as an icon by the notification\n                icon: '/pwa/icons/firefox/firefox-general-64-64.png',\n                // A vibration pattern to run with the display of the notification.\n                // A vibration pattern can be an array with as few as one member.\n                // The values are times in milliseconds where the even indices (0, 2, 4, etc.)\n                // indicate how long to vibrate and the odd indices indicate how long to pause.\n                // For example [300, 100, 400] would vibrate 300ms, pause 100ms, then vibrate 400ms.\n                vibrate: [100, 50, 100],\n                // Arbitrary data that you want associated with the notification. This can be of any data type\n                data: {\n                    dateOfArrival: Date.now(),\n                    primaryKey: '1',\n                    clickUrl: '',\n                },\n            },\n            // notification click event\n            notificationclick: {\n                // Enable/disable notification click event\n                active: true,\n            }\n        }\n    },\n\n    // Cache config\n    cache: {\n        // Images cache config (png|jpg|jpeg|svg|gif)\n        images: {\n            // Enable/disable images caching\n            active: true,\n            // The maximum number of entries to cache.\n            // Entries used the least will be removed as the maximum is reached.\n            maxentries: 500,\n            // The maximum age of an entry before it's treated as stale and removed.\n            maxageseconds: 365 * 24 * 60 * 60,\n        },\n        // Static files cache config (js|json|css)\n        statics: {\n            // Enable/disable static files caching\n            active: true,\n            // The maximum number of entries to cache.\n            // Entries used the least will be removed as the maximum is reached.\n            maxentries: 500,\n            // The maximum age of an entry before it's treated as stale and removed.\n            maxageseconds: 365 * 24 * 60 * 60,\n        },\n        // Fonts cache config (eot|ttf|woff|woff2|otf)\n        // with cross-origin requests example google fonts\n        fonts: {\n            // Enable/disable fonts caching\n            active: true,\n            // The maximum number of entries to cache.\n            // Entries used the least will be removed as the maximum is reached.\n            maxentries: 500,\n            // The maximum age of an entry before it's treated as stale and removed.\n            maxageseconds: 365 * 24 * 60 * 60,\n        },\n        routes: {\n            // Force the response to come from the network\n            networkonly: {\n                // Enable/disable network only routes caching\n                active: true,\n                // Matching routes with a Regular Expression\n                regex: /\\/(?:login|logout)\\//,\n            },\n\t    // Resources are requested from both the cache and the network in parallel.\n            // The strategy will respond with the cached version if available,\n            // otherwise wait for the network response.\n            // The cache is updated with the network response with each successful request.\n            stalewhilerevalidate: {\n                active: true,\n                regex: /\\/news\\/.*/,\n            },\n\t    // Network first request strategy.\n            networkfirst: {\n                active: true,\n                regex: /.*/,\n            },\n\t    // Cache first request strategy.\n            cachefirst: {\n                active: false,\n                // regex: /.*/,\n                // maxentries: 500,\n                // maxageseconds: 365 * 24 * 60 * 60,\n            },\n\t    // Force the response to come from the browser.\n            cacheonly: {\n                active: false,\n                // regex: /.*/,\n            },\n        },\n\t// Add your custom service worker for load it.\n        custom: {\n            active: false,\n            // service worker script route\n\t    // script: '/pwa/sw/my-custom-sw.js',\n        },\n    },\n\n    // Precache config\n    precache: {\n        // Enable/disable precaching\n        active: true,\n        // Routes to\n        routes: [\n            '/assets/example.css',\n            '/assets/example.png',\n            '/assets/example.js',\n        ],\n    },\n}\n```\n\nAt this point your PWA is able to precache static resources, cache the different resources for offline access, icons, splash screen and add to home screen.\n\n\nYou may need more features, for this we will use the classes and methods offered by the global variable PWA that will allow us to do things like subscription to push notifications among other things.\n\n\n## Code Examples\n\n#### Button to subscribe to Push Notifications\n```html\n\u003cbutton type=\"button\" id=\"btn-subscription\"\u003e\n    Subscribe to recive Push Notifications\n\u003c/button\u003e\n```\n```javascript\ndocument\n    .getElementById('btn-subscription')\n    .addEventListener('click', () =\u003e {\n    \tif (\n            PWA.Notification.isDefault() ||\n            PWA.Notification.isGranted()\n        ) {\n            PWA.Push.getSubscription((subscription) =\u003e {\n                if (subscription) {\n                    console.log('You are now subscribed to receive Push Notifications!');\n                } else {\n                    PWA.Push.subscribe((r) =\u003e {\n                        console.log('Congratulations! You are subscribed to receive Push Notifications!');\n                    });\n                }\n            });\n        } else {\n            console.log(\"You've turned off Push Notifications. Allow Push Notifications in your browser settings.\");\n        }\t\n    })\n;\n```\n\n#### Checkbox toggle to subscribe/unsubscribe to Push Notifications\n```html\n\u003cinput type=\"checkbox\" id=\"toggle-subscription\"\u003e Push Notifications\n```\n```javascript\ndocument\n    .getElementById('toggle-subscription')\n    .addEventListener('change', () =\u003e {\n    \tif (\n            PWA.Notification.isDefault() ||\n            PWA.Notification.isGranted()\n        ) {\n            PWA.Push.getSubscription((subscription) =\u003e {\n                if (subscription) {\n                    PWA.Push.unsubscribe((r) =\u003e {\n                        console.log('You have been unsubscribed to receive Push Notifications!');\n                    });\n                } else {\n                    PWA.Push.subscribe((r) =\u003e {\n                        console.log('You are now subscribed to receive Push Notifications!');\n                    });\n                }\n            });\n        } else {\n            console.log(\"You've turned off Push Notifications. Allow Push Notifications in your browser settings.\");\n        }\t\n    })\n;\n```\n\n#### Check the Notifications permissions\n\n###### Default status \n```javascript\nif (PWA.Notification.isDefault()) {\n    console.log('Permission for Notifications are with default status.');\n} else {\n    console.log('Permission for Notifications are not with default status.');\n}\n```\n###### Granted status \n```javascript\nif (PWA.Notification.isGranted()) {\n    console.log('Permission for Notifications was granted!');\n} else {\n    console.log('Permission for Notifications was not granted!');\n}\n```\n###### Blocked status\n```javascript\nif (PWA.Notification.isBlocked()) {\n    console.log('Permission for Notifications was blocked.');\n} else {\n    console.log('Permission for Notifications was not blocked.');\n}\n```\n###### Denied status\n```javascript\nif (PWA.Notification.isDenied()) {\n    console.log('Permission for Notifications was denied.');\n} else {\n    console.log('Permission for Notifications was not denied.');\n}\n```\n\n#### Displays a pop-up requesting permission to allow Notifications\n```javascript\nPWA.Notification.requestPermission((status) =\u003e {\n    console.log('Notification permission status:', status);\n});\n```\n\n#### Get the Notifications permission status\n```javascript\nvar permission = PWA.Notification.getPermission();\n```\n\n#### Show Notification sent by the browser\n```javascript\nconst options = {\n    body: 'Extra content to display within the notification',\n    icon: '../images/touch/chrome-touch-icon-192x192.png'\n};\n\nPWA.Notification.show('Notification Title', options, sent =\u003e {\n    if (sent) {\n    \tconsole.log('The Notification has been sent');\n    }\n});\n```\n\n#### Register the main service worker\n```javascript\nPWA.ServiceWorker.register();\n```\n\n#### Get the Registration object of the service worker\n```javascript\nPWA.ServiceWorker.getRegistration((registration) =\u003e {\n    if (registration) {\n    \tconsole.log(registration);\n    } else {\n       console.log('The Service Worker is not registered!');\n    }\n});\n```\n\n#### Check if the browser support Service Workers\n```javascript\nif (PWA.Navigator.isSupportedServiceWorker()) {\n    console.log('This browser support Service Workers!');\n} else {\n    console.log('This browser does not support Service Workers.');\n}\n```\n\n#### Check if the browser support Notifications\n```javascript\nif (PWA.Navigator.isSupportedNotification()) {\n    console.log('This browser support Notifications!');\n} else {\n    console.log('This browser does not support Notifications.');\n}\n```\n\n#### Check if the browser is offline\n```javascript\nif (PWA.Navigator.isOffline()) {\n    console.log('No Internet connection!');\n} else {\n    console.log('You have an Internet connection!');\n}\n```\n\n#### Clear the browser app cache\n```javascript\nPWA.Navigator.clearCache();\n```\n\n## Documentation\n* [API Reference](https://github.com/jfadev/jfa-pwa-toolkit/blob/master/docs/API.md)\n\n\n## Playground\n* [JSFiddle](https://jsfiddle.net/jfadev/5hsj1pgL/83/)\n\n\n## License\nJFA PWA Toolkit is MIT licensed, as found in the [LICENSE](LICENSE) file.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjfadev%2Fjfa-pwa-toolkit","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjfadev%2Fjfa-pwa-toolkit","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjfadev%2Fjfa-pwa-toolkit/lists"}