Improving My Blog

(Versão em Português)
A GIF of a stream of images reminiscent of something digital.

Big changes have happened on the Blog 🎉🎉🎉

Before explaining what changed, it's important to explain how the blog was structured.

The blog had 3 routes: the list of posts, the post in English, and the post in Brazilian Portuguese.

Every time I made a new post, I had to create a new route for the English post and another route for the Brazilian Portuguese post, as well as copy and paste all the HTML tags I used in one post to the other.

This didn’t scale and was a lot of work 😔

An image of a white cat, sad with tears in its eyes.

I needed to improve the blog so that my focus could solely be on writing new posts, without worrying about routes, HTML tags, and such.

It would be great if I could write a Markdown file, have the blog automatically convert it into an HTML page, and create a route, all automatically.

While researching online, I discovered gray-matter, and everything changed from there.

Gray-matter

The gray-matter is a library inspired by the blog system of Jekyll. Essentially, it can extract both the content and metadata from a Markdown file.

Let's imagine a file named firstPost.md with this content:

# A Title For The First Post

This is the normal content, just a few things.

Another normal content to complete the blog post.

Now, we just need to read the file with gray-matter, like this:

import fs from "fs";
import matter from "gray-matter";

const file = fs.readFileSync("content/firstPost.md");
const matterResult = matter(file);

The variable matterResult will have this value:

{
  content: '# A Title For The First Post\n' +
    '\n' +
    'This is the normal content, just a few things.\n' +
    '\n' +
    'Another normal content to complete the blog post.\n',
  data: {},
  isEmpty: false,
  excerpt: ''
}

All the content from firstPost.md is stored in the content key, which you can manipulate as usual. Let's adjust our file to conform to the Front Matter standard.

---
title: A Title For The First Post
date: "2024-06-09"
---

This is the normal content, just a few things.

Another normal content to complete the blog post.

The result will be:

{
  content: '\n' +
    'This is the normal content, just a few things.\n' +
    '\n' +
    'Another normal content to complete the blog post.\n',
  data: { title: 'A Title For The First Post', date: '2024-06-09' },
  isEmpty: false,
  excerpt: ''
}

With this, we managed to parse and retrieve the content from a Markdown file 😊

But how do we convert this into HTML tags?

React-markdown

The react-markdown is a library that converts the content of a Markdown file into HTML tags. A paragraph in Markdown becomes a <p> tag, a link in Markdown becomes an <a> tag, and so on.

To use it, it's simple:

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

const file = fs.readFileSync("content/firstPost.md");
const matterResult = matter(file);

const PostComponent = () => (
  <div>
    <h1>{matterResult.data.title}</h1>
    <ReactMarkdown>{matterResult.content}</ReactMarkdown>
  </div>
);

The result will be something like this:

<h1>A Title For The First Post</h1>

<p>This is the normal content, just a few things.</p>

<p>Another normal content to complete the blog post.</p>

This way, all I need to do to create new posts is to create Markdown files with the content.

#Bonus: Inserting Code Blocks

To insert code snippets elegantly into our blog, we can use the react-syntax-highlighter library. It provides syntax highlighting and code formatting.

But how do we integrate this with react-markdown?

It's simple, just use the components property. Imagine our firstPost.md file, but with this code snippet:

---
title: A Title For The First Post
date: "2024-06-09"
---

This is the normal content, just a few things.

Another normal content to complete the blog post.

```
const dogName = "Cacau";
console.log(dogName);
```

Now we need to adjust react-markdown to handle code blocks:

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

const file = fs.readFileSync("content/firstPost.md");
const matterResult = matter(file);

const CodeBlock = ({ children }) => {};

const PostComponent = () => (
  <div>
    <h1>{matterResult.data.title}</h1>
    <ReactMarkdown components={{ code: CodeBlock }}>
      {matterResult.content}
    </ReactMarkdown>
  </div>
);

At this point, whenever ReactMarkdown encounters a code snippet, it will call the CodeBlock function. Now, we can use react-syntax-highlighter within our new function:

import fs from "fs";
import matter from "gray-matter";
import ReactMarkdown from "react-markdown";
import { atomDark } from "react-syntax-highlighter/dist/cjs/styles/prism";
import { Prism as SyntaxHighlighter } from "react-syntax-highlighter";

const file = fs.readFileSync("content/firstPost.md");
const matterResult = matter(file);

const CodeBlock = ({ children }) => {
  return (
    <SyntaxHighlighter language={"javascript"} style={atomDark}>
      {children}
    </SyntaxHighlighter>
  );
};

const PostComponent = () => (
  <div>
    <h1>{matterResult.data.title}</h1>
    <ReactMarkdown components={{ code: CodeBlock }}>
      {matterResult.content}
    </ReactMarkdown>
  </div>
);

The language property indicates the programming language used, and style is the theme applied. In my case, I'm using atomDark.

You can check the available themes in the repository of react-syntax-highlighter.

The generated code will look something like this:

const dogName = "Cacau";

console.log(dogName);

Conclusion

All of this has been a significant change in the blog that has allowed me to be more productive when writing new content. Now, I no longer need to worry about creating HTML tags; all I need to do is write a Markdown file.

Soon, I'll explain how I created dynamic routes using Next.

If you want to delve deeper, the amazing post by Jose Felix explains other points in detail.