Markdown conversion in Next.js

Posted by Ashutosh on June 14, 2021


So far:

  1. We have set up a page to list blogs.
  2. Link blogs to their respective links

However, if we click on the individual blog, it is not showing relevant content. In this article, we fix this issue. And our blog URL presents relevant data to the users.

When this series started, I downloaded the content (in Markdown) from dev.to in JSON file saved in a data folder. Now, we need to read the JSON file and load it in our blog.

Create the "Utilities" folder under the root of the application folder, and add the FileRead.js file under Utilities.


import fs from 'fs';
import path from "path";
// Get All Blogs Details
export default function getAllBlogsData(fileName) {
let blog = new Array()
const data = getFileData(fileName) for (let index in data) {
const catList = data[index].cached_tag_list blog.push({
title: data[index].title,
date: data[index].published_at,
body: data[index].body_markdown,
description: data[index].description,
categories: catList,
slug: data[index].slug
});
} // Reverse an Array
const reverseData = blog.slice(0).reverse() return reverseData
} // Get Individual Blog Data from slug
export function getBlogData(fileName, slug) {
const data = getFileData(fileName) for (let index in data) {
if (data[index].slug === slug) {
return data[index]
}
}
return 0
} function getFileData(fileName) {
const fileToRead = path.join(process.cwd(), fileName)
return ( JSON.parse(fs.readFileSync(fileToRead)) )
}

We created a JavaScript file and, add the following functions:

  • getAllBlogsData() - [To read all the data from the JSON file]
  • getBlogData() - [To read the data for the individual blog]
  • getFileData() - [Get all the data from the file in the JSON format]

In the index.js, first, import the getAllBlogsData function from the FileRead.js.


import getAllBlogsData from "../Utilities/FileRead";

And then modify the getStaticProps(context) subroutine.


export async function getStaticProps(context) {
    const data = getAllBlogsData('/data/articles.json')
    return  {
        props: {
            sample_blogs: data
        }
    }
}

Hmm, now our index.js file is cleaner :).

Open the file [slug].js and update the file with the following code:



import BlogDetailsComponent from "../../components/Blog/BlogDetailsComponent";
import {getBlogData} from "../../Utilities/FileRead";
import CategoriesComponent from "../../components/Categories/CategoriesComponent";
import BlogComponent from "../../components/Blog/BlogComponent";
function IndividualBlogPage(props) {
    return (
        <div className="flex flex-auto">
            <CategoriesComponent />
            <BlogDetailsComponent blog={props}/>
            <div></div>
        </div>
    )
}
export async function getServerSideProps(context) {
    const fileName = '/data/articles.json';
    const { params } = context
    const blogData = getBlogData(fileName, params.slug)
    return {
        props:{
            title: blogData.title,
            date: blogData.published_at,
            body: blogData.body_markdown,
            description: blogData.description,
            categories: blogData.cached_tag_list,
            slug: params.slug
        }
    }
}
export default IndividualBlogPage

Up to this point, we pass the slug to the component and, we'll get the result for the individual blog post.

Before proceeding, we also need to some styles to our blog.

Create a new file BlogStyles.js under the blog folder with the following code:


import CSS from 'styled-jsx/css'
export default css.global `
    .blogs ul > li > ul > li {
      font-weight: 400;
      font-size: 0.8em;
    }
    p, ul, li {
      font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Helvetica Neue', sans-serif, Arial;
      letter-spacing: calc(0px * 16);
      line-height: calc(2.5px * 16);
      color: rgb(14, 16, 26);
      -webkit-font-smoothing: antialiased;
      animation-iteration-count: 1!important;
    }
    .blogs h1, p, ul {
      margin-left: 1.5em;
    }
    .blogs p { 
      display: block;
      font-size: calc(1.375px * 16);
      margin-block-start: 1em;
      margin-block-end: 1em;
      margin-inline-start: 1.5em;
      margin-inline-end: 1.5em;
      font-weight: 400;
    }
    .blogs ul {
      margin-left: 1.5em;
      font-size: 1.2em;
      list-style-type: disc;
    }
    .blogs ul > li {
      margin-left: 1.5em;
      font-weight: 400;
    }

    @media (min-width: 768px) {
      .blogs h1 {
        font-size: 1.5em;
        text-align: left;
        margin: 1.5em;
        font-weight: bold;
      }
      .blogs h2 {
        font-size: 1.2em;
        text-align: left;
        margin: 1.5em;
        font-weight: bold;
      }
    }
`


Now, we need to pass the javascript object to the BlogDetailsComponent.js file.



import ReactMarkdown from 'react-markdown'; import styles from './BlogStyles' function BlogDetailsComponent(props) { return ( <div className={`ml-10 mt-4 w-3/5 container mr-4 shadow rounded border blogs `}> <ReactMarkdown >{props.blog.body}</ReactMarkdown> <style jsx global> {styles} </style> </div> ) } export default BlogDetailsComponent



If the 'react-markdown' package doesn't exist, please install it using the npm.

We also have some code written for the tutorials but it is not showing correctly as of now.

Please install


npm install react-syntax-highlighter



And open the BlogDetailsComponent.js and add the following lines at the top of the file:


import { Prism as SyntaxHighlighter } from "react-syntax-highlighter";
import { atomDark } from 'react-syntax-highlighter/dist/cjs/styles/prism';



And modify the function:


function BlogDetailsComponent(props) {
    // let body = props.blog.body
    // let res = body.match(/<img.* \/>/g);
    // console.log(res)
    const customComponents = {
        code(code) {
            const { className, children } =  code
            let language = new Array();
            if (className) {
                language = className.split("-");
            }
            return (
                <SyntaxHighlighter language={language[language.length -1]} children={children} style={atomDark}   />
            )
        }
    }
    return (
        <div className={`ml-10 mt-4 w-3/5 container mr-4 shadow rounded border blogs `}>
            <ReactMarkdown components={customComponents}>{props.blog.body}</ReactMarkdown>
            <style jsx global>
                {styles}
            </style>
        </div>
    )

export default BlogDetailsComponent



Refresh the page and, you'll see the highlighted syntax in our code blocks.

That's all for now. In the upcoming article, we fix the broken images.