Next.js doesn’t come SEO optimized and you need to do some things on your own to make sure that your Next.js based blog or website is properly optimized to be indexed and ranked well in Search Engines.
The Problem
First, what is canonical URL or canonical tag?
Canonical URL is a URL which tells search engines the original source of a particular page. Let’s take the example of this URL:
https://kizie.co
This is a straightforward URL. So when Google
crawls this link it would know that
the source and canonical of the page is same,
which is https://kizie.co
.
The problem arises in cases when there are some query with the URL or when there are UTM parameters. Whole lot of sites like to add UTM parameters to external links and for various reasons.
The same URL with UTM parameters can look like this:
https://kizie.co/?ref=vercel.com
In this case, the search engine would consider the canonical URL of this page same as the original URL which also includes the UTM parameters.
Which means that search engine might consider these:
https://kizie.co
https://kizie.co/?ref=vercel.com
as two different sources or URLs. But we know both these URL are same. The use of canonical URL is to tell search engines that these two URLs are same and that search engines should consider these as same and not different sources.
Setting up canonical
tag in Next.js
Adding canonical
tag in Next.js is pretty
simple. There are two steps to it:
- Determining the canonical URL
- then Adding it in
Head
Determining the canonical URL in Next.js apps
People who have used Next.js might think
this as an easy thing to do, you just
import the router
and then use router.pathname
to figure out the canonical URL.
// URL: https://kizie.co/example
const canonical = `https://kizie.co` + router.pathname;
// canonical will be https://kizie.co/example
Thats it, no? The first search result’s most upvoted answer says so. But it’s, wrong!
Although the above setup would work in most cases, it won’t work in sites that use ISR (Incrementally statically generated pages) or have dynamically generated routes. In ISR blogs and dynamically generated setups, if you access:
// URL: https://kizie.co/example/isr-generated-page
const canonical = `https://kizie.co` + router.pathname;
// canonical will be https://kizie.co/example/[slug]
Certainly https://kizie.co/example/[slug]
is not the
canonical for this page. The issue is that router.pathname
doesn’t know the actual slug of statically or dynamically
generate pages.
The solution
The solution is to use router’s asPath
because it works even on dynamically generated pages.
// URL: https://kizie.co/example/isr-generated-page
const canonical = `https://kizie.co` + router.asPath;
// canonical will be https://kizie.co/example/isr-generated-page
And to make sure the canonical URL doesn’t contain any UTM parameters, we can use the following.
// URL: https://kizie.co/example/isr-generated-page?ref=vercel.com
const canonical = (`https://kizie.co` + router.asPath).split("?")[0];
// canonical will be https://kizie.co/example/isr-generated-page
Basically, we are just stripping off the UTM parameters part in the URL.
Adding canonical
tag in Head
For this I highly recommend to use NextSEO
npm library since it makes really easy not just
to add canonical tag but also other tags like title
,
description
and Open Graph tags.
Open the pages/_app.js
file in your Next.js project
and make the changes like below. Also make sure to change
kizie.co
with your site’s domain name.
import { useRouter } from "next/router";
import { DefaultSeo } from "next-seo";
function MyApp({ Component, pageProps }) {
const router = useRouter();
const canonicalUrl = (`https://kizie.co` + (router.asPath === "/" ? "": router.asPath)).split("?")[0];
return (
<>
<DefaultSeo
canonical={canonicalUrl}
/>
// Other stuff
</>
);
}
export default MyApp;
If you don’t want to use NextSEO, you can also use
Next.js’s Head
to add the canonical tag like so:
import { useRouter } from "next/router";
import Head from "next/head";
function MyApp({ Component, pageProps }) {
const router = useRouter();
const canonicalUrl = (`https://kizie.co` + (router.asPath === "/" ? "": router.asPath)).split("?")[0];
return (
<>
<Head>
<link rel="canonical" href={canonicalUrl} />
</Head>
// Other stuff
</>
);
}
export default MyApp;
The above will make sure you have proper canonical URL in all the pages of your sites, even the pages which are generated through ISR or SSR.