PWA: set manifest.json name & icon dynamically; client-side. Jabbadabbadoo!
Tags: pwa, manifest, dynamic
Published: 2021-08-05 22:08:24

_This is my 1st write up on dev.to site. Source code included as link. I use VueJS, but you can use vanilla JS too._

## manifest.json
PWA app needs manifest.json to be installable. We usually have index.html with the link to the manifest.json file, the separate one, where all items are defined. This file is static, the only way to change it is to edit it's source code (as you are developer, NOT user). _Suppose we serve static files, not dynamically created by node.js or similar server side technology._

Today I will show you a practical example how to change some manifest's items dynamically on client side, i.e. as user.

### note
I need to say my work is not my invention, it is based on excellent work, ideas and researches of other developers.
Special thanks to **alshakero,** his post [How to Setup Your Web App Manifest Dynamically Using Javascript](https://blog.omaralshaker.com/2018/01/16/how-to-setup-your-web-app-manifest-dynamically-using-javascript/) was very helpful.

### idea
We need only three steps to allow user to dynamically set some manifest.json items.
* create a form **outside the index.html** to set items (not mandatory)
* read manifest.json items as parameters from url (aka index.html?name=myApp&icon=icon_1)
* modify object represented manifest.json and inject it into index.html file

Sounds easy, right? 

### step 1: the form
_I write PWA as SPA (single page application) with all content inside index.html using template tags. Navigating via menu or url is maintained by the router and serves corresponding components (template + data) inside already loaded index.html._

For this step - creating the user form - we write another file named config.html. This file is separate and will NOT be included as asset in our PWA app (will NOT be cached via service worker).

This page - config.html - has form and submit button. On click event JS code assign user's input into variables and append it to index.html url as parameters and redirect page to index.html

Important part of config.js file:
```
var url = "https://pwa-dynamic-manifest.pablo74.repl.co/index.html?";
var params = "";
var fullUrl = "";

if (this.name){
  params += "name=" + this.name;
};
if (this.short_name){
  params += "&short_name=" + this.short_name;
};
if (this.bgColor){
  params += "&bg=" + this.bgColor;
};
if (this.isActive){
  params += "&icon=" + this.isActive + ".png";
}

fullUrl = url + params;
window.location.replace(fullUrl);
```

See my source code of [config.html](https://pwa-dynamic-manifest.pablo74.repl.co/config.html) and [config.js](https://pwa-dynamic-manifest.pablo74.repl.co/js/config.js)

### step 2: read parameters from url
We add link to app.js file inside index.html, it contains prefilled object as manifest.json data and code to read parameters and modify object.

Prefilled object (implicit values):
```
var manifestJSON = 
{
  "name": "info-()",
  "short_name": "info-()",
  "scope": "https://pwa-dynamic-manifest.pablo74.repl.co/",
  "start_url": "https://pwa-dynamic-manifest.pablo74.repl.co/index.html",
  "display": "standalone",
  "icons": [
    {
      "src": "https://pwa-dynamic-manifest.pablo74.repl.co/img/icon_192_192.png",
      "sizes": "192x192",
      "type": "image/png"
    },
    {
      "src": "https://pwa-dynamic-manifest.pablo74.repl.co/img/icon_512_512.png",
      "sizes": "512x512",
      "type": "image/png"
    }
  ],
  "background_color": "black",
  "theme_color": "#027be3"
};
```
Pay attention to scope and start_url property; we usually use something like `"scope": "/"` and `"start_url": "/"` but it doesn't work for me; so I have to change it to full url. This is the trick I have not seen elsewhere.

Now is time to show how to read parameters from url:
```
var urlParams = new URLSearchParams(window.location.search);
var name = urlParams.get('name');
var short_name = urlParams.get('short_name');
var bg = urlParams.get('bg');
var icon = urlParams.get('icon');
```

### step 3: modify object represented manifest.json and inject it into index.html file

There is a custom element in index.html for manifest.json file:

```
<link rel="manifest" id="my-manifest-placeholder">
```
You can see there is NOT href attribute, so this link is not functional yet.

Having variables from step 2 we can now modify object and inject it into index.html

```
if (name){
  manifestJSON.name = name;
};

if (short_name){
  manifestJSON.short_name = short_name;
};

if (bg) {
  manifestJSON.background_color = bg;
};

if (icon){
  manifestJSON.icons[0].src = "https://pwa-dynamic-manifest.pablo74.repl.co/img/" + icon;
}

const stringManifest = JSON.stringify(manifestJSON);
const blob = new Blob([stringManifest], {type: 'application/json'});
const manifestURL = URL.createObjectURL(blob);
document.querySelector('#my-manifest-placeholder').setAttribute('href', manifestURL);
```
You may think: "Hey, stop, where are the icons?". Right question. All icons are already included as assets in img folder; i.e. this way we set apps icon from our set of icons only, not from external files.

## Does it really work?
Yes, I tested it on two Android devices: mobile phone with Android 7 and tablet with Android 10. I am able to install even three apps with different name and icon on the same device from the same code base.

## Full source code
Because I use work of other developers, it is fair to show and spread all source code. You can find it on replit.com (repl.it previously): [pwa-dynamic-manifest](https://replit.com/@Pablo74/PWA-dynamic-manifest#index.html)
If you have an account you can simply fork this repl and modify it as needed.

## Important note
Use first config.html to load form, otherwise (loading index.html directly) you will not be able to modify manifest.json (unless you use url parameters directly).

## Improvements...?
This is a functional proof of concept, you can modify and enhance it as you like :-)