#AD "Ten++ Ways to Make Money as a Developer" eBook by Florin Pop

Build a static blog from markdown files with Next.js

Last modified October 26th 2021 | #nextjs | GitHub Source Code [GitHub]

In this tutorial we’ll be building a blog using Next.js with the data for each of the individual blog posts loaded from markdown files. While there are many Next.js blog starter code bases available building from scratch is a great learning experience and shouldn’t take too long to get up and running.

Let’s get started by creating a new Next.js application:

npx create-next-app next-md-blog

We’ll also need a couple of dependencies so let’s go ahead and install those as well:

npm install gray-matter marked
  • gray-matter – Parses front-matter from markdown files.
  • marked – Compiles markdown into HTML.

Next let’s create a layout component that’ll load a global header and footer as is common practice when building a blog or websites in general. Create a new components folder with a Layout.js, Header.js and Footer.js files.

components/Layout.js

import Header from "./Header"; import Footer from "./Footer"; const Layout = (props) => ( <> <Header /> <main>{props.children}</main> <Footer /> </> ); export default Layout;
Code language: JavaScript (javascript)

components/Header.js

import Link from "next/link"; const Header = () => { return ( <header> <Link href="/"> <a>Next.js Blog</a> </Link> </header> ); }; export default Header;
Code language: JavaScript (javascript)

components/Footer.js

const Footer = () => { return ( <footer> <p>&copy; {new Date().getFullYear()} - Powered by Next.js</p> </footer> ); }; export default Footer;
Code language: JavaScript (javascript)

Next let’s create a sample markdown file for a blog post. This along with all subsequent blog post markdown files will need to be saved inside a new posts folder.

posts/hello-world.md

--- title: 'Hello World' teaser: 'This is a short teaser for the first blog post.' published: 'January 1, 2021' thumbnail: '/images/stock-01.jpg' --- Accusamus perferendis **voluptatibus** enim et. Cupiditate dolorum delectus temporibus in [earum cumque](https://w3collective.com) sunt qui. Quia quidem dolores delectus ut beatae id. Totam eius labore ut. Dolores doloribus ut ab minima fugiat eum atque. Sit ullam vel itaque minima non officia sunt ab.
Code language: Markdown (markdown)

The images folder for the thumbnails should be located within the public folder in the root of the application. Create a couple more files like this and then we’re ready to create a Post.js component that’ll display teasers for each of these blog posts on the homepage.

components/Post.js

import Link from "next/link"; export default function Post({ post }) { return ( <div className="post-teaser"> <Link href={`/blog/${post.slug}`}> <a><h3>{post.frontmatter.title}</h3></a> </Link> <img src={post.frontmatter.thumbnail} /> <p>{post.frontmatter.published}</p> <p>{post.frontmatter.teaser}</p> <hr /> </div> ); }
Code language: JavaScript (javascript)

Here we’re getting the front matter data (title, teaser, published, thumbnail) and outputting that data inside some HTML markup. Now to start pulling everything together by updating the pages/index.js file as follows:

import Head from "next/head"; import fs from "fs"; import path from "path"; import matter from "gray-matter"; import Layout from "../components/Layout"; import Post from "../components/Post"; export default function Index({ posts }) { return ( <> <Head> <title>Next.js Blog</title> <meta name="description" content="A simple blog powered by Next.js" /> </Head> <Layout> <div className="posts"> {posts.map((post, index) => ( <Post post={post} key={index} /> ))} </div> </Layout> </> ); } export async function getStaticProps() { const files = fs.readdirSync(path.join("posts")); const sortOrder = (a, z) => { return new Date(z.frontmatter.published) - new Date(a.frontmatter.published) } const posts = files.map((filename) => { const slug = filename.replace(".md", ""); const markdown = fs.readFileSync( path.join("posts", filename), "utf-8" ); const { data: frontmatter } = matter(markdown); return { slug, frontmatter, }; }); return { props: { posts: posts.sort(sortOrder), }, }; }
Code language: JavaScript (javascript)

Now all that’s left it to create the file to display the individual blog posts.

pages/blog/[slug].js

import fs from "fs"; import path from "path"; import matter from "gray-matter"; import marked from "marked"; import Head from "next/head"; import Layout from "/components/Layout"; export default function Post({ frontmatter: { title, published, teaser }, content, }) { return ( <> <Head> <title>{title}</title> <meta name="description" content={teaser} /> </Head> <Layout> <h1>{title}</h1> <p>{published}</p> <div dangerouslySetInnerHTML={{ __html: marked(content) }}></div> </Layout> </> ); } export async function getStaticPaths() { const files = fs.readdirSync(path.join("posts")); const paths = files.map((filename) => ({ params: { slug: filename.replace(".md", ""), }, })); return { paths, fallback: false, }; } export async function getStaticProps({ params: { slug } }) { const markdown = fs.readFileSync(path.join("posts", slug + ".md"), "utf-8"); const { data: frontmatter, content } = matter(markdown); return { props: { frontmatter, slug, content, }, }; }
Code language: JavaScript (javascript)

That’s all for this tutorial, you should now have a functioning blog using Next.js which you can easily add new posts using markdown files. We’ve only just scratched the surface of what’s with Next.js, moving forward we’ll be publishing many more tutorials on the topic that you’ll be able to find here.

Related Posts