Today we will talk about how to get 100% on all four categories of Lightouse:

Progressive Web Apps

Performance

Accessibility

Best Practices

What is Lighthouse ?

Lighthouse is a tool introduced by Google IO in 2017. Lighthouse runs a barrage of tests against the page, and then generates a report on how well the page did. From here you can use the failing tests as indicators on what you can do to improve your app. The tool is very simple to use, we just need to open the developer console of Chrome, click on the Audit tab and start a new Audit. The result well be like this:

Let’s start to code

For this test we will use the npm create-next-app package which gives us a good starting structure

npm install -g create-next-app



create-next-app test-next-lighthouse

cd test-next-lighthouse/

yarn dev

And tadà:

Now let’s start with the first, and more difficult, step.

Progressive Web Apps

Progressive Web Apps, also known as Installable Web Apps or Hybrid Web Apps, are regular web pages or websites, but can appear to the user like traditional applications or native mobile applications. The application type attempts to combine features offered by most modern browsers with the benefits of mobile experience.

The foundamental points to resolve to get an high score are:

Redirect http traffic to https

Does not register a Service Worker

Does not respond with a 200 when offline

User will not be prompted to Install the Web App

Is not configured for a custom splash screen

Address bar does not match brand colors

Redirect http traffic to https

This is not a change to the NextJS code, we could also do it via NodeJS but I do not recommend it, it’s just a small change to our nginx virtualhost configuration

server {

listen 80;

server_name YOURSERVERNAME;

location / {

proxy_pass http://127.0.0.1:3000;

proxy_http_version 1.1;

proxy_set_header Upgrade $http_upgrade;

proxy_set_header Connection 'upgrade';

proxy_set_header Host $host;

proxy_cache_bypass $http_upgrade;

} listen 443 ssl;

ssl_certificate YOURCERTIFICATE/PATH

ssl_certificate_key YOURCERTIFICATE/PATH

ssl_dhparam YOURCERTIFICATE/PATH

return 301

} if ($scheme != "https") {return 301 https://$host$request_uri }

Does not register a Service Worker / Does not respond with a 200 when offline

To resolve this two points we will create a simple Service worker that caches the generated static files (js, css and images) to make our site available even a user return without an internet connection. To helps us we will use webpack and the sw-precache-webpack-plugin.

Let’s start to create the service worker

Under the folder utils we will create the offline.js , this file will be included into our main.js from webpack. The script is very simple:

if (

typeof window !== ‘undefined’ &&

‘serviceWorker’ in navigator

) {

navigator.serviceWorker

.register(‘/sw.js’)

.then(function(reg) {

console.log(‘Service worker registered‘);

})

.catch(function(e) {

console.error(‘Error during worker registration:’, e);

});

}

Now we can install the sw-precache-webpack-plugin with yarn add sw-precache-webpack-plugin --dev and edit our next.config.js to use this plugin in production:

const path = require(‘path’)

const SWPrecacheWebpackPlugin = require(‘sw-precache-webpack-plugin’) module.exports = {

webpack: (config, {dev}) => {

const oldEntry = config.entry

config.entry = () => oldEntry().then(entry => {

entry[‘main.js’].push(path.resolve(‘./utils/offline’))

return entry

})

if(!dev){

config.plugins.push(new SWPrecacheWebpackPlugin({

cacheId: ‘test-lighthouse’,

filepath: path.resolve(‘./static/sw.js’),

staticFileGlobs: [

‘static/**/*’

],

minify: true,

staticFileGlobsIgnorePatterns: [/\.next\//],

runtimeCaching: [{

handler: ‘fastest’,

urlPattern: /[.](png|jpg|css)/

},{

handler: ‘networkFirst’,

urlPattern: /^http.*/

}]

}))

}

return config

}

}

Inside the SWPrecacheWebpackPlugin we will define the:

Cache ID: used to save our cache into the cache storage

Filepath: used to save the effective sw file

staticFileGlob: The path of the static contents that we need to cache

staticFileGlobsIgnorePatterns: The paths of the files that we need to ignore from the cache

runtimeCaching: Define the type of the cache for all the file types

Now we need to edit the server.js file to serve our genereted sw.js in /sw.js

const { createServer } = require(‘http’)

const path = require(‘path’)

const next = require(‘next’)

const dev = process.env.NODE_ENV !== ‘production’

const app = next({ dir: ‘.’, dev })

const handle = app.getRequestHandler() const PORT = process.env.PORT || 3000 app.prepare().then(_ => {

const server = createServer((req, res) => {

if (req.url === ‘/sw.js’) {

app.serveStatic(req, res, path.resolve(‘./static/sw.js’))

} else {

handle(req, res)

}

})

server.listen(PORT, err => {

if (err) throw err

console.log(`> App running on port ${PORT}`)

})

})

Almost done, now we modify the scripts inside the package.json to use the server.js and start ( yarn start ) the application

...

“scripts”: {

“dev”: “node server”,

“prestart”: “next build”,

“start”: “NODE_ENV=production node server”

},

Open the developer console of Chrome on the Application tab and the service worker is magically registered