This is to demonstrate the basic steps of turning an elm-land application into an installable PWA (Progressive Web App). This is just the beginning, you have to add offline functionality or PWA features like nofification, taking pictures and others.
This uses the elm-land example 05-user-auth as a starting point. If you are not familiar with it, have a look at the turorial first.
We have a branch main
where the finished result is. A branch initial
where the original tutorial code is. And a branch introduction-of-pwa
where every step has its own commit.
If you want to sell the full diff for all steps, have a look at the PR
You will find a description of every step in this file in the section Steps
Following every step needed to installable PWA.
We need a server to authenticate the user. We do not want to run it on every device using the PWA, so a localhost implementation isn't a great idea for this. So first lets swap the localhost address to some address I made up with a similar implemented server.
So now the boring stuff is out of our way, lets start with PWA specific things. First of all, what do we need?
MDN tells us, we need 4 things:
- A web manifest
- Web site to be served from a secure HTTPS domain.
- An icon to represant the app on the device
- A service worker
Oh, that sounds like a lot of work and a lot of questions. Lets tackle it one by one.
Elm-land does not have a directory with the name static
right now, so we create it and add the manifast
file. This can take an optional fileextension if you like manifest.webmanifest
more. Files inside the static directory will be served, so it is now possible to bring this file to the client.
Let's have a look at the content. What is in there? I assume you know JSON, this file is formatted with it. So let's just talk about the minimal content.
name
andshort_name
: Well, here you can define the name of your app. It the name is too long for some use cases you can also define a shorter name. If you want to know more, have a look at the documentation for name or short_name.icons
: Without icon you get nothing to tap on your phones home screen, so this is mandatory to have an app.start_url
: This is the path of your elm-land app you want to direct to, when the app is started. In almost every example of PWAs you will find./index.html
as the default value. WATCH OUT elm-land will redirect you to page not found if you enter index.html after a route defined withHome_.elm
. This took me some time to figure it out. Be smart and set./
as thestart_url
.display
: You have to define the look of the app. Our app should look like a normal app on the phone and not like a browser, that's why we usestandalon
. If you do not want this, choose an other option from the documentation.
That was a lot to explain for a 14 line file added. This means there is a lot of heavy lifting done by the browser vendors. The next time will be way easier :-)
So the manifest file should now be reachable by requesting http://localhost:1234/manifest
in your browser. If not, check the port or go back to step 2
. But our application does not know about the manifest file. If you load your app and check the traffic in the dev tools you will not see the manifest file. So how do we get it to load in the client?
Elm-land makes this an easy task. Just add an link entry to the elm-land.json like you did with the stylesheet earlier.
Now refresh your page and you should see the manifest file in your network traffic. (Ignore the error for fivicon for now, we will be fixing this soon)
So an Icon is a static file, just like the manifest file, right? So we put it inside static too. And the other this is, we already put a path and name inside our manifest file, so all we have to do is to find a geat logo for our app. Oh, you say this is a hard task, I know. Lucky me, I have a cute logo of a T-Rex with a clipboard at hand, so I will use this. He will track our progress and remind us if we forget anything.
While we are at it, I will add the same file as static/favicon.ico
so we have a nive friendly tab icon.
This is just for demonstration and no good practice for production apps, these images are way too big. Use small images as icon.
So no more error in dev tools and a nice looking tab, on to the next one :-)
Just to make it clear upfront, this will be a very simple serviceWorker. Do not take it in production without checking your needs
The base for my file can be found here. I just made it a little bit easier (on the eyes), removed the multi cache stuff to have a simpler one cache solution and changed the PRECACHE_URLS
to match elm-lands needs. If you need some stuff from the static directory loaded at runtime and want to store it on the device when the user installs the PWA, add it to this list.
So just have a look at the static/serviceWorker.js
.
What you want to think about is the caching strategy of your PWA. Here I choose Network First
strategy, so local files are just a backup when there is no internet connection. This depends on your goals, I think this blogpost is a good entry point.
So now we have a serviceWorker file. This is where the magic of a PWA is. MDN might explain it better than me, take some time to have a look what the serviceWorker is all about before you continue.
Welcome back, I am really proud of you, because you took the time and read the documentation. Just like I suggested.
So now you ask: 'But just the file is not enough, what do we do with it?' Let's go to the next step to answer that.
So as you know by now, we need to install or register the service worker. Most tutorials will suggest you to add some javascript to your index.html
file. But we are using elm-land, so this will be generated and we can not add any javascript to it. Well, at least not direct.
But we can do the better solution: Create a javascript file and reference the script in our elm-land generted index.html. So how do we do that.
First we need our javascript in a file in the static
directory. Mine is called registerServiceworker.js
and I hope you can gess, what the content will do ;-). Have a look to be sure!
So now that we have a script, how do we execute it using elm-land? The answer is once again the elm-land.json
file. Just add it in the script
block.
"script": [{ "src": "./registerServiceworker.js" }]
Save, refresh your page and have a look in the devtools console. There it is: Registration successful, scope is...
So lets have a recap.
- webmanifest done
- serviceworker done
- logo done
- https todo
So on to the next part.
There are a lot of ways to host a webapp. Feel free to use an other way. This is why I put this as last step, so you can follow along all the way and host as you like. I will use vercel as I find it quick and easy to use and it has documentation on elm-land.
So lets get started.
- Copy the
vercel.json
from elm-land documentation. - execute vercel cli (no parameters needed) with
npx vercel
and follow instructions - Not even a full minute later the app is online
- This is no commercial for vercel, I am just blown away by how easy it is, compared to other products I used in the past. And I know there are other modern and easy alternatives.
I will use an iPhone with Safari do describe how it works. Android Desktop or other usage is an exercise for the reader.
- First visit your website with your phone or use mine.
- Tap on the share button and selet
Add to homescreen
- See your logo, the
short_name
defined in webmanifest and the URL. Tap on add. - Congratulations - You have installed your own PWA.
- Come an, start it.
- Try different cache strategies, test offline functionality (right now it is bad) and learn more.
If you register your serviceworker on your dev machine do not waste time like I did. Depending on your cache strategy you could have realy funny behaviour the next time you develop an app on port 1234. I had a cache first service worker running for lokalhost:1234 and the new elm-land project used the same port. Whenever I loaded a page the old project did not have, I had the correct result. But the homepage has always been the hello world from the last project. One more minute and I would have opened an issue on elm-land, very nasty.
As mentioned in step 2, elm-land responds to ./index.html
with page not found. But every tutorial I found has da default value of ./index.html
for start_url
in the manifest file. Remember to set this to ./
if you use a more complex manifest file as template.