Improve the links to your site with Next js

No more boring links when sharing your website

The Goal

I always found cool when a site has a link preview when you share the url somewhere. It's a really simple thing to add, but I feel it brings a lot to your site.

Let's say you have a countries website and you want to share the link for the country of Bolivia, so you share the link on discord.

We are going from this

Screen Shot 2022-04-09 at 9.00.08 AM.png

to this

Screen Shot 2022-04-09 at 8.39.30 AM.png

App Setup

For this project we will use my favorite react meta framework: Next js!

Let's create a new next js app with the following command

npx create-next-app@latest countries-seo --typescript
# or
yarn create next-app countries-seo --typescript

The only library we will be using for this guide is next-seo, to make the SEO management easier.

Let's install it, on the root folder run the following command.

npm install next-seo
# or
yarn add next-seo

We need to get all this country data from somewhere, let's go with this nice countries api for now.

One small but important thing before we continue, in order for the images from the api to render in our app, add the api domain to the next.config.js file.

/** @type {import('next').NextConfig} */

module.exports = {
  images: {
    domains: ["flagcdn.com"],
  },
  reactStrictMode: true,
};

What will the app do?

We are gonna make an app that displays information about a country based on the url. This will be a dynamic route, because we don't want to make a route for every country in the world.

In order to make the SEO dynamic, we need our data to come from the server. We can do this using using either getServerSideProps or getStaticProps, since the data we are dealing with is not gonna change, let's go with getStaticProps.

For a non dynamic route, the SEO can be static, normally you won't need any data fetching on the server side for those cases

If these words don't sound familiar I recommend learning a bit more about next js and its data fetching methods before continuing with this guide.

It's important to use one of these data fetching approaches since it would make the data available wherever we use paste our links, since it will be server side. If we were to fetch on the client, we would not be able to have dynamic link previews like we are going to implement now.

Creating the Component

Let's create a simple component that will render country information. In order to do this we need a dynamic route, inside the pages folder create another folder called country with a [name].tsx file inside and add the following content.

import Image from "next/image";

import type { GetStaticProps } from "next";

interface Country {
  country: {
    name: {
      common: string;
    };
    flags: {
      png: string;
    };
    capital: string;
  };
}

const Country = ({ country }: Country) => {
  if (!country) {
    return (
      <div>
        <p>Country not found</p>
      </div>
    );
  }
  return (
    <div>
      <p>{country?.name?.common}</p>
      <Image
        alt={country.name.common}
        src={country.flags.png}
        height={213}
        width={320}
      />
    </div>
  );
};

Fetching the Data

Now we need to inject the country information to the component. Since this is a dynamic route that will use getStaticProps we need to add a getStaticPaths function to generate a list of paths. The countries api has an endpoint that lists all the countries available, which is perfect for our case, since those will be all our possible paths.

Below the component add the following

export const getStaticPaths = async () => {
  const res = await fetch("https://restcountries.com/v3.1/all");

  const data = await res.json();

  return {
    paths: data?.map((country: { name: { common: string } }) => {
      return {
        params: {
          name: country?.name?.common?.toLowerCase()?.replace(/ .*/,'')
        },
      };
    }),
    fallback: true,
  };
};

Boom, this will generate all the paths for our dynamic route.

The logic to get the country name is not perfect and it will probably miss some cases, but it will work for most countries and for the purpose of this tutorial

Now that we have our paths, create the getStaticProps function. This function will inject the country data to the component.

export const getStaticProps: GetStaticProps = async ({ params }) => {
  try {
    const res = await fetch(
      `https://restcountries.com/v3.1/name/${params?.name}`
    );

    const results = await res.json();
    return {
      props: {
        country: results?.[0] ?? null,
      },
    };
  } catch (error) {
    return {
      props: {
        country: null,
      },
    };
  }
};

We are getting the country name from the url params, and try to perform an api call that will get the country information if the name is valid. If all good we pass the country as a prop to the component or null if the api call fails.

SEO

Finally, we will use next-seo to generate the dynamic link preview. next-seo helps us create meta tags, you could totally do this without it but with the library is a bit easier. I encourage to explore the next-seo docs to see all the options and functionality available, for now we will add a simple configuration that will show the name of the country, capital and flag.

Add the next-seo component:

const Country = ({ country }: Country) => {
  if (!country) {
    return (
      <div>
        <p>Country not found</p>
      </div>
    );
  }
  return (
    <div>
      <p>{country?.name?.common}</p>
      <Image
        alt={country.name.common}
        src={country.flags.png}
        height={213}
        width={320}
      />
      <NextSeo
        openGraph={{
          type: "website",
          title: country.name.common,
          description: `Capital ${country.capital}`,
          images: [
            {
              url: country.flags.png,
              width: 320,
              height: 213,
              alt: country.name.common,
            },
          ],
        }}
      />
    </div>
  );
};

Wrapping up

And we are done!

Your pages/country/[name].tsx file should look like this

import { NextSeo } from "next-seo";
import Image from "next/image";

import type { GetStaticProps } from "next";

interface Country {
  country: {
    name: {
      common: string;
    };
    flags: {
      png: string;
    };
    capital: string;
  };
}

const Country = ({ country }: Country) => {
  if (!country) {
    return (
      <div>
        <p>Country not found</p>
      </div>
    );
  }
  return (
    <div>
      <p>{country?.name?.common}</p>
      <Image
        alt={country.name.common}
        src={country.flags.png}
        height={213}
        width={320}
      />
      <NextSeo
        openGraph={{
          type: "website",
          url: "https://www.example.com/page",
          title: country.name.common,
          description: `Capital ${country.capital}`,
          images: [
            {
              url: country.flags.png,
              width: 320,
              height: 213,
              alt: country.name.common,
            },
          ],
        }}
      />
    </div>
  );
};

export const getStaticProps: GetStaticProps = async ({ params }) => {
  try {
    const res = await fetch(
      `https://restcountries.com/v3.1/name/${params?.name}`
    );

    const results = await res.json();
    return {
      props: {
        country: results?.[0] ?? null,
      },
    };
  } catch (error) {
    return {
      props: {
        country: null,
      },
    };
  }
};

export const getStaticPaths = async () => {
  const res = await fetch("https://restcountries.com/v3.1/all");

  const data = await res.json();

  return {
    paths: data?.map((country: { name: { common: string } }) => {
      return {
        params: {
          name: country?.name?.common?.toLowerCase()?.replace(/ .*/, ""),
        },
      };
    }),
    fallback: true,
  };
};

export default Country;

Testing

We can't really test this unless we deploy our app. Next js is a bit special, it requires a bit more to deploy than you normal client side app if you are using any of the SSR features. Luckily services like Vercel or Netlify (with the next js plugin) make it really easy to deploy our app with all its features for free (for side projects).

Make an account if you don't have one, deploy your app and let's see how the links look.

Now let's test with Fiji!

Share this link somewhere

${Your website's url}/country/fiji

The flag of Fiji should appear

Screen Shot 2022-04-09 at 9.15.10 AM.png

Looks great!

Now go make all your links amazing!

Thank you for reading, if you found this useful please like and share :)