DEV Community

Alex Merced
Alex Merced

Posted on

2022 Tutorial on Creating a Markdown Blog with NextJS

Why Do you want a markdown blog

  • Markdown makes it much easier to express formatting and focus on writing
  • Updating your blog means committing to github, yay for your heat map
  • More practice with markdown which is good writing documentation.
  • Since markdown blogs are typically statically rendered they are fast and great for SEO

In this tutorial I will be making a markdown blog with NextJS. I do have a tool called create-markdown-blog which can help spin you up blog template using slightly older versions of Next, Nuxt, Gatsby, Sapper and so forth if you don't want to assemble it from scratch. Also, AstroJS has a markdown blog template you can easily generate with the command npm init astro.

Making the Blog

  • Generate a new nextJS app npm init next-app@latest

  • cd into the new folder

  • create a folder called components

  • create a Header and Footer component

/components/Header.js

function Header (props){
    return <h1>Header Component</h1>
}

export default Header
Enter fullscreen mode Exit fullscreen mode

/components/Footer.js

function Footer (props){
    return <h1>Footer Component</h1>
}

export default Footer
Enter fullscreen mode Exit fullscreen mode
  • create a Layout component

/components/Layout.js

import Link from "next/link";
import Header from "./Header";
import Footer from "./Footer";

export default function Layout({ children }) {
  return (
    <div>
      <Header />
      {children}
      <Footer />
    </div>
  );
}
Enter fullscreen mode Exit fullscreen mode
  • Add the layout component so it shows up on every page.

/pages/_app.js

import "../styles/globals.css";
import Layout from "../components/Layout";

function MyApp({ Component, pageProps }) {
  return (
    <Layout>
      <Component {...pageProps} />
    </Layout>
  );
}

export default MyApp;
Enter fullscreen mode Exit fullscreen mode
  • create a folder called posts

Markdown Files

In this posts folder is where you'll add markdown files, make a file called example-post.md with the following.

---
title: "This is an example post"
author: "Alex Merced"
category: "example"
date: "2022-03-13"
bannerImage: "url-to-image.png"
tags:
    - example
---

## This is an example blog post

This is sample content. The section above is called Frontmatter where we can add post metadata like title and author. You can add as little or as many properties in the frontmatter using YAML syntax.
Enter fullscreen mode Exit fullscreen mode

Creating the Blog List

We will create a page /blog that will act as where all our blog posts will be listed so created /pages/blog.js with the following. Also make sure to install grey-matter so can get the Frontmatter (the YAML above each blog post).

npm install gray-matter

/pages/blog.js

import fs from 'fs';
import matter from 'gray-matter';
import Image from 'next/image';
import Link from 'next/link';

// The Blog Page Content
export default function Blog({posts}){
    return <main>
        {posts.map(post => {
            //extract slug and frontmatter
            const {slug, frontmatter} = post
            //extract frontmatter properties
            const {title, author, category, date, bannerImage, tags} = frontmatter

            //JSX for individual blog listing
            return <article key={title}>
                <Link href={`/posts/${slug}`}>
                    <h1>{title}</h1>
                </Link>
                <h3>{author}</h3>
                <h3>{date}</h3>
            </article>
        })}
    </main>
}


//Generating the Static Props for the Blog Page
export async function getStaticProps(){
    // get list of files from the posts folder
    const files = fs.readdirSync('posts');

    // get frontmatter & slug from each post
    const posts = files.map((fileName) => {
        const slug = fileName.replace('.md', '');
        const readFile = fs.readFileSync(`posts/${fileName}`, 'utf-8');
        const { data: frontmatter } = matter(readFile);

        return {
          slug,
          frontmatter,
        };
    });

    // Return the pages static props
    return {
        props: {
          posts,
        },
    };
}
Enter fullscreen mode Exit fullscreen mode

Making the Individual Post pages

We'll need to make a dynamic route that will create a page for every post we have. We start by creating /pages/posts/[slug].js the [] denotes the dynamic route to nextjs. The conents of this page should be the following:

Also make sure to install markdown-it

npm install markdown-it

/pages/posts/[slug].js

import fs from "fs";
import matter from "gray-matter";
import md from 'markdown-it';

// The page for each post
export default function Post({frontmatter, content}) {

    const {title, author, category, date, bannerImage, tags} = frontmatter

    return <main>
        <img src={bannerImage}/>
        <h1>{title}</h1>
        <h2>{author} || {date}</h2>
        <h3>{category} || {tags.join()}</h3>
        <div dangerouslySetInnerHTML={{ __html: md().render(content) }} />
    </main>
}

// Generating the paths for each post
export async function getStaticPaths() {
  // Get list of all files from our posts directory
  const files = fs.readdirSync("posts");
  // Generate a path for each one
  const paths = files.map((fileName) => ({
    params: {
      slug: fileName.replace(".md", ""),
    },
  }));
  // return list of paths
  return {
    paths,
    fallback: false,
  };
}


// Generate the static props for the page
export async function getStaticProps({ params: { slug } }) {
    const fileName = fs.readFileSync(`posts/${slug}.md`, 'utf-8');
    const { data: frontmatter, content } = matter(fileName);
    return {
      props: {
        frontmatter,
        content,
      },
    };
  }
Enter fullscreen mode Exit fullscreen mode

That's it, the blog functionality should be working. Now you just need to style it, tweak it to your liking and write more posts! Since this is a NextJS project deployment will be as simple as connecting a github repo to Vercel, how awesome is that!

Top comments (9)

Collapse
 
surjithctly profile image
Surjith S M

Thanks, How do we do if we want to show a widget on the Home page? Do we still need to use getStaticprops on the homepage again?

Collapse
 
alexmercedcoder profile image
Alex Merced

depends on the widget, if your talking about a youtube widget I'd just paste the code right into the page. If the widget relies on data that needs to be pre-fetched at build time then use getStaticProps

Collapse
 
bedrock02 profile image
Steven J.

Is there another way to set the html? I see that we are using dangerouslySetInnerHTML.

Collapse
 
pavindulakshan profile image
Pavindu Lakshan

I have the same concern. May be we can use something like npmjs.com/package/react-markdown to render markdown as html securely.

@alexmercedcoder it would be great if you can update the blog post with some alternative to dangerouslySetInnerHTML :)

Collapse
 
bedrock02 profile image
Steven J.

@pavindulakshan Yup that package checks out. See the commit diff github.com/Bedrock02/tech-blog/com...

Collapse
 
open852134 profile image
open852134

Is there any possibility I can put those images in the same folder kind like the structure below:

  • posts
    • nextjs-page-options-and-how-they-work
      • index.md
      • 22-09-2021.jpg
    • nextjs-posting-data-to-postgres-through-prisma
      • index.md
      • 27-10-2021.jpg
Collapse
 
davidkartuzinski profile image
David Kartuzinski

I had the same question as I am moving from Gatsby to Nextjs, I found this blogpost that shows how to do it. I haven't done it YET, but I will. Hope this helps you.

How to store images like Gatsby next to the .md/.mdx file

Collapse
 
ssangyongsports profile image
Peter yang • Edited

# title in /post/example-post not working. How tto fix this error

Collapse
 
ilhamgum profile image
Ilham Gumilang

This is awesome, thanks a lot