Next.js PWA (Service Worker + Manifest.json)

Sofienne Lassoued picture Sofienne Lassoued · Jul 31, 2018 · Viewed 10.6k times · Source

I am using Next.js to develop a Server Side Rendering website and I want to make it a Progressive Web App but the problem I couldn't find the way to make it happen correctly.

When I build the application it serves correctly the service worker but there is no manifest.json and in some projects examples it serves the manifest.json but I tried it in Lighthouse audit and it says

Service worker does not successfully serve the manifest's start_url

One of the examples I used Create Next App With Service Worker Precache

I think that the problem is that the start_url is . or / and not a valid file because in Next.js there is no index.html to serve from the start.

In summary I am looking for an example using Next.js to build it to a dist folder and when I serve it it has a valid Service Worker and a valid Web Manifest.

Answer

Clément Prévost picture Clément Prévost · Sep 9, 2018

A. Some file are expected to be found at "/"

You have this error because browsers expect some files to be served from the root of the server, including:

  1. /manifest.json
  2. /sitemap.xml
  3. /favicon.ico
  4. /robots.txt
  5. /browserconfig.xml
  6. /site.webmanifest

While the majority of these paths can be set with meta tags, older browsers just ignore them and error if these exact file names are not served.

B. Configure alternative paths and use NextJS static file

At the time of writing, there is ongoing work for supporting offline in NextJS. But it's not ready yet.

If you don't need to support older browsers and you don't want advanced SEO, you can use NextJS's Head component (see documentation) to define the manifest.json path like you would for any NextJS static file:

import Head from "next/head"

export default () => (
    <Head>
        <link rel="manifest" href="/static/manifest.json" />
        <link rel="manifest" href="/static/site.webmanifest" />
        <link rel="shortcut icon" href="/static/favicon.ico"
    </Head>
)

Please note that robots.txt cannot be serve from a subdirectory (source), so this solution is not a good fit if you need to define this file.

C. Serve these files like expected

The proper solution would be to serve these files from your express server like so

const { createServer } = require('http')
const { parse } = require('url')
const next = require('next')
const { join } = require('path')

const port = parseInt(process.env.PORT, 10) || 3000
const dev = process.env.NODE_ENV !== 'production'
const app = next({ dev })
const handle = app.getRequestHandler()

app.prepare()
  .then(() => {
    createServer((req, res) => {
      const parsedUrl = parse(req.url, true)
      const rootStaticFiles = [
        '/manifest.json',
        '/sitemap.xml',
        '/favicon.ico',
        '/robots.txt',
        '/browserconfig.xml',
        '/site.webmanifest',
      ]
      if (rootStaticFiles.indexOf(parsedUrl.pathname) > -1) {
        const path = join(__dirname, 'static', parsedUrl.pathname)
        app.serveStatic(req, res, path)
      } else {
        handle(req, res, parsedUrl)
      }
    })
      .listen(port, (err) => {
        if (err) throw err
        console.log(`> Ready on http://localhost:${port}`)
      })
  })

Note: This code directly comes from the NextJS examples repository