Previewing Markdown code with React
Tags: react, webdev, javascript, markdown
Published: 2022-12-15 02:02:40

by [Eli Musa](https://blog.openreplay.com/authors/eli-musa)

Markdown, created by John Gruber in 2004, is one of the most popular markup languages. The Markdown format converts plain text into HTML and is easy to write and read. Markdown is popular because the syntax used is simple to grasp. In addition, people who do not know HTML can use Markdown and convert their documents automatically into HTML pages.

We aim to create a Markdown previewer with Next.js and customize the components using Styled-Components. By the end of this article, you will be able to implement the previewer on a blog project.


## Development Environment Setup

The most important thing is ensuring that we have a Node.js environment setup in our Operating System. We can first check the version of Node.js installed.

```bash
node -v
```

If you are not yet a Node.js user, follow the instructions in this [link](https://nodejs.org/en/download/). The process may differ depending on the operating system. After successfully installing Node.js, we can begin by initializing our project. In this phase, we will create a Next.js app using create-next-app.
Next.js community has made the installation process easier by implementing program prompts that guide developers.
Open the terminal by typing "cmd" in the program search area, then use the command below to create a Next.js app.

```bash
npx create-next-app@latest
```

If the command doesn't work for you, you can try these two alternatives.

```bash
yarn create-next-app
```

or

```bash
pnpm create-next-app
```

In our case, we will name our project `markdownpreviewer`, and since we only need JavaScript, we will not include the Typescript template; select "no" when prompted.

We now have a new directory called `markdownpreviewer`. Let's navigate into it:

```bash
cd markdownpreviewer
```

Then, run the project using the command:

```bash
npm run dev
```

The web app is now visible in our browser.

![1](https://blog.openreplay.com/images/previewing-markdown-code-with-react/images/image01.png)

## Installing Required Packages

The next step is installing the packages needed for our project. The first package will be `Styled-Components`. This is our choice for styling our application, and it will also help us when customizing our markdown output.

```bash
npm i styled-components
```

We will use `react-markdown` for our markdown rendering logic. Let's install the package.

```bash
npm i react-markdown
```

Now we are ready for the fun part. Let's ensure that the room has no distractions before starting our hacking endeavor.

## Building the Markdown previewer

Since we have already installed the dependencies, we can open the editor. With Visual Studio Code installed, we can open it from our command prompt using the command below.

```bash
cd markdownpreviewer
code .
```

The editor will open with the project as the current workspace when we press enter.

![2](https://blog.openreplay.com/images/previewing-markdown-code-with-react/images/image02.png)

The first thing that we should do is re-create our project structure. Mostly, it's recommended that we create a source folder, `src`, and get every project file from that folder. The following is the produced structure.

![3](https://blog.openreplay.com/images/previewing-markdown-code-with-react/images/image03.png)

If you are having trouble setting up Styled-Components, I have you covered. Let's clear the files in our styles folder and paste this code into a new `Globals.js` file.

```javascript
// Globals.js

import { createGlobalStyle } from "styled-components";

const GlobalStyle = createGlobalStyle`
body{
background:white;
margin:0;
padding:0;
}
`;

export default GlobalStyle;
```
The second step is importing and using our styles in our root page, which is `_app.js` in our `pages` folder.

```javascript
// _app.js
import { Fragment } from "react";
import GlobalStyle from "../styles/Globals";

function MyApp({ Component, pageProps }) {
  return (
    <Fragment>
      <GlobalStyle />
      <Component {...pageProps} />
    </Fragment>
  );
}

export default MyApp;
```

After setting the global styled-components, let's create a `Previewer.js` file in our components folder. Here we will create a functional component that will hold the logic needed for both our editor and previewer. We will start by importing our modules at the top of our file.

```javascript
// Previewer.js
import React from "react";
import { useState } from "react";
import styled from "styled-components";
import ReactMarkdown from "react-markdown";
```

After the imports, create an Editortool functional component in that file by adding the code below.

```javascript
export function Editortool() {
  const [form, setForm] = useState({ markdown: "" });

  const handleChange = (e) => {
    setForm({
      ...form,
      [e.target.name]: e.target.value,
    });
  };

  return (
    <div>
      <h1>Markdown Previewer</h1>
      <div>
        <textarea
          id="textarea_id"
          name= "markdown"
          type= "text"
          onChange={handleChange}
        />
        <ReactMarkdown>{form.markdown}</ReactMarkdown>
      </div>
    </div>
  );
}
```

We must define the method we want to use when handling our state. Since we have a functional component, we will use the `useState` hook. Define the variable `form` as our current state, and update it through the `setForm` function. We then set our current state to an empty string with `useState({markdown:"})`.

The `handleChange` arrow function is reserved for handling the change effect when typing in our textarea. The change of an input element is then detected through the `onChange` event. After creating the component, we import it into our `index.js` page, which serves as our home page.

```javascript
// index.js
import Editortool from "../components/Editor"

export default function Home() {
  return (
    <>
      <Editortool />
    </>
  );
}

```

If we run our code, we get that the editor and previewer are working, but it still needs to be styled. Let's execute the code using the command below.

```bash
npm run dev
```

Clicking on the link provided opens the browser.

![4](https://blog.openreplay.com/images/previewing-markdown-code-with-react/images/image04.png)

Let's create our styling components. The styled-components syntax involves creating a variable and assigning the `styled` function to it. We use the CSS inside backticks as Styled-Components.

```javascript
const Styledexample = styled.div`
  position: absolute;
  top: 60px;
  min-height: 90%;
  width: 90vw;
  left: 5%;
`;
```

We will need three Styled-Components for our styling; Markdowncontainer, Editor, and Reviewarea components. Let's add them to the code.

```javascript
import React from "react";
import { useState } from "react";
import styled from "styled-components";
import ReactMarkdown from "react-markdown";

const H1 = styled.h1`
  position: absolute;
  left: 35%;
`;

const MarkdownContainer = styled.div`
  position: absolute;
  top: 60px;
  min-height: 90%;
  width: 90vw;
  left: 5%;
  display: grid;
  grid-template-columns: 1fr 1fr;
  grid-gap: 8px;
  border: 1px solid black;
  box-sizing: border-box;
`;

const Editor = styled.textarea`
  top: 0;
  font-size: 18px;
  background: #1e2126;
  color: white;
  border: 1px solid #c4c4c4;
  box-sizing: border-box;
  max-width: 45vw;
`;

const Reviewarea = styled(ReactMarkdown)`
  top: 0;
  padding: 20px;
  font-size: 18px;
  background: white;
  border: 1px solid blue;
  box-sizing: border-box;
  max-width: 45vw;
`;

export function Editortool() {
  const [form, setForm] = useState({ markdown: "" });

  const handleChange = (e) => {
    setForm({
      ...form,
      [e.target.name]: e.target.value,
    });
  };
  return (
    <div>
      <H1>Markdown Previewer</H1>
      <MarkdownContainer>
        <Editor
          id="textarea_id"
          name= "markdown"
          type= "text"
          onChange={handleChange}
        />
        <Reviewarea components={CodeBlock} remarkPlugins={[remarkGfm]}>
          {form.markdown}
        </Reviewarea>
      </MarkdownContainer>
    </div>
  );
}

```

The CSS in our styled component uses the grid system to arrange the elements. Using Styled-Components is just like using CSS in JavaScript directly. The final output is created after running the code.

![5](https://blog.openreplay.com/images/previewing-markdown-code-with-react/images/image05.png)

With fewer lines of code, we have our editor and previewer running. In the next step, we will customize the different components rendered by our previewer; fonts, font family, image size, tables, and code snippets. If you are new to Styled-Components and wonder how, this is the easiest part of this tutorial.


<h2>Open Source Session Replay</h2> 

_<a href="https://github.com/openreplay/openreplay" target="_blank">OpenReplay</a> is an open-source, session replay suite that lets you see what users do on your web app, helping you troubleshoot issues faster. OpenReplay is self-hosted for full control over your data._

![OpenReplay](https://blog.openreplay.com/banner-blog_1oYPGT.png)

_Start enjoying your debugging experience - <a href="https://github.com/openreplay/openreplay" >start using OpenReplay for free</a>._




## Customize Components with Styled Components

Let's now work on styling our components.

### Changing the styling of the text

The need to change the font size and family may arise because of a client's specification or the target of a specific audience. Let's see how we go about it.

We will start by creating a new file in our `styles` folder and naming it `Styledcomponents.js`. The file acts like an external style sheet, making the code look clean and easy to follow up with if any debugging is required.
Let's copy the recently created styled components from our `Previewer.js` file into our newly created file, adding an export declaration at the beginning.

```javascript
import styled from "styled-components";
import ReactMarkdown from "react-markdown";

export const MarkdownContainer = styled.div`
  position: absolute;
  top: 60px;
  min-height: 90%;
  width: 90vw;
  left: 5%;
  display: grid;
  grid-template-columns: 1fr 1fr;
  grid-gap: 8px;
  border: 1px solid black;
  box-sizing: border-box;
`;

export const Editor = styled.textarea`
  top: 0;
  font-size: 18px;
  background: #1e2126;
  color: white;
  border: 1px solid #c4c4c4;
  box-sizing: border-box;
  max-width: 45vw;
`;

export const Reviewarea = styled(ReactMarkdown)`
  top: 0;
  padding: 20px;
  font-size: 18px;
  background: white;
  border: 1px solid blue;
  box-sizing: border-box;
  max-width: 45vw;
`;

export const H1 = styled.h1`
  position: absolute;
  left: 35%;
`;

```

When we use the components, we must import them in our `Previewer.js` file.

```javascript
import { Editor, H1, MarkdownContainer, Reviewarea } from “../styles/Styledcomponents”
```

The most common font sizes used in blog post range between 16-20 pixels. Let's see how we can change our font sizes and aim at an older audience by changing the font size to 19 pixels and the font family to Monoscope. Add these lines of code in the `Reviewarea` component. This should change the styling of both the heading and paragraph elements.


```css
  h1 {
    color: brown;
    font-family: “Lucida Console”, “Courier New”, monoscope;
  }
  p {
    color: #333333;
    font-size: 21px;
  }
```

This is how our code will look when the styles are added.

```javascript
export const Reviewarea = styled(ReactMarkdown)`
  position: relative;
  top: 0;
  padding: 20px;
  font-size: 18px;
  background: white;
  border: 1px solid blue;
  box-sizing: border-box;
  max-width: 45vw;

  h1 {
    color: brown;
    font-family: "Lucida Console", "Courier New", monoscope;
  }
  p {
    color: #333333;
    font-size: 21px;
  }
`;

```

This is what we produce:

![6](https://blog.openreplay.com/images/previewing-markdown-code-with-react/images/image06.png)

The previewer is now customized, the header has a custom color, and the paragraph font size larger than the default.

### Positioning and Sizing Images

We may also need to reduce the size of images, either to reduce the size of web pages or to create a custom hero image for a blog header section.
If we try adding an image, we notice it's added with its default size. This makes the previewer break since it's larger than the previewer.

![7](https://blog.openreplay.com/images/previewing-markdown-code-with-react/images/image07.png)

We don't want our editor breaking each time, so let's make our image maximum size 500px by 400px in dimension. This only takes two to three lines. The property for width and height is `width:szpx;` and `height:szpx;`.
In the same `Reviewarea` component, add an `img` tag with these properties and check the output once again.

```javascript
export const Reviewarea = styled(ReactMarkdown)`
  top: 0;
  padding: 20px;
  font-size: 18px;
  background: white;
  border: 1px solid blue;
  box-sizing: border-box;
  max-width: 45vw;

  img {
    width: 500px;
    height: 400px;
  }
`;

```

This is the output:

![8](https://blog.openreplay.com/images/previewing-markdown-code-with-react/images/image08.png)

As we can see, sizing images is very easy. In cases of smaller images, they tend to lie on one side. The image in the previewer below is 300px by 150px.

![9](https://blog.openreplay.com/images/previewing-markdown-code-with-react/images/image09.png)

Let's center the image using the margin CSS property in styled components.

```javascript
export const Reviewarea = styled(ReactMarkdown)`
  top: 0;
  padding: 20px;
  font-size: 18px;
  background: white;
  border: 1px solid blue;
  box-sizing: border-box;
  max-width: 45vw;
  img {
    width: 300px;
    height: 150px;
    display: block;
    margin-left: auto;
    margin-right: auto;
  }
`;
```

When the margin property is set to auto on the right and left sides, the image gets centered like the one below.

![10](https://blog.openreplay.com/images/previewing-markdown-code-with-react/images/image10.png)

### Customizing and Including Support for Tables

Since table syntax in markdown isn't considered standard, react-markdown does not support them by default. For tables to work, we need to use a plugin called `remark-gfm`, the most suitable plugin for table functionality.
Quick, let's install the plugin using the command below.

```bash
npm i remark-gfm
```

We only need to import the `remarkGfm` module from the plugin and use it in our ReactMarkdown component. Below is the syntax used in a plain react-markdown component.

```javascript
return (
  <ReactMarkdown remarkPlugins={[remarkGfm]}>{form.markdown}</ReactMarkdown>
);

```

Since we are using Styled-Components, the code looks different compared with the names given to our styling components.

```javascript
import React from "react";
import { useState } from "react";
import ReactMarkdown from "react-markdown";
import remarkGfm from "remark-gfm";

export default function Editortool() {
  const [form, setForm] = useState({ markdown: "" });
  const handleChange = (e) => {
    setForm({
      ...form,
      [e.target.name]: e.target.value,
    });
  };

  return (
    <div>
      <MarkdownContainer>
        <Editor
          id="textarea_id"
          name= "markdown"
          type= "text"
          onChange={handleChange}
        />
        <Reviewarea remarkPlugins={[remarkGfm]}>{form.markdown}</Reviewarea>
      </MarkdownContainer>
    </div>
  );
}
```

Our previewer now supports markdown table syntax; we can try it out.

```
| Column 1 | Column 2 | Column 3 |
| -------- | -------- | -------- |
| Text | Text | Text |
```

We can now view the output in our previewer.

![11](https://blog.openreplay.com/images/previewing-markdown-code-with-react/images/image11.png)

The table is generated successfully, but there is a minor problem: no borders. We can add them using the CSS border property. Let's add a `table` property in our `Reviewarea` styled component.

```javascript
export const Reviewarea = styled(ReactMarkdown)`
  top: 0;
  padding: 20px;
  font-size: 18px;
  background: white;
  border: 1px solid blue;
  box-sizing: border-box;
  max-width: 45vw;
  table {
    border-collapse: collapse;
    th,
    td {
      border: 0.5px solid #dfe2e5;
    }
  }
`;

```

In the code above, notice that we have added a border-collapse property? This is to remove the extra space in the cells. Below is the outstanding-looking table.

![12](https://blog.openreplay.com/images/previewing-markdown-code-with-react/images/image12.png)

With Styled Components, you have unlimited capabilities that you can implement on your own; try adding different colors to the table background and text.

### Custom Code block highlighter
In the case of a technical blog, we regard a code block as an essential component. In our previewer, we will need a code block syntax highlighter; the best library for this would be `react-syntax-highlighter`.

```bash
npm i react-syntax-highlighter
```

After a successful installation, navigate to the `styles` folder and create a new `Highlighter.js` file. The required module imports are going to be our first lines of code.

```javascript

import React from "react";
import styled from "styled-components";
import { Prism as SyntaxHighlighter } from "react-syntax-highlighter";
import { dracula } from "react-syntax-highlighter/dist/cjs/styles/prism";

```

We will need the `SyntaxHighlighter` element the most, but we will still need a theme called `dracula`. Create a Styled-Component and call it `CodeContainer`, then assign it the `SyntaxHighlighter` element just like in the code below.

```javascript
import React from "react";
import { Prism as SyntaxHighlighter } from "react-syntax-highlighter";
import styled from "styled-components";
import { dracula } from "react-syntax-highlighter/dist/cjs/styles/prism";

const Codecontainer = styled(SyntaxHighlighter)`
  width: 98%;
  position: relative;
  left: 10px;
`;

const CodeBlock = {
  code({ node, inline, className, children, ...props }) {
    const match = /language-(\w+)/.exec(className || "");
    return !inline && match ? (
      <Codecontainer
        style={dracula}
        language={match[1]}
        PreTag= "div"
        {...props}
      >
        {String(children).replace(/\n$/, "")}
      </Codecontainer>
    ) : (
      <code className={className} {...props}>
        {children}
      </code>
    );
  },
};

export default CodeBlock;

```

The functional component above overwrites the normal handling of a code element by passing a component into it. Since we have a prop called `code`, we can now change the code element by rendering custom code elements.

We also use a ternary operator in the return statement. If the typed string matches with a coding language, then the `Codecontainer` will be returned, and a normal code block will be returned if false.

This is how our final code will look after importing the `CodeBlock` container to use it in our `Reviewarea` component.

```javascript
// Previewer.js
import React from "react";
import { useState, useEffect } from "react";
import ReactMarkdown from "react-markdown";
import remarkGfm from "remark-gfm";
import CodeBlock from "../styles/Highlighter";

export default function Editortool() {
  const [form, setForm] = useState({ markdown: "" });
  const handleChange = (e) => {
    setForm({
      ...form,
      [e.target.name]: e.target.value,
    });
  };
  return (
    <div>
      <MarkdownContainer>
        <Editor
          id="textarea_id"
          name= "markdown"
          type= "text"
          onChange={handleChange}
        />
        <Reviewarea components={CodeBlock} remarkPlugins={[remarkGfm]}>
          {form.markdown}
        </Reviewarea>
      </MarkdownContainer>
    </div>
  );
}
```


Let's test our syntax highlighter and try using backticks or tildes syntax.

![13](https://blog.openreplay.com/images/previewing-markdown-code-with-react/images/image13.png)

Congrats on getting this far. We now have our customized Next.js Markdown Previewer.

## Conclusion
When we contemplate the infinite possibilities of customizing a react markdown previewer, what we have covered is only a foundation for more ideas. You may add support for flowcharts, including a display for math expressions, or play with distinct color patterns. It's all about your imagination.

Let's meet in our following tutorial. I hope you had fun.

## References
1. [React Markdown](https://github.com/remarkjs/react-markdown)
2. [Create a Next.js App](https://nextjs.org/learn/basics/create-nextjs-app)
3. [Styled Components](https://styled-components.com/)


> A TIP FROM THE EDITOR: For more on Markdown and React, don't miss [How To Safely Render Markdown From A React Component](https://blog.openreplay.com/how-to-safely-render-markdown-from-a-react-component/).




[![newsletter](https://blog.openreplay.com/newsletter_Z1P0UIP.png)](https://newsletter.openreplay.com/)