How to use Image and Preview in your Nextjs & Strapi Blog
Tags: nextjs, node, blog, cms
Published: 2021-07-27 13:15:36

# 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.


![nextjs-strapi-blog](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/9scrsl52xc2pjs1eygqa.gif)



# 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](https://strapi.io/documentation/developer-docs/latest/setup-deployment-guides/installation.html).

If you want to follow along with this post, feel free to use [https://github.com/amirtds/strapi-sqlite](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/](http://localhost:1337/) and create an admin account at [http://localhost:1337/admin](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.

![Authors](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/90lpegl02k94zwstl70o.png)

![Blogs](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/4gb0kgbg2azza2170lel.png)


Feel free to add new records for each content type by clicking on *Authors* and *Blogs* on the left sidebar.


![Records](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/6pwy28bbsn0xrve2zgnx.png)


**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.


![Permissions](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/7htyrdd8lsoqbv68cbgk.png)


**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.


![GraphQL plugin](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/15jg0wq9cz0xrp8l9ikr.png)


# 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](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](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](https://github.com/amirtds/blog/blob/main/src/components/blogs.jsx#L73) 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](https://lighthouse-dot-webdotdevsite.appspot.com//lh/html?url=https%3A%2F%2Fnextjs-blog-rose-eta.vercel.app).


![lighjouse report](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/9qz2s82wdq6l3x7ai5qn.png)



# 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](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.


![cookies](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/fas86759uq4spzd7ix37.png)


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.


![](https://www.dropbox.com/s/w74frtne9zvr5uv/preview.gif?raw=1)



## 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](https://nextjs-blog-rose-eta.vercel.app)
You can find the source code at [https://github.com/amirtds/blog](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.