on
Rendering Markdown With React
Markdown (MD) is the Rich Text Format of developers. With it we can put on a screen our ideas in a visual way without having to rely on other, more complicated, markup languages.
I want to explore the process to insert markdown on React components and maybe go further each time.
Limitations of Markdown
First, let’s get this out of the way: Markdown limits itself to text and image related content. That means it’s only capable of handling headings (h1
…h6
), text, lists, links and images. Any other elements like main
, section
, div
is out of MD’s scope.
Simple rendering
Here, I want to render text like this:
# Hello World!
This is Markdown.
And have it render like this in HTML:
<h1>Hello World!</h1>
<p>This is Markdown.</p>
Fortunately, there’s already a few libraries that can do this: react-markdown, react-markdown-renderer and react-mde.
I will use react-markdown for this example.
import React from 'react';
import ReactMarkdown from 'react-markdown';
import './styles.css';
const input = `
# Hello World!
This is Markdown.
`;
export default function App() {
return (
<div className='App'>
<ReactMarkdown source={input} />
</div>
);
}
And sure enough, that works! Here is the result in Codesandbox.
That was easy. Now for something a little more complicated.
Rendering from a file
Is it as easy as importing the MD file from our JSX file? It is as easy as importing the MD file from our JSX file!
Using react-markdown, for its simplicity, again.
import React from 'react';
import ReactMarkdown from 'react-markdown';
import HelloWorld from './hello-world.md';
import './styles.css';
export default function App() {
return (
<div className='App'>
<ReactMarkdown source={HelloWorld} />
</div>
);
}
Here is the demo in Codesandbox again. It is even neater than the previous example. Now, something even more difficult.
Rendering multiple files
Finally, I want to read multiple files at the same time and output their render as before.
In addition, I want to be able to output each file on it’s own with a link.
This is done with a router. Fortunately, a react-router exists! Even more so, I’m able to achieve what I wanted just reading at the first example in the page, which is excellent news.
Also, Node has a module called fs
that allows to read the file system, in this case our app environment. And it’s as easy as a couple of lines.
Assuming I have put my file on a a /post
folder, I can execute this code.
const path = './src/posts/'; // path for the MD files folder within our project
const fs = require('fs');
const fileList = fs.readdirSync('./src/posts'); // creates an array of filenames
Then, I want to read the data contents, so I create a function for this.
function getData(file) {
const data = fs.readFileSync(file, 'utf8');
return data ? data : null;
}
This function takes a file and returns its content as a string. I have to declare encoding ("utf-8"
) or else I get an array of number, which is the characters values in ASCII.
Now I can create my app.
export default function App() {
return (
<div className='App'>
<Router>
<ul>
{fileList.map((post) => (
<li>
<Link to={`/${post}`}>{post}</Link>
</li>
))}
</ul>
<Switch>
{fileList.map((post) => (
<Route path={`/${post}`}>
<ReactMarkdown source={getData(`${path}${post}`)} />
</Route>
))}
</Switch>
</Router>
</div>
);
}
I have surround all my code on the <Router>
component for it to work and create separate .map
functions for the link list and the actual routing. The <Switch>
component takes care of rendering the text formatted from markdown. I can’t do it in one fucntion because it seems that the <Switch>
can’t have the links inside of it.
The result, in Codesandbox, is a bit hacky since it uses the file name as the link text.
But I’m satisfied that I could get the result I was looking for with relative ease. All I’m left with is to explore how to read certain lines from a file to extract the title and fix the link text.
I will do that in a later post.