This blog is part of a series where I document rebuilding a website that relies on HTML, CSS and Bootstrap in React.js using the Next.js framework to improve performance, reduce costs and increase my workflow for future changes.
The finished website: https://wallisconsultancy.co.uk
The source code: https://github.com/james-wallis/wallisconsultancy
Want to track users of your website in real time? Check out my post about Google Analytics and how to use it with Next.js.
Intro
For a website to rank highly on Google and have the best chance of generating business, it needs to perform well and be SEO friendly. Fortunately, Next.js has two plugins that can help take our website to the next level.
Now that the Wallis Consultancy website has been re-built it’s now time to focus on the finer details which are:
- SEO specifics (Page titles, descriptions, Open Graph)
- Image Optimisation (Ensure images are as small as possible, load smaller images on mobile, decrease load times)
We can achieve this easily with Next.js and two third party modules.
These are:
With the release of Next.js 10 a built-in image optimisation component has released. Checkout my article where I briefly compare it with next-optimized-images.
next-seo
Next-seo provides the ability to configure SEO focused fields, such as title, description and canonical URL that are found in the <head>
of a HTML document.
It works by adding a <NextSEO>
component into a page and passing it props. An example of this for title and description:
import React from ‘react’;
import { NextSeo } from ‘next-seo’;
export default () => (
<>
<NextSeo
title=“Simple Usage Example”
description=“A short description goes here.”
/>
<p>Simple Usage</p>
</>
);
For some attributes, they don’t change on a page by page basis so next-seo provides the DefaultSeo
component that can be added to a Next.js _app.js
. An example of this is using the twitter
and openGraph
props:
import App, { Container } from ‘next/app’;
import React from ‘react’;
import { DefaultSeo } from ‘next-seo’;
export default class MyApp extends App {
render() {
const { Component, pageProps } = this.props;
return (
<>
<DefaultSeo
openGraph={{
type: ‘website’,
locale: ‘en_IE’,
url: ‘https://www.url.ie/‘,
site_name: ‘SiteName’,
}}
twitter={{
handle: ‘@handle’,
site: ‘@site’,
cardType: ‘summary_large_image’,
}}
/>
<Component {…pageProps} />
</>
);
}
}
For wallisconsultancy.co.uk the default next-seo is:
<DefaultSeo
titleTemplate={‘%s | Wallis Consultancy’}
openGraph={{
type: ‘website’,
locale: ‘en_IE’,
url: ‘https://wallisconsultancy.co.uk/‘,
site_name: ‘Wallis Consultancy’,
}}
/>
next-optimized-images
This package optimises all images in a Next.js application by using require('filepathToImage')
as the value of the src
attribute in an img
tag. It is able to handle jpeg, png, svg, webp and gif file types and can also enable progressive loading and inline small images in addition to reducing their size between 20% to 60%.
As described in the documentation, you need to install additional packages to next-optimized-images
to take advantage of its optimisation power. As wallisconsultancy.co.uk contains JPG images I also installed imagemin-mozjpeg
, webp-loader, responsive-loader
and sharp
. The latter two packages enable the ability to resize images into multiple sizes at build time - meaning mobiles can be sent smaller images than desktops resulting in faster load times on slower, mobile networks. The webp-loader
package is a tool that will generate a webp
type copy of the initial jpg
image to be shown on devices that support it. webp
is a next generation file type that has superior file compression to jpg
making them smaller in size and faster to load onto the page
An example use for the image on the wallisconsultancy.co.uk homepage is:
import Layout from ‘../components/layout’
export default function IndexPage() {
return (
<Layout pageTitle=“Home”>
<div className=“flex flex-col md:flex-row”>
<div className=“w-100 md:w-2/3”>
...content
</div>
<div className=“w-100 md:w-1/3 flex items-center justify-center”>
<picture>
<source srcSet={require('../images/mike-wallis2.jpg?webp')} type="image/webp" />
<source srcSet={require('../images/mike-wallis2.jpg')} type="image/jpeg" />
<img
src={require('../images/mike-wallis2.jpg')}
alt="Mike Wallis"
/>
</picture>
</div>
</div>
</Layout>
)
}
This will create 2 compressed copies of the image, jpg
and webp
. The device will attempt to select webp
but fallback to jpg
if it isn't supported. On a device where the picture
tag is not supported, the img
tag will be used.
Google Lighthouse
Lighthouse is an open-source, automated tool for improving the quality of web pages. You can run it against any web page, public or requiring authentication. It has audits for performance, accessibility, progressive web apps, SEO and more.
Before adding the packages used in this blog, the performance score for Wallis Consultancy was around 70 (mainly as the images sizes were not optimised and not using next generation formats such as webp
) and the SEO was 60.
After adding the SEO fields and optimising all the images on the website it achieved an almost perfect Google Lighthouse score:
Roundup
This blog demonstrated the power of two third party Next.js packages next-seo
and next-optimised-images
. The former will ensure all the pages are setup to rank highly on search engines and the latter makes each page load that little bit faster.
In the next blog, the final of the series, I’ll create a Travis pipeline which will deploy the website to Github pages.
Bonus:
Improving website SEO and performance is a great way to gain more users. Once this is done it’s a good idea to track them and see how real people use your website. Let me take you through adding Google Analytics to your Next.js app to be able to see this information and more.
Top comments (2)
Hi James, can you please share how you loaded in the optimization plugins into your nextjs application?
Hi, for image optimisation it's here: github.com/james-wallis/walliscons...
Another example is here which just uses the default values (passes an empty object) github.com/james-wallis/ameira.me/...
Some comments have been hidden by the post's author - find out more