TL;DR: It's easier than ever to transform your MeteorJS app into a PWA. Only few things have changed since my original article from 2020. These are mostly improvements on ServiceWorker, Manifest file and Head tags. Transforming to PWA will be a great benefit for your app's performance, UX and SEO.
Table of Contents
- What are PWAs?
- Why should I make my MeteorJS app a PWA?
- Enough talk, show me how to do it
- Where to go from here?
- About me
What are PWAs?
According to web.dev PWAs are
[...] web apps built and enhanced with modern APIs to deliver enhanced capabilities, reliability, and installability while reaching anyone, anywhere, on any device, all with a single codebase.
This makes them very different from native apps, which are build specifically for their target platforms (desktop or mobile devices). PWAs instead leverage the universal availability of the web platform. Does this sound familiar as a MeteorJS developer? Right, MeteorJS does so, too.
Why should I make my MeteorJS app a PWA?
With MeteorJS you build your apps by default and primarily for the web platform (although you can also build for desktop using Electron and for mobile using Cordova or React Native). They rely on a single codebase, as PWAs do, and use JavaScript across the full stack.
Transforming your MeteorJS app into a PWA is a low-hanging fruit that comes with great benefits for your users in terms of performance (caching), SEO (manifest, meta tags), user experience (responsive design) or universal availability (offline capabilities).
Enough talk, show me how to do it
As a preliminary requirement you either need to have an existing MeteorJS app, optimally using a responsive design library, such as Tailwind or Bootstrap.
If you don't have one, you can create it with ease. Simply installing MeteorJS, then enter the following commands:
$ meteor create --tailwind pwa-example
$ cd pwa-example
$ meteor
1. Add a service worker
The ServiceWorker is the core component that drives your PWA.
It takes the form of a JavaScript file that can control the web page/site that it is associated with, intercepting and modifying navigation and resource requests, and caching resources in a very granular fashion to give you complete control over how your app behaves in certain situations (the most obvious one being when the network is not available).
MDN Service Worker API
To integrate a ServiceWorker you need to create a new folder and fetch the foundation file from jamauro/pwa-kit.
$ mkdir -p public
$ curl "https://raw.githubusercontent.com/jamauro/pwa-kit/refs/heads/main/sw.js" >> ./public/sw.js
You can extend or modify it to your needs or leave it as a good default. Your next goal is to init the ServiceWorker as soon as possible. For this, create a startup file:
$ mkdir -p imports/startup/client
$ touch imports/startup/client/serviceworker.js
Add the following content to the file:
Meteor.startup(async () => {
try {
await navigator.serviceWorker.register('/sw.js');
} catch (error) {
console.error('Service Worker registration failed:', error);
}
});
Finally, you need to import the startup file in client/main.js
or client/main.jsx
(depending on which frontend you have chosen)
import '../imports/startup/client/serviceworker';
That's it! You're already halfway-through on your way to your PWA.
But in MeteorJS, most traffic between clients and the server happens through DDP, a WebSocket communication protocol. ServiceWorkers work on HTTP-level.
That's correct. However, the MeteorJS client app itself is still a bundle of HTML, JavaScript, CSS and various assets and it is beneficial to control most of these resources in the most performant way possible. Additionally, Meteor does not restrict you from using HTTP requests, for example via fetch
.
2. Provide a manifest
In the next step you will provide a manifest file that describes your app and configures appearance and behavior, when installed as PWA.
In order to do that, create a new JSON file in your public folder via
$ echo "{}" >> public/app.webmanifest
Then you need to provide at least the following minimal information:
{
"name": "your app name",
"short_name": "app name",
"icons": [
{
"src": "/icons/icon-192.png",
"type": "image/png",
"sizes": "192x192"
},
{
"src": "/icons/icon-512.png",
"type": "image/png",
"sizes": "512x512"
}
],
"theme_color": "#0096FF",
"background_color": "#0096FF",
"start_url": "/",
"scope": "/",
"display": "standalone"
}
Read more at web.dev about the web-app manifest file.
As you can see, there are several icons included. Make sure to adjust the paths to match your icons and that you have at least an icon sized in 192x192px and 512x512px available under these paths!
If you don't then you can use the following code to grab two placeholder icons from my older article:
$ mkdir -p public/icons
$ curl -o public/icons/icon-192.png https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fid91jftqd0ualcuirtyf.png
$ curl -o public/icons/icon-512.png https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fevszcjg6ix64lkcc5wb0.png
To show you the awesomeness of these images, let me link them here again:
3. Add Head entries
In order to make your manifest file and icons discoverable you need to link them in your HTML head
using the meta
and link
tags in your client/main.html
file:
<head>
<title>Meteor App</title>
<meta charset="utf-8">
<meta http-equiv="x-ua-compatible" content="ie=edge">
<meta
name="viewport"
content="width=device-width, height=device-height, viewport-fit=cover, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no"
>
<meta name="mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-capable" content="yes">
<link rel='manifest' href='/app.webmanifest'>
<link rel='icon' type='image/png' sizes='192x192' href='/icons/icon-192.png'>
<!-- TODO: update colors based on your theme color -->
<meta name='msapplication-TileColor' content='#0096FF'>
<meta name='theme-col4. Force sslor' content='#0096FF'>
<!-- optional - these icons require further action -->
<link rel='icon' type='image/png' sizes='32x32' href='/icons/favicon-32x32.png'>
<link rel='icon' type='image/png' sizes='16x16' href='/icons/favicon-16x16.png'>
<link rel='mask-icon' href='/icons/safari-pinned-tab.svg' color='#0096FF'>
<link rel='apple-touch-icon' href='/icons/apple-touch-icon.png' />
<noscript>
<style>
body:before { content: 'Sorry, your browser does not support JavaScript!'; }
</style>
</noscript>
</head>
While this tutorial provides you the minimal set of icons, required for a working PWA, you need to action to meet platform-specific icon formats in order to be compliant.
Also note, there are various optimizations in head tags load order, something you might want to further investigate, too.
4. Force ssl
The ServiceWorker requires HTTPS, which you can easily enforce via
$ meteor add force-ssl
If you use Galaxy (Meteor Cloud) as your hosting platform, then this is sufficient on it's own and you need no further configuration when you deploy as subdomain of meteorapp.com
. If you use a custom domain then you can configure SSL in a single step.
If you deploy to another hosting provider or envirely custom environment, then you need to make sure you have SSL certificates configured correctly on your own. Meteor-UP might be a good choice in this case, as it provides SSL config via let's encrypt out of the box.
There are also other resources linked in this Meteor guide section on SSL.
Where to go from here?
You should checkout jam:offline if you want to gradually add offline-capabilities to your new upgraded PWA.
If the provided ServiceWorker is not sufficient for your needs, then you might checkout Google workbox and its recipes.
If you think about push notifications, then you might want to consider activitree:push or solutions like Novu. If you want to be free of any vendor infrastructure then you might want to implement Web Push Notifications for PWAs, which are also supported by ServiceWorkers.
The web.dev guide on PWA covers nearly every aspect of PWAs and how they work.
Finally, we also covered PWAs in one of our livestreams episodes "This Week in MeteorJS":
About me 👋
I regularly publish articles about MeteorJS and JavaScript here on dev.to. I also recently co-hosted the weekly MeteorJS Community Podcast, which covers the latest in Meteor and the community.
You can also find me (and contact me) on GitHub, Twitter/X and LinkedIn.
If you like what you read and want to support me, you can sponsor me on GitHub or buy me a book from my Amazon wishlist.
Top comments (3)
Thanks for the update Jan. Any thoughts on the best way to handle push notifications ? That's our main reason for adding the PWA layer to our app.
Well, this depends on your overall vendor strategy. If you want to use Firebase Messaging then you might want to look into
activitree:push
or solutions like Novu. If you want to be free of any vendor infrastructure then you might want to implement Web Push Notifications for PWAs, which are also supported by ServiceWorkers.However, we found that PN are in general to be used with care. While engaging for some users, we found it to be rather annoying for most of our users if it's not connected to a direct interaction (such as incoming message in a chat). We implemented for our users a naotification inbox inside the app and send emails on urgent circumstances.
Thanks Jan, that's quite the comprehensive guide Paul put together. Will have to take some time and study everything.