How to add SEO Meta tags in your Next.js apps

How to add SEO Meta tags in your Next.js apps

SEO is kind of like a buzzword when it comes to building websites or web applications these days. Early-stage startups are most of the time, advised to invest so much in the marketing (SEO) of their products so that they can get to as many people at a time.

But now, you might be wondering... "I am not a startup founder, neither am I building a ravishing awesome product! why should I care about SEO?". Well... I'm here to let you know that SEO has a lot of benefits to offer you when you apply it to your side projects or gigs.

In this article, we are going to see how we can implement this in a Next.js application. Since, Next.js, as you already know, or may not know, supports SEO out of the box, implementing this wouldn't be much of a problem. Now, let's get started, shall we?

Getting started

As I mentioned in the preamble of this article that Next.js has an out-of-the-box support for SEO. "How?" you may ask me. Here's the thing... Next.js operates with a file architecture that is quite different from that of create-react-app, by providing a pages folder for you, such that any file you place in that folder automatically becomes a route that can be accessed by anyone.

pages/index.js -> // becomes accessible through this pathname "/"
pages/contact.js -> // becomes accessible through this pathname "/contact"
pages/about.js -> // becomes accessible through this pathname "/about"
pages/blog.js -> // becomes accessible through this pathname "/blog"

With that in place, Next.js also provides a <Head /> component that allows you to be able to pass meta tags in your page files or routes. It can be achieved by importing it from the built-in library.

import Head from "next/head"

export default function BlogPage () {
  return (
     <React.Fragment>
         <Head>
           <title>My blog</title>
         </Head>
         <h1>This is my blog page</h1>
     </React.Fragment>
  )
}

And this same pattern, in the snippet above, will be repeated in all of your page routes.

Adding open-graph meta tags

Open graph tags, No, there are no such things as open-graph tags. I think they are basically just meta tags with open-graph attributes assigned to them. Take a look at an example below.

<meta property="og:title" content="My blog" key="ogtitle" />
<meta property="og:description" content="this is my blog" key="ogdesc" />

These meta tags control how the links of, say your portfolio website or a blog post you wrote somewhere are displayed when you share them on social media platforms.

These are the current list of [og]:open-graph meta tags that are available below.

<meta property="og:title" content="this is my title" key="ogtitle" />
<meta property="og:description" content="a description" key="ogdesc" />
<meta
    property="og:image"
    content="path/to/image"
    key="ogimage"
/>
<meta
    property="og:site_name"
    content="name-of-your-site"
    key="ogsitename"
/>
<meta
    property="og:url"
    content="https://your-site-name/"
    key="ogurl"
/>
<meta property="og:type" content="article" key="ogtype" />

In a typical Next.js app that uses the data fetching methods to parse, or read through markdown files, we can obtain certain information about our arbitrary articles/blog posts and then, have them passed down as props to the desired components.

export async function getStaticProps() {
  // obtain blog posts from the "/posts" directory
  const files = fs.readdirSync(path.join("articles"));

  // getting the frontmatter/blog post excerpt from
  // the posts directory in each file
  const posts = files.map((filename) => {
    const slug = filename.replace(".md", "");

    // obtaining the frontmatter
    // it is the excerpt of tha article/blog post, that'll contain
    // the title of the article, the author's name,
    // the date it was published and so on.
    const articlePreview = fs.readFileSync(
      path.join("articles", filename),
      "utf-8"
    );

    // destructuring graymatter by assigning 'frontmatter'
    // to the 'data' key
    const { data: frontmatter } = matter(articlePreview);

    return {
      slug,
      frontmatter,
    };
  });

  return {
    props: {
      posts,
    },
  };
}

The snippet above shows how we're using Node.js' file system (fs) function to get all the markdown files in the "articles" directory of our codebase, parsing the markdown with matter from the "gray-matter" library, and destructuring the frontmatter variable, which is then passed as props to the desired page.

Take a look at the snippet below;

export default function BlogPage({ posts }) {
  return (
    <React.Fragment>
      <div className="blog-layout">
        {posts.map(post => {
           return <Blog articles={posts} />
        })}
      </div>
    </React.Fragment>
  );
}

// blog component
export const Blog = ({ data }) => {
  return (
    <>
      <Link href={`/blog/${data.slug}`}>
        <div className="blog-card">
          <h1 className="title">{data.title}</h1>
          <p className="summary">{data.excerpt}</p>
          <p className="date">
            {dayjs(data.publishedAt).format('MMMM D, YYYY')}
          </p>
        </div>
      </Link>
    </>
  )
}

Adding twitter cards meta tags

The Twitter cards are just some bunch of meta tags with the twitter:{attribute} attribute attached to the meta tag. Twitter cards will display the appropriate information about the links you share on Twitter.

With these links absent, your content won't render properly on Twitter. Open-graph tags only work on platforms like Facebook, Instagram, WhatsApp, LinkedIn, and many more that I am not aware of.

There are four types of twitter cards, and they are:

  • Summary Card: text with a square image.
  • Summary Card with Large Image:
  • App Card: used for promoting apps.
  • Player Card: used for showing videos.

I'd recommend that you always make use of the "summary card with large image" if you want something like this, below.

Screenshot from 2022-04-10 17-44-29.png

using the twitter:card in a unique blog post

To continue from where we stopped in the previous snippets that illustrate the Next.js blog using the built-in data-fetching methods. We'd need to have a way of showing a unique article that has been clicked upon by a user

// -> [slug].js

// dynamically generate the slugs for each article(s)
export async function getStaticPaths() {
  const files = fs.readdirSync(path.join("articles"));

  // getting all paths of each article as an array of
  // objects with their unique slugs
  const paths = files.map((filename) => ({
    params: {
      slug: filename.replace(".md", ""),
    },
  }));

  return {
    paths,
    // // in situations where you try to access a path
    // // that does not exist. it'll return a 404 page
    fallback: false,
  };
}

// destructuring params to get the unique slugs
export async function getStaticProps({ params: { slug } }) {
  //fetch the particular file based on the slug
  const articles = fs.readFileSync(
    path.join("articles", `${slug}.md`),
    "utf-8"
  );

  const { data: frontmatter, content } = matter(articles);
  return {
    props: {
      frontmatter,
      content,
      slug,
    },
  };
}

The snippet above shows how we're using the data-fetching methods of Next.js to get the unique id/slug of a markdown file in our "articles" directory and rendering that content on a statically generated path that is the same as the slug.

// [slug].js

const UniqueArticle = ({
  slug,
  frontmatter: { title, excerpt },
  content,
}) => {
  return (
    <React.Fragment>
      <Head>
        <title>{title} | My Blog</title>
        <meta name="twitter:card" content="summary_large_image" />
        <meta name="twitter:title" content={title} />
        <meta name="twitter:description" content={excerpt} />
        <meta
          name="twitter:image"
          content={`https://your-website.com/${cover_image}`}
        />
      </Head>
      <div className="content">
          <h1 className="article-title">{title}</h1>
          <div className="article-body">
            <div dangerouslySetInnerHTML={{ __html: marked(content) }}></div>
          </div>
      </div>
    </React.Fragment>
  );
};

export default UniqueArticle;

Wrapping up

Now that you have seen the usefulness of SEO, and how it can be used in a static blog, I hope that you will begin to adopt this practice henceforth.

Should in case you want to use my code snippets, I'll have to let you know that you'd be needing some npm packages in your project for it to work fine. Here they are below;

  • dayjs: 2KB immutable date time library alternative to Moment.js with the same modern API
  • gray-matter: A library used for parsing front-matter from a string or file
  • marked: low-level compiler for parsing markdown without caching or blocking for long periods of time