Nerd For Tech
Published in

Nerd For Tech

Dynamic Markdown Blogs in Next.js/React using gray-matter, react-markdown and react-syntax-highlighter

You are looking for a sustainable, scalable solution to create dynamic blogs whether it be for your website, or someone else’s, then react-markdown is the solution for it. This blog assumes, obviously, you are working with Next.js/React.

To get started with, create a directory say ‘_snippets’ and in this directory create a sample markdown file, say ‘Dynamic Markdown Blogs in Next.js using gray-matter, react-markdown and react-syntax-highlighter.md’. The slug of the route for the particular blog would be derived from the markdown file name itself.

Here’s the sample markdown for you to copy:

---
title: 'Dynamic Markdown Blogs in Next.js/React using gray-matter, react-markdown and react-syntax-highlighter'
excerpt: 'Implementing markdown blogs in Next.js/React with remark-markdown in just 5 minutes. Thinking of using strings? Nah, they are too old, use markdown. Export statically to achieve great perfomance.'
date: '2021-01-06'
---

You are looking for a sustainable, scalable solution to create dynamic blogs whether it be for your website, or someone else's, then react-markdown is the solution for it. This blog assumes, obviously, you are working with Next.js/React.

Done with the content 🚀 & proceed by executing the following on your terminal:

npm i react-syntax-highlighter gray-matter react-markdown

Done with the installation 🚀, proceed by creating the following component:

// File: /components/MarkdownHighlight.js

import React from 'react'
import { Prism as SyntaxHighlighter } from 'react-syntax-highlighter'
import {
materialDark,
materialLight,
} from 'react-syntax-highlighter/dist/cjs/styles/prism'

const MarkdownHighlight = ({ darkMode, language, value }) => {
return (
<SyntaxHighlighter
language={language}
style={darkMode ? materialLight : materialDark}
>
{value}
</SyntaxHighlighter>
)
}

export default MarkdownHighlight

This component is gonna take care of highlighting your markdown code blocks whether html, css, js or markdown. Browse react-syntax-highlighter/dist/cjs/styles/prism for exploring different kind of filters such as {coy}, {dark} apart from {materialDark}, {materialLight}.

The code below will be handling formation of blog data from the markdown files. Explanations for each function has been added.

// File: /lib/markdown.js

import { join } from 'path'
import fs from 'fs'
import matter from 'gray-matter'

// Directory of snippets
const pagesDirectory = join(process.cwd(), '_snippets')

// Form slugs from the markdown names
export function getSlugsFromDirectory(dir) {
return fs.readdirSync(dir)
}

/**
* Gets the contents of a file
* The gray-matter (metadata at the top of the file) will be
* added to the item object, the content will be in
* item.content and the file name (slug) will be in item.slug.
*/
export function getBySlug(dir, slug, fields = []) {
const realSlug = slug.replace(/\.md$/, '')
const fullPath = join(dir, `${realSlug}.md`)
const fileContents = fs.readFileSync(fullPath, 'utf8')
const { data, content } = matter(fileContents)
const items = {}
fields.forEach((field) => {
if (field === 'slug') {
items[field] = realSlug
}
if (field === 'content') {
items[field] = content
}
if (data[field]) {
items[field] = data[field]
}
})
return items
}

// Returns contents of a page in the _snippets directory
export function getPageContentBySlug(slug, fields = []) {
return getBySlug(pagesDirectory, slug, fields)
}

// Returns pages in the _snippets directory
export function getAllSnippets(fields = []) {
const slugs = getSlugsFromDirectory(pagesDirectory)
const pages = slugs.map((slug) => getPageContentBySlug(slug, fields))
return pages
}

Done with the creating helping functions 🚀, let’s proceed by creating the dynamic blog page:

// File: /pages/snippet/[slug].js

import { useRouter } from 'next/router'
import ReactMarkdown from 'react-markdown'
import MarkdownHighlight from '@/components/MarkdownHighlight'
import { getAllSnippets, getPageContentBySlug } from '@/lib/markdown'

const Snippet = ({ page, darkMode }) => {
const router = new useRouter()
return router.isFallback ? (
<div>Loading...</div>
) : (
<div className="container">
<h1>{page.title}</h1>
<div className={styles['mt-3']}>
<div className={darkMode ? 'text-white' : 'text-dark'}>
<ReactMarkdown
source={page.content}
renderers={{
code: ({ language, value }) => {
/* Automatically takes in the language & value from the markdown file when: ```<html/css/js>
Content here
``` in the markdown file*/
return (
<MarkdownHighlight
language={language}
value={value}
darkMode={darkMode}
/>
)
},
}}
/>
</div>
</div>
</div>
)
}

export default Snippet

export async function getStaticProps({ params }) {
const { slug } = params
const page = getPageContentBySlug(slug, [
'title',
'image',
'slug',
'content',
'date',
])
return {
props: {
page: {
...page,
markdown: page.content,
},
},
}
}

export async function getStaticPaths() {
const posts = getAllSnippets(['slug'])
const paths = posts.map(({ slug }) => ({
params: {
slug,
},
}))
return {
paths,
fallback: 'blocking'
}
}

All set! Hope you liked it :)

--

--

--

NFT is an Educational Media House. Our mission is to bring the invaluable knowledge and experiences of experts from all over the world to the novice. To know more about us, visit https://www.nerdfortech.org/.

Recommended from Medium

React Presentational Components and Smart Components Explained…

Merge Sort (avascript)

zsh: command not found: expo

error: no ‘operator++(int)’ declared for postfix ‘++’ [-fpermissive]

‘doc-coverage’ — A Documentation Coverage Tool for Javascript

State Updates in React

Just Do It Already — Conquering Javascript Line by Line.

Selection Sort (Javascript)

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store
Rishi Raj Jain

Rishi Raj Jain

Solutions Engineer @Layer0 by Limelight | @Storyblok Ambassador | Ex-SWE @dpschool_io ✨

More from Medium

Basic Authentication with AWS Cognito and Next.js

Serverless NextJS/GPT-3 Template

Page Refresh issue in NextJS with S3 + CloudFront

How Serverless Deployment Works: Best Practices, Principles, Examples