Strapi and Next.js are the best headless CMS and application development environments available on the market today! They are two powerful technologies that can work in tandem to give you the best digital experience possible.
Strapi is a flexible, open-source Headless CMS that allows developers to use their favorite tools and frameworks while also giving editors power over their content. Next.js is a developer's dream come true with all the features needed for production: hybrid static & server rendering, TypeScript support, smart bundling, assets optimization, and more!
This tutorial will build a blog app using Strapi as the CMS and Next.js as the framework. We will also be using two of its cool features, image optimization, and preview mode. Here's an example of what we will be building.
node <=14.0.0
npm >=6.0.0
yarn
Strapi (v3.6.5)
Next.js (v.11.0.1)
This article is all about Next.js and Strapi! You will learn how to use the Image component to add images into your application and use Next.js Preview. See an edited version of your content from Strapi.
Before getting started, let's talk about two features of Next.js that we will use in our blog application.
A website's images can significantly impact its loading time. Typically, they are assets that can harm our site performance if they are not in the proper format or size.
Finding and fixing large images is a tedious process. If you don't have an automatic way to do it, you will find yourself spending hours looking for those images that slow your site down and optimize them.
Using the next/image component, we can resize, optimize and serve images in modern formats. This helps us drastically to improve site speed and user experience with images. Next.js can optimize not only locally hosted images but can work with external data sources as well. In our case, images hosted on Strapi.
Static Site Generation is a great way to create static pages ahead of time before users request them. This makes the loading of your blog posts fast, but it makes the editing experience not too pleasant. Each time you edit in a post, and you want to see how the edit looks, and you have to rebuild the whole site.
In this case, preview mode can come to the rescue. Preview mode bypasses static generation and renders the page at the request time instead of build time with the draft data instead of production. In simple words, what it does is make static pages dynamic.
To Install Strapi, you can choose one of the installation methods.
If you want to follow along with this post, feel free to use https://github.com/amirtds/strapi-sqlite.
1 git clone https://github.com/amirtds/strapi-sqlite
2 strapi-sqlite
3 yarn install && yarn develop
After successfully running the develop command, you should be able to visit the Strapi dashboard at http://localhost:1337/ and create an admin account at http://localhost:1337/admin.
We built two content types for our Blog.
Below are the images of the fields that should be included in each collection.
Feel free to add new records for each content type by clicking on Authors and Blogs on the left sidebar.
API Access
We are using GraphQL to consume Strapi Data. Make sure your Strapi is set up correctly, and you have the appropriate permissions. Go to settings → Roles → Public and give find
and count
permission to the public.
GraphQL Plugin
If you are not using our repo for Strapi, make sure the GraphQL plugin is installed! You can find it in the Marketplace section in the left sidebar.
Let's create a new project called next-blog
using our example in the GitHub repo and run the development environment. Make sure you created some records in Strapi for Authors and Blogs before running this command.
1 npx create-next-app next-blog --example "https://github.com/amirtds/blog/tree/develop"
2 cd next-blog
3 npm run dev
Now you should be able to access the site at http://localhost:3000.
Nextjs Image In our blog application, we are using the Nextjs Image component to optimize our images. For more information, visit https://nextjs.org/docs/basic-features/image-optimization.
Use Image
Component
1 import Image from 'next/image'
It's necessary to set the width and height properties of the Image
. In our app, We also set the src as src={urlBuilder(post.image[0].url)}
Let's take a deeper look at our code. In the src/components/blogs.jsx we have:
1 {siteBlogs.map((post) => (
2 <Link key={post.id} href={`/blogs/${post.slug}`}>
3 <a>
4 <motion.div variants={fadeIn} key={post.id} whileHover={{ scale: 1.05 }} whileTap={{ scale: 0.95 }}className="flex flex-col rounded-lg shadow-lg overflow-hidden">
5 <div className="flex-shrink-0">
6 <Image width={600} height={350} className="h-48 w-full object-cover" src={urlBuilder(post.image[0].url)} alt={post.title} />
7 </div>
8 <div className="flex-1 bg-white p-6 flex flex-col justify-between">
9 <div className="flex-1">
10 <a href={post.href} className="block mt-2">
11 <p className="text-xl font-semibold text-gray-900">{post.title}</p>
12 <p className="mt-3 text-base text-gray-500">{post.description}</p>
13 </a>
14 </div>
15 <div className="mt-6 flex items-center">
16 <div className="flex-shrink-0">
17 <span className="sr-only">{post.author.name}</span>
18 <Image width={50} height={50} className="h-10 w-10 rounded-full" src={urlBuilder(post.author.photo[0].url)} alt={post.title} />
19 </div>
20 <div className="ml-3">
21 <p className="text-sm font-medium text-gray-900">
22 {post.author.name}
23 </p>
24 <div className="flex space-x-1 text-sm text-gray-500">
25 <time dateTime={post.published}>{post.published}</time>
26 </div>
27 </div>
28 </div>
29 </div>
30 </motion.div>
31 </a>
32 </Link>
33 ))}
*siteBlogs*
is an array that has a list of all of our blogs. We are looping over it and creating a blog card based on each blog item in this list. In the Image
src={urlBuilder(post.image[0].url)}
result will be STRAPI_URL/IMAGE_URL
for example http://localhost:1337/uploads/strapi_cover_1fabc982ce_1c5a5b390a.png
.
Set domain
in next.config.js
. In this file, you should have something like
1 module.exports = {
2 images: {
3 domains: ["localhost"],
4 },
5 }
In our case, we have
1 module.exports = {
2 images: {
3 domains: [configs.STRAPI_DOMAIN],
4 },
5 }
Which configs.STRAPI_DOMAIN
is what we have in the configs.json
file for the Strapi domain.
We do not have many pictures in our Blog, but after using the image component, we got great results from the lighthouse audit.
Preview makes a pre-rendered page to be visible as server-side rendered pages. This means that, with Preview, you can see your changes live without having to go through the whole build process again!
Next.js checks your site cookies, and if two special cookies are present, it considers the request as preview mode, and it bypasses the SSG. For more information about Preview, please visit https://nextjs.org/docs/advanced-features/preview-mode.
Create APIs
We need to create 2 APIs for preview functionality.
Firstly, we will have the /api/preview
, which adds preview mode cookies to your site. After successfully implementing this API, calls to it will add __prerender_bypass
and __next_preview_data
cookies.
Open the preview.js
file and add the following codes:
1 // src/pages/api/preview.js
2
3 import { getPost } from 'lib/api'
4
5 export default async function handler(req, res) {
6 # Check if the user is requesting with valid token
7 if (req.query.secret !== (process.env.STRAPI_PREVIEW_SECRET)) {
8 return res.status(401).json({ message: "Invalid token" });
9 }
10
11 # Make sure the blog post actiually exits
12 const slug = req.query.slug
13 const blogData = await getPost(slug)
14 if (!blogData) {
15 return res.status(401).json({ message: "Invalid slug" });
16 }
17 # If all good we set preview cookies
18 # And we redirect the user to the preview version of the blog post
19 res.setPreviewData({});
20
21 res.writeHead(307, { Location: `/blogs/${slug}` });
22 res.end();
23 };
Secondly, we will create the last API /api/exit-preview
. To return to SSG mode, we need to remove those cookies from our browser. This API will take care of that.
1 // src/pages/api/exit-preview.js
2
3 export default async function exit(_, res) {
4 // Exit the current user from "Preview Mode". This function accepts no args.
5 res.clearPreviewData()
6 // Redirect the user back to the index page.
7 res.writeHead(307, { Location: "/" })
8 res.end()
9 }
live
or preview
content from StrapiThe last step is to fetch data from Strapi based on preview mode. Before we start fetching and displaying the data from our Strapi, let's look at how to detect preview mode.
The following context
object has a preview
attribute that returns true
or false
How we use it on our page. In the getStaticProps
function of your page, you can use context
as an argument, and based on the status of Preview, we fetch live or preview content from Strapi.
1 // src/pages/blogs/[slug].js
2
3 export const getStaticProps = async (context) => {
4 const previewMode = context.preview == false || context.preview == null ? "live" : "preview"
5 const slug = context.params.slug
6 const BLOG_QUERY = gql`
7 query($slug: String, $previewMode: String){
8 blogs(where: {slug: $slug, _publicationState: $previewMode}){
9 id
10 title
11 subtitle
12 description
13 published
14 slug
15 image{
16 url
17 }
18 author {
19 name
20 photo {
21 url
22 }
23 }
24 content
25 }
26 }
27 `
28 const { data:blogData } = await apolloClient.query({
29 query: BLOG_QUERY,
30 variables: {
31 slug,
32 previewMode
33 },
34 preview: context.preview,
35 })
As you see, we have _publicationState
condition in our call that can be live
or Preview
.
We changed "Build a Next.js Blog with Strapi and use preview and image component!" Blog title to "Build a Next.js Blog with Strapi - Draft," but I didn't build the site again, let's see how it looks like.
In this article, we learned how to leverage the power of Next.js previews and images optimization with Strapi content.
We hope this tutorial has helped teach you how easy it is to integrate these tools into your application. It is more important than ever to create an exceptional digital experience for your customers in today's world.
The Blog is hosted at Vercel: https://nextjs-blog-rose-eta.vercel.app You can find the source code at https://github.com/amirtds/blog
With Strapi and Next.js, you can do just that! We saw how these two powerful technologies work together seamlessly to help you build a blog app with previews and optimized images quickly and if you have any questions, feel free to contact me at amirtds@gmail.com or my twitter.
I'm a full stack engineer who loves the challenges of working with cutting-edge technologies like React, Nextjs, Strapi.