Setting up a basic Next.js app locally [Building Personal Blog Website Part 2]
Tags: tutorial, headlesscms, nextjs, strapi
Published: 2022-08-20 09:42:00

Now that the basic CMS is set up on *Railway* and *Cloudinary* is configured to handle the images, the next step would be... creating a *Next* app.

This *Next* app will consume content provided by the CMS and present it on a webpage. In this part of the series basic homepage with a list of blog posts will be created and set up locally.

So let's just jump into it. First thing you need to create is a new *Next.js* app. You can do this by this simple command:

```
npx create-next-app@latest

```

After running it the prompt will ask for the app's name - provide one and then the project will be created.

![https://res.cloudinary.com/dvrqjaeju/image/upload/v1661531654/Blog_tutorial_1_498857321b.png](https://res.cloudinary.com/dvrqjaeju/image/upload/v1661531654/Blog_tutorial_1_498857321b.png)

Before you start working on an actual app let's just install some dependencies that will be useful later on - *TailwindCSS* for styling the app and *Apollo Client* for working with *GraphQL*.

First - let's go with *TailwindCSS*.

```
yarn add tailwindcss postcss autoprefixer
npx tailwindcss init -p
```

After running the init command the file `tailwind.config.js` will be created. You need to make a few changes to the generated one so everything will be working correctly.

Replace an empty `content` array with:

```
content: [
    "./pages/**/*.{js,ts,jsx,tsx}",
    "./components/**/*.{js,ts,jsx,tsx}",
  ],
```

![https://res.cloudinary.com/dvrqjaeju/image/upload/v1661531709/Blog_tutorial_2_8ca3d2c219.png](https://res.cloudinary.com/dvrqjaeju/image/upload/v1661531709/Blog_tutorial_2_8ca3d2c219.png)

You also need to import *TailwindCSS* rules to the `./styles/globals.css` file:

```
@tailwind base;
@tailwind components;
@tailwind utilities;

```

![https://res.cloudinary.com/dvrqjaeju/image/upload/v1661531983/Blog_tutorial_2_3_8b60adcecb.png](https://res.cloudinary.com/dvrqjaeju/image/upload/v1661531983/Blog_tutorial_2_3_8b60adcecb.png)

And now you're done with setting up *TailwindCSS*. Let's move on to the *Apollo Client*.

```
yarn add @apollo/client graphql

```

That's it! You're ready to create your *GraphQL-driven* blog page.

First you need to set up an *Apollo Client*, that will work as a "gateway" to your *GraphQL* API. Create a new file in the root of the project called apollo-client.js (you can name it however you want TBH).

```
import { ApolloClient, InMemoryCache } from "@apollo/client";

const client = new ApolloClient({
    uri: "https://[YOUR-CMS-GRAPHQL-URL]",
    cache: new InMemoryCache(),
});

export default client;

```

With this in place it's time to use it! You'll start by getting a list of all blog posts to be displayed on the homepage. You'll use the *Static Site Generation* feature of *Next.js* - for this the `getStaticProps` method is needed.

---

### Side note - Why SSG?

As in this tutorial series we are talking about setting up this blog for free - we are limited performance-wise. The CMS set on free *Railway* tier isn’t exactly a speed demon, *Next.js* app with SSR set on free *Netlify* would also be kinda slow. So, SSG seems to be the best option, even though every change on our blog would need an app rebuild. If budget is not a concern to you, then you can think about other options, but that’s not the topic of this series.

---

Before we go any further let's just add two more fields in your Blog post configuration. Open up a repo for your *Strapi CMS* and run it in development mode (`yarn develop`). Log in and go to the Content-Type Builder. First you'll need to create a new type named *Tag*.

It has an ID and a name that should be unique.

![https://res.cloudinary.com/dvrqjaeju/image/upload/v1661531985/Blog_tutorial_2_4_dd2a8f9c9c.png](https://res.cloudinary.com/dvrqjaeju/image/upload/v1661531985/Blog_tutorial_2_4_dd2a8f9c9c.png)

![https://res.cloudinary.com/dvrqjaeju/image/upload/v1661531985/Blog_tutorial_2_5_048bf8e3c6.png](https://res.cloudinary.com/dvrqjaeju/image/upload/v1661531985/Blog_tutorial_2_5_048bf8e3c6.png)

When this is done you are ready to "upgrade" your Blog post type. In the Content-Type Builder open the Post and add two new fields. First one is excerpt - the short text that will be presented on your homepage, sneak peak into the blog post. It should be of type string and preferably have some maximum length (250 characters in my case). It also should be required.

![https://res.cloudinary.com/dvrqjaeju/image/upload/v1661531985/Blog_tutorial_2_6_e2c852c029.png](https://res.cloudinary.com/dvrqjaeju/image/upload/v1661531985/Blog_tutorial_2_6_e2c852c029.png)

![https://res.cloudinary.com/dvrqjaeju/image/upload/v1661531985/Blog_tutorial_2_7_5b3253ddae.png](https://res.cloudinary.com/dvrqjaeju/image/upload/v1661531985/Blog_tutorial_2_7_5b3253ddae.png)

Now you need to add Tags. It will be a Relation type field and the relation is Many-to-Many as one Post can have multiple Tags and every Tag can belong to multiple Posts. Do it like this:

![https://res.cloudinary.com/dvrqjaeju/image/upload/v1661531985/Blog_tutorial_2_8_950249e1cc.png](https://res.cloudinary.com/dvrqjaeju/image/upload/v1661531985/Blog_tutorial_2_8_950249e1cc.png)

Now go to your *Terminal* and commit all of the changes that were made in your config files by *Strapi*. And then push your changes to rebuild the CMS hosted on Railway:

```
git push

```

When the app on Railway is rebuilt - log in and change Public user permissions so you'll be able to query Tags.

![https://res.cloudinary.com/dvrqjaeju/image/upload/v1661531985/Blog_tutorial_2_9_3838f0c00a.png](https://res.cloudinary.com/dvrqjaeju/image/upload/v1661531985/Blog_tutorial_2_9_3838f0c00a.png)

Now you can fetch the data in `getStaticProps` in `index.js` file of your *Next.js* app. Add this at the bottom of `pages/index.js`

```
export async function getStaticProps() {
  const { data } = await client.query({
    query: gql`
      query Posts {
        posts(
          sort: "publishedAt:desc"
          pagination: { limit: 5 }
          filters: { publishedAt: { notNull: true } }
        ) {
          data {
            attributes {
              title
              slug
              cover {
                data {
                  attributes {
                    url
                  }
                }
              }
              excerpt
              tags {
                data {
                  attributes {
                    tagId
                    name
                  }
                }
              }
            }
          }
        }
      }
    `,
  });

  return {
    props: {
      posts: data.posts.data,
    },
  };
}

```

What this does? On every build (`yarn build`) *Next* will perform this query before "rendering" the page/creating the static page output. This means that you will not need any loading indicators on your webpage, because the data will already be fetched. Downside of this is of course that you will have to rebuild the app on every change of content in the CMS. But for personal blogs this is a pretty good and cheap solution.

You are downloading only the most important data that will be shown on the homepage. So let's just create a simple homepage then.

You'll start with creating a new component - a card to preview the Blog Post. Create a new file `components/BlogPostPreview.jsx` (if there's no `component` folder - create it).

```
import Image from "next/image";
import React from "react";

const BlogPostPreview = ({ post }) => {
  return (
    <div className="max-w-sm rounded overflow-hidden shadow-lg">
      <a href="#">
        <Image
          className="w-full"
          src={post.attributes.cover.data.attributes.url}
          alt=""
          width={1000}
          height={480}
          objectFit="cover"
        />
        <div className="px-6 py-4">
          <div className="font-bold text-xl mb-2">{post.attributes.title}</div>
          <p className="text-gray-700 text-base">{post.attributes.excerpt}</p>
        </div>
      </a>
      <div className="px-6 pt-4 pb-2">
        {post.attributes.tags.data.map((tag) => (
          <a href="#" key={tag.attributes.tagId}>
            <span className="inline-block bg-gray-200 rounded-full px-3 py-1 text-sm font-semibold text-gray-700 mr-2 mb-2">
              {tag.attributes.name}
            </span>
          </a>
        ))}
      </div>
    </div>
  );
};

export default BlogPostPreview;

```

You get one `prop` here - post. It has all the fetched information inside. Then we create a simple card with a cover image, title, excerpt and a list of tags. End result will look something like that:

![https://res.cloudinary.com/dvrqjaeju/image/upload/v1661531986/Blog_tutorial_2_10_053405e47c.png](https://res.cloudinary.com/dvrqjaeju/image/upload/v1661531986/Blog_tutorial_2_10_053405e47c.png)

As you can see there are some empty `a` tags in the code - in the later stages of this tutorial series you'll make a use of them, don't worry.

Now it's finally the time to showcase your blog posts. In `pages/index.js` make use of a newly created component.

```
export default function Home({ posts }) {
  return (
    <div className="flex flex-col items-center">
      <h1 className="text-3xl uppercase font-serif font-bold my-8">
        My personal blog
      </h1>
      <section className="grid grid-cols-3 gap-4">
        {posts.map((post) => (
          <BlogPostPreview post={post} key={post.attributes.slug} />
        ))}
      </section>
    </div>
  );
}

```

One more thing before you build this - if you use *Cloudinary* for the images (as was done in the second part of this series) then you will need to add *Cloudinary* domain to the `next.config.js`. This is needed so you'll be able to use `next/image` component instead of `img` tag.

![https://res.cloudinary.com/dvrqjaeju/image/upload/v1661531984/Blog_tutorial_2_11_449746a2de.png](https://res.cloudinary.com/dvrqjaeju/image/upload/v1661531984/Blog_tutorial_2_11_449746a2de.png)

Wow, that was quite a ride. Let's finally build this! Run
`yarn build` and wait for this process to finish. If everything goes well - run `yarn develop` and open your site in the browser `http://localhost:3000/`. You should see something like this:

![https://res.cloudinary.com/dvrqjaeju/image/upload/v1661531988/Blog_tutorial_2_12_55394fe0c9.png](https://res.cloudinary.com/dvrqjaeju/image/upload/v1661531988/Blog_tutorial_2_12_55394fe0c9.png)

Of course don't forget to create a few *tags* and *posts* (you'll need to create a *tag* before you'll be able to use it in a *post*). Also as you added some new required fields - update existing posts so they have every needed information filled.

And that's it. That's the end of the second part of this tutorial series. In the next one you'll create a Blog Post Page. See you then!