最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - How to create dynamic manifest.json file for PWA in ReactJS? - Stack Overflow

programmeradmin0浏览0评论

I have a project which needs to create a dynamic manifest.json file for my PWA (ReactJS).

Please see below my codes:

app.service.js:

        function getAppDetails() {
            const requestOptions = {
                method: 'GET',
                headers: authHeader()
            };
            // TODO create dynamic manifest.json file here
            return fetch(`${config.apiUrl}/api/apps`, requestOptions).then(handleResponse);
        }   

        function handleResponse(response) {
            return response.text().then(text => {
                const data = text && JSON.parse(text);
                if (!response.ok) {
                    if (response.status === 401) {
                        // auto logout if 401 response returned from api
                        logout();
                        location.reload(true);
                    } 
                    const error = (data && data.message) || response.statusText;
                    return Promise.reject(error);
                }

                return data;
            });
}

app.actions.js:

function getAppDetails() {
        return dispatch => {
            dispatch(request());

            appService.getAppDetails()
                .then(
                    details => dispatch(success(details)),
                    error => dispatch(failure(error.toString()))
                );
        };

        function request() { return { type: appConstants.GETDETAILS_REQUEST } }
        function success(details) { return { type: appConstants.GETDETAILS_SUCCESS, details } }
        function failure(error) { return { type: appConstants.GETDETAILS_FAILURE, error } }
}

LoginPage.jsx:

    import { appActions } from '../_actions';
    class LoginPage extends React.Component {
        constructor(props) {
            super(props);

            // reset login status
            this.props.dispatch(userActions.logout());

            this.state = {
                email: '',
                password: '',
                isDisabled: false,
                submitted: false
            };

            this.handleChange = this.handleChange.bind(this);
            this.handleSubmit = this.handleSubmit.bind(this);
            this.showSwal = this.showSwal.bind(this);
        }

        componentDidMount() {
            // TODO call the action to add dynamic manifest file to index.html
            this.props.dispatch(aapActions.getAppDetails());
        }

        render() {
            return (
                ................
            );
        }
}

I am new to this kind of thing. How can I get started?

I have a project which needs to create a dynamic manifest.json file for my PWA (ReactJS).

Please see below my codes:

app.service.js:

        function getAppDetails() {
            const requestOptions = {
                method: 'GET',
                headers: authHeader()
            };
            // TODO create dynamic manifest.json file here
            return fetch(`${config.apiUrl}/api/apps`, requestOptions).then(handleResponse);
        }   

        function handleResponse(response) {
            return response.text().then(text => {
                const data = text && JSON.parse(text);
                if (!response.ok) {
                    if (response.status === 401) {
                        // auto logout if 401 response returned from api
                        logout();
                        location.reload(true);
                    } 
                    const error = (data && data.message) || response.statusText;
                    return Promise.reject(error);
                }

                return data;
            });
}

app.actions.js:

function getAppDetails() {
        return dispatch => {
            dispatch(request());

            appService.getAppDetails()
                .then(
                    details => dispatch(success(details)),
                    error => dispatch(failure(error.toString()))
                );
        };

        function request() { return { type: appConstants.GETDETAILS_REQUEST } }
        function success(details) { return { type: appConstants.GETDETAILS_SUCCESS, details } }
        function failure(error) { return { type: appConstants.GETDETAILS_FAILURE, error } }
}

LoginPage.jsx:

    import { appActions } from '../_actions';
    class LoginPage extends React.Component {
        constructor(props) {
            super(props);

            // reset login status
            this.props.dispatch(userActions.logout());

            this.state = {
                email: '',
                password: '',
                isDisabled: false,
                submitted: false
            };

            this.handleChange = this.handleChange.bind(this);
            this.handleSubmit = this.handleSubmit.bind(this);
            this.showSwal = this.showSwal.bind(this);
        }

        componentDidMount() {
            // TODO call the action to add dynamic manifest file to index.html
            this.props.dispatch(aapActions.getAppDetails());
        }

        render() {
            return (
                ................
            );
        }
}

I am new to this kind of thing. How can I get started?

Share Improve this question edited Aug 18, 2023 at 10:45 Sanjeet kumar 3,4413 gold badges18 silver badges27 bronze badges asked Oct 25, 2018 at 20:09 Erlinda SantiagoErlinda Santiago 311 gold badge1 silver badge2 bronze badges 1
  • 1 It may be hard for people to help here, since they may not be sure where you are stuck. Did you try the task you describe, and had some sort of difficulty? If you can narrow the issue down, that might help people help you. – halfer Commented Oct 30, 2018 at 20:50
Add a comment  | 

5 Answers 5

Reset to default 13

if you want to create dynamic manifest.json you want to have a link tag in the HTML with rel="manifest" but without href attribute. And use this tag later to populate your manifest. As in

<!DOCTYPE html>
    <html>
      <head>
        <meta charset="utf-8">
        <title>title</title>
        <link rel="manifest" id="my-manifest-placeholder">
      </head>
      <body>
        <!-- page content -->
      </body>
    </html>

Use a JSON object to set your manifest

var myDynamicManifest = {
  "name": "Your Great Site",
  "short_name": "Site",
  "description": "Something dynamic",
  "start_url": "<your-url>",
  "background_color": "#000000",
  "theme_color": "#0f4a73",
  "icons": [{
    "src": "whatever.png",
    "sizes": "256x256",
    "type": "image/png"
  }]
}
const stringManifest = JSON.stringify(myDynamicManifest);
const blob = new Blob([stringManifest], {type: 'application/json'});
const manifestURL = URL.createObjectURL(blob);
document.querySelector('#my-manifest-placeholder').setAttribute('href', manifestURL);

Unfortunately, the advice on using blob: or data: URLs fails to work correctly in some cases, because manifest.json URLs are supposed to be static:

Do not change the name or location of your web app manifest file, doing so may prevent the browser from updating your PWA.

As that article explains, there's a special mechanism for updates to manifest.json (triggered by very specific conditions) - it is not supposed to be updated via the URL name.

Moreover, Android may no longer recognize the app as a full (WebAPK-installable) PWA, even when the app passes the Lighthouse check. In that case, it's still "installed" as a regular PWA with an ugly Chrome icon in the corner of the app launch icon. I've spend many hours trying to understand why, and finally stumbled on that article with the explanation.

So please either

  1. Serve dynamic manifest.json from a completely static URL, or
  2. Determine href based on some attribute (e.g. hostname) that doesn’t change over the lifetime of the app (so that different variations of manifest could be served for different clients, but each client’s version would have a static URL).

Inspired from @Sanjeet kumar's solution for my NextJs application:

// _document.tsx
<link href="/manifest.json" rel="manifest" id="manifest" />


// _app.tsx
import manifest from "../../public/manifest.json";

const manifestElement = document.getElementById("manifest");
const manifestString = JSON.stringify({
  ...manifest,
  start_url: `${homePagePath}${storeCode}`,
});
manifestElement?.setAttribute(
  "href",
  "data:application/json;charset=utf-8," + encodeURIComponent(manifestString)
);

I was facing some issues with the Blob, It was not working in android as expected. The below answer works fine.

 var myDynamicManifest = {
  "name": "Your Great Site",
  "short_name": "Site",
  "description": "Something dynamic",
  "start_url": "<your-url>",
  "background_color": "#000000",
  "theme_color": "#0f4a73",
  "icons": [
      {
        src: "icon_size_36.png",
        sizes: "36x36",
        type: "image/png",
      },
      {
        src: "icon_size_48.png",
        sizes: "48x48",
        type: "image/png",
      },
      {
        src: "icon_size_72.png",
        sizes: "72x72",
        type: "image/png",
      },
      {
        src: "icon_size_96.png",
        sizes: "96x96",
        type: "image/png",
      },
      {
        src: "icon_size_144.png",
        sizes: "144x144",
        type: "image/png",
      },
      {
        src: "icon_size_192.png",
        sizes: "192x192",
        type: "image/png",
      },
      {
        src: "icon_size_512.png",
        sizes: "512x512",
        type: "image/png",
      },
    ]
  }
  const link = document.createElement("link");
  link.rel = "manifest";    
  const stringManifest = JSON.stringify(myDynamicManifest);
  link.setAttribute('href', 'data:application/json;charset=utf-8,' + encodeURIComponent(stringManifest))
  document.head.appendChild(link);

Starting with a <link rel="manifest"> and setting a href from javascript works, with both data: and blob: URLs.

Known issues:

  • relative paths for start_url, scope and icons won't work. They'll be interpreted as relative to the blob: or data: urls, which is invalid. Symptom: Manifest: property 'start_url' ignored. Solution: build absolute URL, e.g. for URLs with the same origin as the frontend, I've used
    new URL(`/my/absolute/path/?evensomeparameter=${valueVariable}`,
   window.location.origin).href
  • Content-Security-Policy may prevent the manifest from being loaded from blob: or data:. Symptom: in the console, you'll find errors stating refused to load manifest from 'blob:https://my.domain.com/blah' because it violates the following Content Security Policy directive:. Solution: you can enable manifest-src blob: or manifest-src data: in the Content Security Policy. This comes either from a response header - see load balancer / CDN / server config - , or from a <meta head directive on your page https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy
  • there's an issue last updated in 2022 stating that Firefox on Android only read the manifest on document load, so setting the href with a delay won't work https://github.com/mozilla-mobile/fenix/issues/16672

Just for completeness sake, an alternative: take advantage of session data stored in cookies and generate the manifest server-side. But take care of allowed domains - see Content-Security-Policy above. And figure out where you want the static assets like icons and splash screen to be served from. Prefer absolute URLs.

https://medium.com/limehome-engineering/create-a-pwa-app-manifest-dynamically-spa-angular-1627260e0390

发布评论

评论列表(0)

  1. 暂无评论