Build A Next.js Markdown Blog.
Tags: react, javascript, tutorial
Published: 2020-09-06 19:24:57

**Note:**  This is an advanced Topic So I assume you are already familiar with React, JavaScript, and basics of web development. 

## Next.Js

Nextjs is a [React](https://reactjs.org/) framework. It is the most popular framework because it is easy to use, very flexible, and has a great file-based routing system. It gives you server-side rendering out of the box. 

## Let's Dive in

if You don't want to code along and only want to see the code please checkout [source code](https://github.com/imran-ib/next-markdown-blog.git)

I Had to create blogs for my personal [portfolio site](https://imranirshad.com/blogs). And there are few posts on the internet but I could not find any simple solution.  So I decided To write a simple post about this.  Lets Start

To Create a nextjs app run the following Command in terminal

```bash
npm init next-app
# or
yarn create next-app
```

You can use `npm` or `yarn` package manager but I will be using `yarn` 

Give your project a name. The Package manager will install all the necessary packages. 

Run this command 

`cd YOUR_PROJECT_NAME`

start the Project

`yarn dev`

Your project should be online on port [3000](http://localhost:3000/).  and you should see something like this

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/xhfsh8ucuh9a34ov3zy9.png)

**Great**.  In `pages/index.js` remove everything and paste following code

```react
import React from "react";

const Index = () => {
  return <h1>My First Blog ✍ </h1>;
};

export default Index;
```

create a file `config.json` in the root of the folder and provide site title and description. (This step is for SEO purpose).

```javascript
{
  "title": "Nextjs Blog Site",
  "description": "A Simple Markdown Blog build with Nextjs."
}
```

create a folder in the root directory called `content`. This is where our `.md` files will go.

Now Your Folder structure should look like this

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/d5pqqjfejhqd1w1660st.png)

**components** directory will contain our blogs logic

**content** directory will contain our markdown files

**pages** directory contains our pages (routes)

**public** directory for serving static files (assets)

Lets open `pages/index.js`and bring in the site title and description from `config.json` 

```react
import React from "react";

const Index = (props) => {
  console.log("Index -> props", props);

  return <h1>My First Blog ✍ </h1>;
};

export default Index;


export async function getStaticProps() {
  const siteData = await import(`../config.json`);

  return {
    props: {
      title: siteData.default.title,
      description: siteData.default.description,
    },
  };
}

```

After You Save this page you should see something like this in your browser's console.

`Index -> props {title: "Nextjs Blog Site", description: "A Simple Markdown Blog build with Nextjs."}`.

Ok So What just Happened here. Let's Break it down 

`getStaticProps` [getStaticProps](https://nextjs.org/docs/basic-features/data-fetching#getstaticprops-static-generation) is Nextjs Function which we can call from our `page` . It will return the props to our component. just like we have `props` to our `index`	

We will use this method to fetch our posts later.  

The content will be generated at the build time. If you don't know what it means don't worry about it just keep that in mind that the content will be available prebuild  and we will not be fetching posts every time user visits our site. Pretty Cool right.

we are importing our `config.json` file and returning  title and description as `props` to our `index` components

Next.js also gives us the `Head` component that we can append elements to the `head` of the page. like site title, `meta` tags `links`, and such.

`import Head from 'next/head'`

```react
 <Head>
	<title>My page title</title>
	<meta name="viewport" content="initial-scale=1.0, width=device-width" />
  </Head>
```

Let's add this to our `Index` page

```react
import React from "react";
import Head from "next/head";

const Index = (props) => {
  return (
    <>
      <Head>
        <meta name="viewport" content="width=device-width, initial-scale=1" />
        <meta charSet="utf-8" />
        <meta name="Description" content={props.description}></meta>
        <title>{props.title}</title>
      </Head>
      <h1>My First Blog ✍ </h1>;
    </>
  );
};

export default Index;

export async function getStaticProps() {
  const siteData = await import(`../config.json`);

  return {
    props: {
      title: siteData.default.title,
      description: siteData.default.description,
    },
  };
}

```



After adding `Head ` take a look at the tab of your browser, what do you see? The Title of the site has been updated.

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/3nobpz2c4v2o637q1p1k.png)

Ideally you would want to put this to [Layout](https://github.com/vercel/next.js/tree/canary/examples/layout-component) component but in our case, I think this is fine.

**Now** back to our Blogs.  We Need To add some packages to our project. Run The Following command
`yarn add react-markdown gray-matter raw-loader` 

**OR** 

`npm install react-markdown gray-matter raw-loader`

[react-markdown](https://www.npmjs.com/package/react-markdown) will help us parse and render markdown files 

[gray-matter](https://www.npmjs.com/package/react-markdown)  will parse *front matter* of our blogs. (the part at the top of file between `---`   ) 

We will need this metadata for `title` , `data`  and `description` and `slug`.   You can add anything you like here (maybe hero image URL)

[raw-loader](https://www.npmjs.com/package/raw-loader) will help us import our markdown files.

After we are done with installation we need a little bit of web pack configuration. create a file `next.config.js`	in the root directory 

and paste the following code.

```javascript
module.exports = {
  webpack: function(config) {
    config.module.rules.push({
      test: /\.md$/,
      use: 'raw-loader',
    })
    return config
  }
}
```

**NOTE:** After you Create This file You Must Restart your dev server. 



In the `content` directory create two markdown files 

`content/blog-one.md`

```markdown
---

slug: blog-one
title: My First Blog
description: This Description Of My First Blog.
date: 25-September-2020
---

# h1

## h2

### h3

Normal text

```

`content/blog-two.md`

```markdown
---
slug: blog-two
title: My Second Blog
description: This Description Of My Second Blog.
date: 25-September-2020
---

# h1

## h2

### h3

Normal text

```

First, we will render lists of blogs with titles and descriptions.

in our `index.js`  replace the  `getStaticProps` function with 



```javascript
export async function getStaticProps() {
  const siteData = await import(`../config.json`);
  const fs = require("fs");

  const files = fs.readdirSync(`${process.cwd()}/content`, "utf-8");

  const blogs = files.filter((fn) => fn.endsWith(".md"));

  const data = blogs.map((blog) => {
    const path = `${process.cwd()}/content/${blog}`;
    const rawContent = fs.readFileSync(path, {
      encoding: "utf-8",
    });

    return rawContent;
  });

  return {
    props: {
      data: data,
      title: siteData.default.title,
      description: siteData.default.description,
    },
  };
}
```

`fs` is `nodejs` module that helps us read and write files. we will use `fs.readdirSync` to read the files. 

`process.cwd()` will give us the directory where Next.js is being executed. From Our current directory (root) we want to go in `/content` and read all the files and store them in variable `files`

`endsWith`  [endsWith](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/endsWith) is a JavaScript string method which determines whether a string ends with the characters of a specified string, returning `true` or `false` as appropriate.

we will map over the blogs and get `path` and `rawContent` 

Now Our `index`  components will receive `data` prop.

```react
import React from "react";
import Head from "next/head";
import matter from "gray-matter";
import Link from "next/link";

const Index = ({ data, title, description }) => {
  const RealData = data.map((blog) => matter(blog));
  const ListItems = RealData.map((listItem) => listItem.data);

  return (
    <>
      <Head>
        <meta name="viewport" content="width=device-width, initial-scale=1" />
        <meta charSet="utf-8" />
        <meta name="Description" content={description}></meta>
        <title>{title}</title>
      </Head>
      <h1>My First Blog ✍ </h1>;
      <div>
        <ul>
          {ListItems.map((blog, i) => (
            <li key={i}>
              <Link href={`/${blog.slug}`}>
                <a>{blog.title}</a>
              </Link>
                <p>{blog.description}</p>
            </li>
          ))}
        </ul>
      </div>
    </>
  );
};
```
We are mapping over `data` and formatting each blog with `gray-matter`;

At this point, Your should see something like this 

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/wlf0ue8ko8evt6n1imvz.png)

if You click on *My First Blog* it should take you to `/blog-one` or whatever you named your blog 
## Dynamic Routes

we May have fifty different blogs. we Don't want to page for each blog.  if we make a file in pages directory `blog`  we can navigate to `localhost:3000/blog`. But if add square brackets around blog (name of file) like so `[blog].js`  we have a dynamic route. 

the route will end up to `localhost:3000/:blog `

Create a new page `[blog].js` in pages directory

```react
import react from "react";

const Blog = () => {
  return <h1>Blog</h1>;
};

export default Blog;

```

Now Lets Get the file from `content` Directory

```react
Blog.getInitialProps = async (context) => {
  const { blog } = context.query;
  
  const content = await import(`../content/${blog}.md`);
  const data = matter(content.default);

  return { ...data };
};

```

You Should have `content` and `data` prop available in the `Blog` component

```react
import react from "react";
import matter from "gray-matter";
import ReactMarkdown from "react-markdown";

const Blog = ({ content, data }) => {
  const frontmatter = data;

  return (
    <>
      <h1>{frontmatter.title}</h1>
      <h3>{frontmatter.description}</h3>
      <ReactMarkdown escapeHtml={true} source={content} />
    </>
  );
};

export default Blog;

Blog.getInitialProps = async (context) => {
  const { blog } = context.query;
  // Import our .md file using the `slug` from the URL
  const content = await import(`../content/${blog}.md`);
  const data = matter(content.default);

  return { ...data };
};

```

Oh My Goodness. It is working. 
##  What about Code 

For Code formatting, we will use the `react-syntax-highlighter` package

`yarn add react-syntax-highlighter` 

Now Create a code Block in `[blog].js` and pass it to `ReactMarkdown`

```react
import { Prism as SyntaxHighlighter } from "react-syntax-highlighter";

const CodeBlock = ({ language, value }) => {
  return (
    <SyntaxHighlighter showLineNumbers={true} language={language}>
      {value}
    </SyntaxHighlighter>
  );
};
```

Now Your `[blog].js` should look like this

```react
import react from "react";
import matter from "gray-matter";
import ReactMarkdown from "react-markdown";
import { Prism as SyntaxHighlighter } from "react-syntax-highlighter";

const CodeBlock = ({ language, value }) => {
  return (
    <SyntaxHighlighter showLineNumbers={true} language={language}>
      {value}
    </SyntaxHighlighter>
  );
};

const Blog = ({ content, data }) => {
  const frontmatter = data;

  return (
    <>
      <h1>{frontmatter.title}</h1>
      <h3>{frontmatter.description}</h3>
      <ReactMarkdown
        escapeHtml={true}
        source={content}
        renderers={{ code: CodeBlock }}
      />
    </>
  );
};

export default Blog;

Blog.getInitialProps = async (context) => {
  const { blog } = context.query;
  // Import our .md file using the `slug` from the URL
  const content = await import(`../content/${blog}.md`);
  const data = matter(content.default);

  return { ...data };
};

```

Create a new File in the content directory `conding-blog.md`

```markdown
---
slug: coding-blog
title: Coding blog
author: Imran Irshad
description: Coding Post For Beautiful Code
date: 30-September-2020
---

# React Functional Component

​```jsx
import React from "react";

const CoolComponent = () => <div>I'm a cool component!!</div>;

export default CoolComponent;
​```

```

Now If  Click `coding-blog`  
![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/odmz8jspshglv9fbdg3j.png)

## Images

Create a new file in `content`  named `image-blog`

```markdown
---
slug: image-blog
title: Image Blog
description: See How Images Looks in our Blog
date: 30-September-2020
---

# Image

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/goubishlyb1h9sarh7vo.png)

## Conclusion

Nextjs is awesome and very flexible. You can create really cool stuff with it. I hope you learned a thing or two from this post.