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.
You have this error because browsers expect some files to be served from the root of the server, including:
/manifest.json
/sitemap.xml
/favicon.ico
/robots.txt
/browserconfig.xml
/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.
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.
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