How to use the Remark Markdown converters with Next.js projects
Tags: react, javascript, webdev, node
Published: 2020-09-29 22:46:45

Next.js is a React.js framework for server side rendering and static projects. It's easy to use and works extremely well when building a website which stores its content in Markdown. A feature called dynamic routes, which generates routes at build time, provides functionality to read a directory full of Markdown files and generate separate webpages for each file.

[Remark-react](https://github.com/remarkjs/remark-react) is a package that converts Markdown into React and it works with a Next.js application. It differs from [remark-html](https://github.com/remarkjs/remark-html) as it doesn't force the developer to use `dangerouslySetInnerHTML`.

Honestly, if you're wanting to use Markdown with Next.js I'd recommend you try out remark-react. If you do, you should also be aware of the following:

> Remark-react will create all links using the `<a>` component whereas in Next.js you should be using the `<Link>` component from `next/link`.

Using the proper `<Link>` component in a Next.js project is important as it changes the content of the page and the URL without a full redirect, whereas when an anchor (`<a>`) tag is used the page will be reloaded and React will need to do a full render - this can cause your application to perform badly and appear slow.

## Configuring a Next.js application to use remark-react, properly

*The example is taken from [GitHub](https://github.com/james-wallis/wallisconsultancy)*

### Adding Markdown to React conversion
*[printMarkdown.js on GitHub](https://github.com/james-wallis/wallisconsultancy/blob/master/components/markdown/printMarkdown.js)*

You'll need three NPM packages:
```bash
npm i -s unified remark-parse remark-react
```

Once you've added them, import them all into your React app:
```javascript
import unified from 'unified';
import parse from 'remark-parse';
import remark2react from 'remark-react';
```

Next, add the following code to convert Markdown into React components:
```javascript
const content = unified()
    .use(parse)
    .use(remark2react)
    .processSync(markdown).result;
```

From the code blocks above, your Next.js application should now be rendering Markdown into React.

### Adding a `CustomLink` to handle local and external linking
Now we can add a custom link component to the remark-react configuration to instruct it to use the `<Link>` component for local links and the `<a>` component for external links.

*[customLink.js on GitHub](https://github.com/james-wallis/wallisconsultancy/blob/master/components/customLink.js)*

Add a new component called `customLink.js` to your project with the contents:

```javascript
import Link from 'next/link';

export default function CustomLink({ children, href }) {
  // If the link is local it will start with a "/"
  // Otherwise it'll be something like "https://"
  return href.startsWith('/') || href === '' ? (
    <Link href={href}>
      <a>
        {children}
      </a>
    </Link>
  ) : (
    <a
      href={href}
      target="_blank"
      rel="noopener noreferrer"
    >
      {children}
    </a>
  );
}
```

The `CustomLink` component will render a `<Link>` if the `href` passed in has a local target such as `/about` but will render an `<a>` if it has an external target such as `https://dev.to`.

### Updating the Markdown converter to use the `CustomLink` component
Finally, we need to update the Markdown to React code to use the `CustomLink` component when rendering links. This can be done by adding a configuration object to the remark-react `.use` line:

```javascript
  const content = unified()
    .use(parse)
    .use(remark2react, {
      remarkReactComponents: {
        // Use CustomLink instead of <a>
        a: CustomLink,
      },
    })
    .processSync(markdown).result;
```

## Summary

That's all you need to do to use React-remark in your Next.js project. Using the configuration object, you can add a custom component for more than just an anchor HTML tag, incase you want to swap the `img` tag for something more intelligent (image compression?) for example.

Drop me a reaction or comment if this has helped you.
Thanks for reading!