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
to this
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
Looks great!
Now go make all your links amazing!
Thank you for reading, if you found this useful please like and share :)
Top comments (0)