Creating dynamic social media cards for SEO in Next.js

Sometime back, I switched to working with Next Js for a client that needed Server-side rendering (SSR) and Static Site Generation (SSG) on their website. During this switch, I noticed that unlike in Gatbsy I shouldn’t be relying on React Helmet to implement meta tags to help with creating dynamic social media sharing cards as the latest version of Next already has a next/head library. Next Js, however,  didn’t have much documentation on how to go about creating an SEO component that might help with generating dynamic meta tags for the pages as with Gatsby. This, to be honest, was a bit of a bummer, which is why after some research and inspiration from Cassidy at Netlify I set out to create a component modelled after what Gatbsy has.

As much as we front-end devs love and admire the work that goes into creating a great looking website using a framework like Next JS, it’s not going to see much daylight if the bots that crawl the internet cannot crawl and index it due to a lack of meta tags. This lack of meta tags is a serious threat to the discoverability of your website which is why you would often find the Lighthouse audit screaming red. This isn’t even the worst of it as anytime a user tries to share your meta-tag-less website, it will look horrible as you can see in my cover picture for this blog post.

Okay, now that I’ve impressed the need for you to put some effort into your meta tags, let’s see how we can do this in Next Js. For this I assume you already have a working Next Js app and is looking to fetch data from a CMS.

Create a config.js with the below code. I usually have this in components/global. The purpose of having this is to serve the default content into the SEO component we will be creating next. If you have experience working with Gatsby, you will notice that this is similar to the siteMetadata in the gatsby-config.js

1export default {
2  originalTitle: "Na Moje Wyspie",
3  currentURL: "https://namojejwyspie-new.vercel.app",
4  originalImage: "https://i.ibb.co/NtDBtQd/image-3.jpg",
5  author: {
6    name: "Michalina",
7  },
8  originalDescription:
9    "Blog on cuisine, history and everything you need to know about Sri Lanka written by a huge fan of Sri Lanka ",
10  social: {
11    twitter: "Michalina",
12  },
13  siteName: "Na Mojej Wyspie",
14};
15

Now we create SEO.js in the component/global as this is where the meta tags are hosted. You can see that this component is expecting certain props such as description, title, image, slug, article to be provided to it. In the event of a lack of those props, it will default to the ones that are provided from the config.js file we created earlier. 

1import Head from "next/head";
2import config from "../globals/config";
3export default function SEO({ description, title, image, slug, article }) {
4  const {
5    originalTitle,
6    originalDescription,
7    siteName,
8    social: { twitter },
9    currentURL,
10    originalImage,
11  } = config;
12  return (
13    <Head>
14      <meta name="viewport" content="width=device-width, initial-scale=1" />
15      <meta charSet="utf-8" />
16      <title>{`${title} | ${originalTitle}`}</title>
17      <meta
18        name="description"
19        content={`${description ? description : originalDescription}`}
20      />
21      <meta
22        name="image"
23        content={`${image ? image : originalImage}`}
24        key="ogtitle"
25      />
26      {article ? (
27        <meta property="og:type" content="article" key="ogtype" />
28      ) : (
29        <meta property="og:type" content="website" key="ogtype" />
30      )}
31      <meta
32        property="og:title"
33        content={`${title ? title : originalTitle}`}
34        key="ogtitle"
35      />
36      <meta
37        property="og:description"
38        content={`${description ? description : originalDescription}`}
39        key="ogdesc"
40      />
41      <meta
42        property="twitter:card"
43        content="summary_large_image"
44        key="twcard"
45      />
46      <meta name="twitter:creator" content={twitter} key="twhandle" />
47      <meta
48        name="twitter:title"
49        content={`${title ? title : originalTitle}`}
50        key="twtitle"
51      />
52      <meta
53        name="twitter:description"
54        content={`${description ? description : originalDescription}`}
55        key="twdescription"
56      />
57      <meta
58        name="twitter:image"
59        content={`${image ? image : originalImage}`}
60        key="twimage"
61      />
62      <meta property="og:url" content={`${currentURL}/${slug}`} key="ogurl" />
63      <meta
64        property="og:image"
65        content={`${image ? image : originalImage}`}
66        key="ogimage"
67      />
68      <meta property="og:site_name" content={siteName} key="ogsitename" />
69    </Head>
70  );
71}

Once the above is done, all we have to is import the SEO component into our layout component. We do this as the Layout component wraps around all the pages in our app and is an excellent place to feed dynamic meta tags, as it can then, in turn, be passed to the SEO component.

1import React from "react";
2import Footer from "./footer/Footer";
3import Navbar from "./navbar/Navbar";
4import SEO from "./SEO";
5const Layout = ({ children, title, description, image, slug, article }) => {
6  return (
7    <React.Fragment>
8      <SEO
9        title={title}
10        description={description}
11        image={image}
12        slug={slug}
13        article={article}
14      />
15      <Navbar />
16      <main>{children}</main>
17      <Footer />
18    </React.Fragment>
19  );
20};
21export default Layout;
22
Take a look at how I am adding meta tags dynamically to the Layout component which in turn passes it onto the SEO component. 

1import React from "react";
2import Layout from "../../components/globals/Layout";
3// Initialising my CMS clients here
4export async function getStaticPaths() {
5  // fetching data from CMS before creating paths and params for them
6}
7export async function getStaticProps({ params }) {
8  // using params to fetch the content for this particular page and feed it to the Index component here
9}
10const Index = ({ post }) => {
11  const { title, image, slug, description } = post;
12  return (
13    <Layout title={title} image={image} slug={slug} description={description}>
14      {/* Other components here */}
15    </Layout>
16  );
17};
18export default Index;

And that is all there is to it folks! I hope you managed to get this working as optimising your meta tags is crucial for good SEO and a great overall UX if a user of your website decides to share it across their social media channels. 

You might also want to read