How to create a SEO-friendly website using NextJS & Notion API ?
Tags: webdev, nextjs, notionapi, tutorial
Published: 2022-07-30 19:22:00

Today, we will see how to create your own SEO-friendly static website which can be a blog, an ecommerce, a portfolio or whatever you want it to become !

**Live example here : [my personal portfolio](https://www.yacine-messaadi.com).**
All the content is fetched from Notion by NextJS.

**Source code here : [Github Repo (OpenSource)](https://github.com/YacineMessaadi/nextjs-notionapi).**
You can fork it, copy and do anything you want with this code, it's free !

## Setting up the project

### Create a Notion integration

Follow this link: [https://www.notion.so/my-integrations](https://www.notion.so/my-integrations) and create a new internal integration.

- Give it a name
- Select the right workspace to link
- Give it the ability to read your content

Then save it.

### Build your Notion database

Create a page in your Notion workspace, containing a Database.
You can put the properties you want and the lines you want, but never let an empty line !
For example, there is the database used in this article (you can duplicate it):

[Notion DB template](https://www.notion.so/f394d3f878ce4c6b88412da5391e4603) 

In a future step, you will need your database ID. To find it, click the Share button on your own database and get the ID between the `/` and the `?` 
The ID of the example database is `f394d3f878ce4c6b88412da5391e4603` (don’t use this one).

### Create a NextJS project

Make sure you have Node installed on your computer.
Execute this command line by replacing the example name by your own project name:

```bash
npx create-next-app notionapi-example
```

Once the project created, open it in your preferred IDE (VS Code for me).

## Build the website

### Add NextJS Image optimization

In the `next.config.js` file, write this:

```jsx
/** @type {import('next').NextConfig} */
const nextConfig = {
  reactStrictMode: true,
  swcMinify: true,
  images: {
    domains: ["s3.us-west-2.amazonaws.com"],
  },
};

module.exports = nextConfig;
```

### Add TailwindCSS

Execute this command lines:

```bash
npm install -D tailwindcss postcss autoprefixer
npx tailwindcss init -p
```

It will create a `tailwind.config.js` file. Modify it to get the same as below:

```jsx
/** @type {import('tailwindcss').Config} */
module.exports = {
  content: [
    "./pages/**/*.{js,ts,jsx,tsx}",
    "./components/**/*.{js,ts,jsx,tsx}",
  ],
  theme: {
    extend: {},
  },
  plugins: [],
};
```

Copy these lines to the `styles/globals.css` file:

```css
@tailwind base;
@tailwind components;
@tailwind utilities;

html,
body {
  scroll-behavior: smooth;
}
```

### Add the dependencies

Install the dependencies by running this:

```bash
npm install dotenv @notionhq/client@1.0.4
```

### Add your integration informations

Create a `.env` file at the root of your project.
Get your secret key on your integration dashboard.
Add your integration’s key and ID like this:

```jsx
NOTION_API_KEY = YOUR INTEGRATION SECRET KEY
NOTION_DB_ID = YOUR DATABASE ID
```

### Creating the API call

Notion API doesn’t allow to call it from the front-end, so we must call it from our back-end, the `pages/api` folder.

Create a file named `articles.js` in this folder and write the code to instantiate the Notion API client and parse the content of our Notion page.

```jsx
require("dotenv").config();
// the following lines are required to initialize a Notion client
const { Client } = require("@notionhq/client");
const notion = new Client({ auth: process.env.NOTION_API_KEY });
const databaseId = process.env.NOTION_DB_ID;

export default async function getArticles() {
  const response = await notion.databases.query({
    database_id: databaseId,
  });

  // options for the Date format of the articles
  const options = {
    year: "numeric",
    month: "long",
    day: "numeric",
  };

  const responseResults = response.results.map((page) => {
    return {
      id: page.id,
      tag: page.properties.tag.select.name,
      title: page.properties.title.title[0]?.plain_text,
      description: page.properties.description.rich_text[0].plain_text,
      image: page.properties.image.files[0].file.url,
      date: new Date(
        page.properties.creation_date.created_time
      ).toLocaleDateString("en-US", options),
    };
  });
  return responseResults;
}
```

### Clear the default page

Delete all the `index.js` page content, and replace it by those lines :

```jsx
import Head from "next/head";
import Image from "next/image";
import getArticles from "./api/articles";

export default function Home({ articles }) {
  function buildArticleCards() {
    return articles.map((article) => {
      return (
        <a href="#" key={article.id} className="group rounded-xl pb-5">
          <div className="block overflow-hidden aspect-w-16 aspect-h-9 rounded-xl transition-all duration-200 backdrop-blur-xl backdrop-filter">
            <figure className="px-10 pt-10 mt-4 relative h-44">
              <Image
                layout="fill"
                src={article.image}
                alt={article.title}
                objectFit="contain"
                className="rounded-xl"
              />
            </figure>
          </div>
          <div className="flex items-center mt-6 space-x-2 ml-2">
            <p className="text-sm font-medium text-gray-900">{article.tag}</p>
            <span className="text-sm font-medium text-gray-900">•</span>
            <p className="text-sm font-medium text-gray-900">{article.date}</p>
          </div>
          <p className="mt-4 ml-2 text-xl font-bold text-gray-900 group-hover:text-gray-600">
            {article.title}
          </p>
        </a>
      );
    });
  }

  return (
    <div>
      <Head>
        <title>NextJSxNotion</title>
        <meta name="description" content="Created by Yacine Messaadi" />
        <link rel="icon" href="/favicon.ico" />
      </Head>

      <main>
        <h1 className="text-4xl my-10 text-center">Welcome to NextJSxNotion</h1>
        <section id="blogInsight">
          <h2 className="text-center mt-4 font-heading font-sans text-3xl sm:text-3xl">
            <span className="text-center self-center">Blog Articles</span>
          </h2>
          <div className="px-4 mx-auto sm:px-6 lg:px-8 max-w-7xl">
            <div className="grid grid-cols-1 gap-10 mt-6 xl:gap-20 sm:grid-cols-2 lg:grid-cols-3 sm:mt-10">
              {buildArticleCards()}
            </div>
          </div>
        </section>
      </main>
    </div>
  );
}

export async function getStaticProps() {
  const articles = await getArticles();
  return {
    props: {
      articles,
    },
    // Revalidate is a way to rebuild the static website following an interval of seconds.
    revalidate: 86400,
  };
}
```

### Try it out !

Start your server by writing the command below and open your browser to [localhost:3000](http://localhost:3000).

```bash
npm run dev
```

### The end

You are now able to start an amazing journey through the Notion API capabilities, mixed with the power of NextJS !