Introduction
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.
Prerequisites
node <=14.0.0
npm >=6.0.0
yarn
- Basic knowledge about git
- The Latest version of
Strapi (v3.6.5)
- The Latest version of
Next.js (v.11.0.1)
Goals
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.
Optimize image loading with Image
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.
Get draft previews with Nextjs preview mode
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 making static pages dynamic.
Scaffolding a Strapi Project
Installation
To Install Strapi, you can choose one of the installation methods here.
If you want to follow along with this post, feel free to use https://github.com/amirtds/strapi-sqlite.
git clone https://github.com/amirtds/strapi-sqlite
strapi-sqlite
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.
Content Types
We built two content types for our Blog.
- Author
- 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 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.
Building the Frontend with Nextjs
Create New Project
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.
npx create-next-app next-blog --example "https://github.com/amirtds/blog/tree/develop"
cd next-blog
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
- To use the image component, you to first import it
import Image from 'next/image'
- Set Width, height and src
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:
{siteBlogs.map((post) => (
<Link key={post.id} href={`/blogs/${post.slug}`}>
<a>
<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">
<div className="flex-shrink-0">
<Image width={600} height={350} className="h-48 w-full object-cover" src={urlBuilder(post.image[0].url)} alt={post.title} />
</div>
<div className="flex-1 bg-white p-6 flex flex-col justify-between">
<div className="flex-1">
<a href={post.href} className="block mt-2">
<p className="text-xl font-semibold text-gray-900">{post.title}</p>
<p className="mt-3 text-base text-gray-500">{post.description}</p>
</a>
</div>
<div className="mt-6 flex items-center">
<div className="flex-shrink-0">
<span className="sr-only">{post.author.name}</span>
<Image width={50} height={50} className="h-10 w-10 rounded-full" src={urlBuilder(post.author.photo[0].url)} alt={post.title} />
</div>
<div className="ml-3">
<p className="text-sm font-medium text-gray-900">
{post.author.name}
</p>
<div className="flex space-x-1 text-sm text-gray-500">
<time dateTime={post.published}>{post.published}</time>
</div>
</div>
</div>
</div>
</motion.div>
</a>
</Link>
))}
*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
module.exports = {
images: {
domains: ["localhost"],
},
}
In our case, we have
module.exports = {
images: {
domains: [configs.STRAPI_DOMAIN],
},
}
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.
Nextjs Preview
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!
How it works ?
NextJS 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:
// src/pages/api/preview.js
import { getPost } from 'lib/api'
export default async function handler(req, res) {
# Check if the user is requesting with valid token
if (req.query.secret !== (process.env.STRAPI_PREVIEW_SECRET)) {
return res.status(401).json({ message: "Invalid token" });
}
# Make sure the blog post actiually exits
const slug = req.query.slug
const blogData = await getPost(slug)
if (!blogData) {
return res.status(401).json({ message: "Invalid slug" });
}
# If all good we set preview cookies
# And we redirect the user to the preview version of the blog post
res.setPreviewData({});
res.writeHead(307, { Location: `/blogs/${slug}` });
res.end();
};
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.
// src/pages/api/exit-preview.js
export default async function exit(_, res) {
// Exit the current user from "Preview Mode". This function accepts no args.
res.clearPreviewData()
// Redirect the user back to the index page.
res.writeHead(307, { Location: "/" })
res.end()
}
Fetch live
or preview
content from Strapi
The 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.
// src/pages/blogs/[slug].js
export const getStaticProps = async (context) => {
const previewMode = context.preview == false || context.preview == null ? "live" : "preview"
const slug = context.params.slug
const BLOG_QUERY = gql`
query($slug: String, $previewMode: String){
blogs(where: {slug: $slug, _publicationState: $previewMode}){
id
title
subtitle
description
published
slug
image{
url
}
author {
name
photo {
url
}
}
content
}
}
`
const { data:blogData } = await apolloClient.query({
query: BLOG_QUERY,
variables: {
slug,
previewMode
},
preview: context.preview,
})
As you see, we have _publicationState
condition in our call that can be live
or Preview
.
How it looks
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.
Conclusion
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.