Installing web apps
Safari, Chrome, and Edge all allow you to install websites as though they���re apps.
On mobile Safari, this is done with the ���Add to home screen��� option that���s buried deep in the ���share��� menu, making it all but useless.
On the desktop, this is ���Add to dock��� in Safari, or ���Install��� in Chrome or Edge.
Firefox doesn���t offer this functionality, which as a shame. Firefox is my browser of choice but they decided a while back to completely abandon progressive web apps (though they might reverse that decision soon).
Anyway, being able to install websites as apps is fantastic! I���ve got a number of these ���apps��� in my dock: Mastodon, Bluesky, Instagram, The Session, Google Calendar, Google Meet. They all behave just like native apps. I can���t even tell which browser I used to initially install them.
If you���d like to prompt users to install your website as an app, there���s not much you can do other than show them how to do it. But that might be about to change���
I���ve been eagerly watching the proposal for a Web Install API. This would allow authors to put a button on a page that, when clicked, would trigger the installation process (the user would still need to confirm this, of course).
Right now it���s a JavaScript API called navigator.install, but there���s talk of having a declarative version too. Personally, I think this would be an ideal job for an invoker command. Making a whole new install element seems ludicrously over-engineered to me when button invoketarget="share" is right there.
Microsoft recently announced that they���d be testing the JavaScript API in an origin trial. I immediately signed up The Session for the trial. Then I updated the site to output the appropriate HTTP header.
You still need to mess around in the browser configs to test this locally. Go to edge://flags or chrome://flags/ and search for ���Web App Installation API���, enable it and restart.
I���m now using this API on the homepage of The Session. Unsurprisingly, I���ve wrapped up the functionality into an HTML web component that I call button-install.
Here���s the code. You use it like this:
Install the appUse whatever text you like inside the button.
I wasn���t sure whether to keep the button element in the regular DOM or generate it in the Shadow DOM of the custom element. Seeing as the button requires JavaScript to do anything, the Shadow DOM option would make sense. As Tess put it, Shadow DOM is for hiding your shame���the bits of your interface that depend on JavaScript.
In the end I decided to stick with a regular button element within the custom element, but I take steps to remove it when it���s not necessary.
There���s a potential issue in having an element that could self-destruct if the browser doesn���t cut the mustard. There might be a flash of seeing the button before it gets removed. That could even cause a nasty layout shift.
So far I haven���t seen this problem myself but I should probably use something like Scott���s CSS in reverse: fade in the button with a little delay (during which time the button might end up getting removed anyway).
My connectedCallback method starts by finding the button nested in the custom element:
class ButtonInstall extends HTMLElement { connectedCallback () { this.button = this.querySelector('button'); ��� }customElements.define('button-install', ButtonInstall);If the navigator.install method doesn���t exist, remove the button.
if (!navigator.install) { this.button.remove(); return;}If the current display-mode is standalone, then the site has already been installed, so remove the button.
if (window.matchMedia('(display-mode: standalone)').matches) { this.button.remove(); return;}As an extra measure, I could also use the display-mode media query in CSS to hide the button:
@media (display-mode: standalone) { button-install button { display: none; }}If the button has survived these tests, I can wire it up to the navigator.install method:
this.button.addEventListener('click', async (ev) => { await navigator.install();});That���s all I���m doing for now. I���m not doing any try/catch stuff to handle all the permutations of what might happen next. I just hand it over to the browser from there.
Feel free to use this code if you want. Adjust the code as needed. If your manifest file says display: fullscreen you���ll need to change the test in the JavaScript accordingly.
Oh, and make sure your site already has a manifest file that has an id field in it. That���s required for navigator.install to work.
Jeremy Keith's Blog
- Jeremy Keith's profile
- 56 followers

