Build a CMS using Strapi and Next.js
Step-by-step guide to create your own static CMS
There are a variety of users on the internet- entrepreneurs, artists, influencers, entertainers, students, teachers, and more. All of them use this cyberspace for their own specific use-case.
Students use the internet to learn, teachers use it to research. Influencers, artists, entertainers, writers, photographers, editors, and so on, use the internet to showcase their talents- as portfolio websites, which help them land more clients, find employment, and answer the question “Who are you?”.
So to create those portfolio websites creators use Content Management Systems.
What is CMS and why has it lost its head?
CMS is Content Management System that helps creators to create, edit and showcase their talents on the web without any prior technical knowledge. Some CMS examples are WordPress, Drupal, Joomla, Squarespace, etc.
Traditional CMS serves the entire site from a single system that connects the front-end and the back-end of a website in a neat and easy application codebase. It has limited flexibility, doesn't always scale well, and creates only website content.
In headless CMS the front end has been completely separated. It deals strictly with the content, unlike traditional CMSs in which the focus is on content and front-end both. It is more flexible and scalable.
Today we will be using Next.js and strapi for building a headless CMS which will be highly scalable as Nextjs supports static sites and strapi will help in handling the APIs.
Let’s get started by creating APIs using Strapi in 5 minutes.
npx create-strapi-app <file-name> --quickstart
Once the app is created, the server will be running on http://localhost:1337, where you have to create an admin user.
After the user is created you will be redirected to the welcome page
I have created two collection types
- Post
- User
After creating the collection types, you can now start adding fields in your collection types, with the required data types.
In the Post collection type, I have added fields such as Title, Slug, Content, Media, and User is of datatype Relation in the Post collection-type.
In strapi, it is really easy to set up a relationship with another collection:
Now we have successfully created a many-to-one relationship between Posts and Users- which means that a user can have many posts but a post cannot have many users.
In the User collection type, I have added fields such as username, email, provider, password, resetPasswordToken, confirmationToken, and so on in the User collection-type.
Next, I have created 2 single types that will be used as the header on the website- Blog title, and Blog Video.
Other than creating collection types and single types which will contain the content to be shown on the website, you can customize the roles and permissions provided to the admin users from the Settings page.
The backend setup is now complete.
Note: By default the API is private so you have to change the permissions from the settings to make the APIs publically accessible or change as per your use case requirements.
I am making the APIs publically available for the sake of this tutorial by going to Settings > Roles > Public > Permissions >
Check all the permissions that you want to make public,
Save the Permissions and go to http://localhost:1337/api/posts to check if all the posts that you have created are public or not.
Now you can explore and perform CRUD operations as well and work with the data however you want.
Let’s move ahead and create the front-end using Next.js
npx create-next-app <file-name>
Once the installation is done you can run the app
npm run dev
I have used Material UI for styling purposes you can use CSS or any other styling library of your choice.
Now, create the index.js file
import React from "react";import ReactPlayer from "react-player";import PostCard from "./PostCard";import { Box, Typography, ImageList, ImageListItem } from "@mui/material";export default function Home({ posts, blogTitle, video }) {return (<Boxdisplay="flex"alignItems="center"justifyContent="center"flexDirection="column"style={{backgroundColor: "#fcfcfc",width: "100%",padding: "50px 70px 50px 70px",}}><Boxdisplay="flex"alignItems="center"justifyContent="space-around"width="100%"style={{backgroundColor: "#52618f",borderRadius: "10px",padding: "40px",marginBottom: "40px",}}><Box display="flex" flexDirection="column" alignItems="center"><TypographymaxWidth="400px"variant="h2"style={{color: "#fcfcfc",fontWeight: "bold",marginBottom: "15px",marginRight: "35px",textAlign: "center",}}>{blogTitle.attributes.BlogTitle}</Typography><Boxstyle={{backgroundColor: "#484848",width: "fit-content",padding: "8px",borderRadius: "8px",}}><TypographymaxWidth="400px"variant="h4"style={{color: "#fcfcfc",fontWeight: "bold",}}>Watch the video</Typography></Box></Box><ReactPlayer url={`${video.attributes.blogvideo}`} /></Box><ImageList cols={3} gap={45} variant="masonry">{posts.map((post) => (<ImageListItem key={post.id}><PostCard post={post} /></ImageListItem>))}</ImageList></Box>);}
Now, add the getStaticProps() function as the data is coming from a CMS.
export async function getStaticProps() {const postsRes = await fetch("http://localhost:1337/api/posts?populate=*");const postsData = await postsRes.json();const posts = postsData.data;const blogTitleRes = await fetch("http://localhost:1337/api/blog");const blogTitleData = await blogTitleRes.json();const blogTitle = blogTitleData.data;const videoRes = await fetch("http://localhost:1337/api/video?populate=*");const videoData = await videoRes.json();const video = videoData.data;return {props: { posts, blogTitle, video },};}
Create a PostCard component
import {Paper,CardHeader,CardMedia,CardContent,CardActions,Avatar,IconButton,Typography,} from "@mui/material";import FavoriteIcon from "@mui/icons-material/Favorite";import ShareIcon from "@mui/icons-material/Share";import ExpandMoreIcon from "@mui/icons-material/ExpandMore";import MoreVertIcon from "@mui/icons-material/MoreVert";export default function PostCard({ post }) {const media =post.attributes.Media &&post.attributes.Media.data[0].attributes.formats.large.url;return (<Paperelevation={6}style={{backgroundColor: "#52618f",color: "#fcfcfc",borderRadius: "10px",}}><CardHeaderavatar={<Avatar sx={{ bgcolor: "#484848" }} aria-label="user">J</Avatar>}action={<IconButton aria-label="settings"><MoreVertIcon /></IconButton>}title={post.attributes.Title}subheader={post.attributes.publishedAt}/><CardMediacomponent="img"height="194"image={`http://localhost:1337${media}`}alt="blog-image"/><CardContent><Typographyvariant="body1"color="#fcfcfc"style={{width: "95%",}}>{post.attributes.Content}</Typography></CardContent><CardActions disableSpacing><IconButton aria-label="add to favorites"><FavoriteIcon color="white" /></IconButton><IconButton aria-label="share"><ShareIcon /></IconButton><IconButton aria-label="show more"><ExpandMoreIcon /></IconButton></CardActions></Paper>);}
Your Front-end is now ready, you can add new entries in the posts from the admin dashboard and the changes will reflect on the static website on http://localhost:3000/.
Note: You can find the Github repo here.
That’s it! You have your own headless Content Management System.
You can now modify and create a CMS that fits your particular use case. Also, share this blog if you found it helpful.