DEV Community

Cover image for How to Build a Notion Clone with Strapi v4 and Next.js (Part 1 of 2)
Strapi for Strapi

Posted on • Originally published at strapi.io

How to Build a Notion Clone with Strapi v4 and Next.js (Part 1 of 2)


This two-part tutorial series will walk you through the creation of a Notion clone with Strapi and Next.js.

Author: Hubert Nare

Notion is a popular note-taking tool loved by many for its responsiveness and dynamic features. Its collaborative setting allows teams around the world to document, share, and communicate easily. On top of that, the component-like nature of Notion content not only supports many types of formats (images, links, tables, etc.), but it makes writing and editing a breeze.

Strapi is the leading open-source content management system (CMS), and it will act as our content hub for this project. Not only is it fast, Strapi is also very developer-friendly. You can consume the Strapi API from any client using REST or GraphQL. Strapi offers many integrations, along with ample documentation on how to integrate it with all the major frontend libraries such as Next.js, React, Vue, Gatsby, and others.

This two-part tutorial series will walk you through the creation of a Notion clone with Strapi and Next.js. In this article, you will get familiar with Strapi and its admin interface. You will also learn about the GraphQL playground and how to create queries and mutations.

Prerequisites

Before you can jump into this content, you need to have:

  1. Basic knowledge of JavaScript
  2. Basic knowledge of ReactJS
  3. Basic knowledge of Next.js
  4. Basic understanding of Strapi - get started here
  5. Node.js version 14 downloaded and installed

The Initial Planning

In order to create your Notion clone, you will need two important components: a Next.js client and a Strapi server.

To start with, you will need a Strapi backend. As mentioned above, Strapi will act as your content hub. This will not only allow you to create data structures, but will also store your data. For this project, you will have two kinds of data:

  • Pages
  • Content blocks

By using blocks, you can replicate the way Notion creates content. Every block will have raw HTML stored in it that, when fetched, will be rendered on the client. As for the pages, they will have a title and a list of blocks that belong to them.

You will be interacting with this data thanks to GraphQL, which you will install with your Strapi backend. Once done, you will design queries and mutations that will allow you to create, update, and delete content through your new API.

Finally, there is the Next.js client which will reproduce some of the functionalities of Notion. Just like the popular note-taking application, this frontend will have a few features such as creating pages, but also the ability to create blocks of content on your page. You will be using an HTML editor to recreate a rich but easy-to-use editing experience.

The project will also take advantage of Next.js static and server-side rendering by setting a static route for your list of pages but dynamic routes for your single page. Of course, you will also be setting up the routing between them. Finally, we will use the library graphql-hooks to create a GraphQL client, which will allow you to connect to your Strapi backend and retrieve, save, or update your data.

Building the Backend

This section will walk you through a step-by-step guide in building the clone.

To begin, you need to setup your Strapi backend.

Step 1: Create a Strapi Project

To create a Strapi backend, run one of the commands below:

    npx create-strapi-app@latest notion-clone-strapi-backend --quickstart
    #or using yarn
    yarn create strapi-app notion-clone-strapi-backend --quickstart
Enter fullscreen mode Exit fullscreen mode

Step 2: Create Your Administrator Account

In this step, you are going to create your Strapi admin account.

Once your terminal finishes constructing the backend, by default, Strapi will automatically start a localhost development server. In addition to starting the server, a new tab will launch, requesting you to register your first administrator account:

Register new Strapi admin account

By chance, if a Strapi server fails to start, use your terminal or command prompt to browse to the newly formed backend folder, /notion-clone-strapi-backend. Execute the command, yarn run develop. The aforementioned "new tab" will then open, requiring your credentials for admin registration.

After providing your credentials and hitting “Let’s start”, you will be redirected to a super short survey:

Strapi survey

From the dropdown menu similar to that above, choose a title that best describes you. Once you are done, finally you will be redirected to a Strapi admin panel:

Strapi admin page

Step 3: Create Your Collections

You currently have an empty project. In the admin panel, you can create your data structure for your future data entries. Strapi calls those data structures collections. For this project, you will need two kinds of collections:

  • Content Block
  • Page
Create Content Block Collection
  1. On the left-hand side, head over to Content-Type Builder and click Create new collection type.
  2. Add Content Block for the display name and click Continue.
  3. Under select a field for your collection type, choose Text.
  4. In the Add new text field modal, enter “content” for the Name and choose Long text.
  5. Click Finish and then Save.

Note: The reason you will choose Text here is in order to take advantage of the vast choices in terms of HTML editors for React. As a result, you will be storing raw HTML for your content. It is, however, interesting to note that Strapi does have a Rich Text field which supports Markdown. This can be very helpful for some developers used to writing this way.

Create Page Collection
  1. Click on Create new collection type.
  2. Fill out “Page” for the display name and click continue.
  3. Select Text and name the field “title”.
  4. Add a second field, and this time, select Relation.

Note: Strapi offers many different kinds of relationships depending on your needs. Whether it’s one-to-one, one-to-many, many-to-one or many-to-many, you will find what you need.

Relationship dialog screen in Strapi dashboard

  1. Select the third option (“Page belongs to many Content Blocks”).
  2. Click Finish.

Note: This will create a content_blocks field on pages with an array of content blocks IDs, but it will also create a page field on the content block to save the page ID.

Step 4: Turn on Permissions

Now that your collections are set up, you are almost ready to set up GraphQL. But before you do so, you need to make your data accessible. This means that you need to allow your data to be accessed and retrieved through your GraphQL API. The steps to do so are:

  1. Head to General > Settings.
  2. Under Users & Permissions Plugin, select Roles.
  3. Click on Public.
  4. Click on the Select all for pages and content-block.
  5. Click Save.

Permissions screen in Strapi dashboard

Now that your data is accessible through an endpoint, it’s time to set up GraphQL.

Implementing GraphQL

If your Strapi server is still running, stop it with Ctrl+C. In the terminal, run the command:

    npm run strapi install graphql
    # or
    yarn strapi install graphql
Enter fullscreen mode Exit fullscreen mode

Then you can start up your server again with yarn develop or npm develop.

Implementing CRUD

Now that your Strapi backend is set up with GraphQL, it’s time to play with your data a bit. You can access the GraphQL playground and try to create and retrieve some data.

Strapi GraphQL playground

If you are unfamiliar with this technology, GraphQL uses queries and mutations to interact with data. Queries allow you to retrieve data by asking specifically for what you want. Mutations, on the other hand, let you modify data whether by creating, updating, or deleting it. Do not hesitate to check out the GraphQL documentation for more explanation.

Next, create your first query in the GraphQL playground. In a new tab, input the type (query or mutation). Because you’ll want to start by retrieving all the pages, you should choose a query and then enter getAllPages to name your function.

Once that is done, specify the collection you want to retrieve (i.e pages) and the fields from that collection you want (id, title).

    query getAllPages {
      pages {
        data {
          id
          attributes {
            title
          }
        }
      }
    }
Enter fullscreen mode Exit fullscreen mode

Run it and see the response displayed in the right hand-side of the playground.
If you want to retrieve the related content—say, all the content blocks—add content_blocks and specify which fields in this collection you want to retrieve.

    query getAllPages {
      pages {
        data {
          id
          attributes {
            title
            content_blocks {
              data {
                id
                attributes {
                  content
                }
              }
            }
          }
        }
      }
    }
Enter fullscreen mode Exit fullscreen mode

Here are some more queries and mutations for you to try out:

Create a Page:

    mutation createPage {
      createPage(data: { title: "New Page" }) {
        data {
          id
          attributes {
            title
          }
        }
      }
    }
Enter fullscreen mode Exit fullscreen mode
Retrieve One Page:
    query getOnePage {
      page(id: 2) {
        data {
          id
          attributes {
            title
          }
        }
      }
    }
Enter fullscreen mode Exit fullscreen mode
Update a Page:
    mutation updatePage {
      updatePage (id: 3, data: { title: "Third Page"}) {
        data {
          id
          attributes {
            title
          }
        }
      }
    }
Enter fullscreen mode Exit fullscreen mode
Delete a Page:
    mutation deletePage {
      deletePage (id: 1) {
        data {
          id
          attributes {
            title
          }
        }
      }
    }
Enter fullscreen mode Exit fullscreen mode

Try to create some data and see it appear in your Strapi admin panel.

Conclusion

In this article, you learned how to set up a Strapi backend. You did so by first creating a Strapi project, setting up your administrator, and installing GraphQL.

When that was done, you discovered how to create collections in the Strapi admin dashboard. You created two collections, for pages and content blocks, and added the required fields for each. You also set up a relationship between pages and content blocks so that pages would have a list of blocks that belong to them, and blocks would have a record of which page they belong to.

You also learned how permissions worked and edited them so that your data could be accessible through the GraphQL API.

To finish, you interacted with the GraphQL playground and learned how to create queries and mutations.

In part 2, we will continue this tutorial and you will create a Next.js client to consume your GraphQL API.

For more information, the frontend of the codebase can be found in this repo.

Part II: How to Build a Notion Clone with Strapi v4 and Next.js (Part 2 of 2)

Discussion (0)