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:
- Basic knowledge of JavaScript
- Basic knowledge of ReactJS
- Basic knowledge of Next.js
- Basic understanding of Strapi - get started here
- 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
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:
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:
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:
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
- On the left-hand side, head over to Content-Type Builder and click Create new collection type.
- Add Content Block for the display name and click Continue.
- Under select a field for your collection type, choose Text.
- In the Add new text field modal, enter “content” for the Name and choose Long text.
- 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
- Click on Create new collection type.
- Fill out “Page” for the display name and click continue.
- Select Text and name the field “title”.
- 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.
- Select the third option (“Page belongs to many Content Blocks”).
- 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:
- Head to General > Settings.
- Under Users & Permissions Plugin, select Roles.
- Click on Public.
- Click on the Select all for pages and content-block.
- Click Save.
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
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.
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
}
}
}
}
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
}
}
}
}
}
}
}
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
}
}
}
}
Retrieve One Page:
query getOnePage {
page(id: 2) {
data {
id
attributes {
title
}
}
}
}
Update a Page:
mutation updatePage {
updatePage (id: 3, data: { title: "Third Page"}) {
data {
id
attributes {
title
}
}
}
}
Delete a Page:
mutation deletePage {
deletePage (id: 1) {
data {
id
attributes {
title
}
}
}
}
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)