Improve the links to your site with Next js
Tags: javascript, react, nextjs, seo
Published: 2022-04-09 07:51:46

### 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](https://cdn.hashnode.com/res/hashnode/image/upload/v1649462431220/KmIeTElHY.png)

to this

![Screen Shot 2022-04-09 at 8.39.30 AM.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1649461189535/jrHgDE7EH.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

```bash
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.
```bash
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](https://restcountries.com/) 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.

```js
/** @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.

```jsx
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
```ts
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.

```ts
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:

```jsx
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

```jsx
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](https://cdn.hashnode.com/res/hashnode/image/upload/v1649463324246/6Ql3nj7yx.png)

Looks great!

Now go make all your links amazing!

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