Include React Components in MDX Content

5 min read

Markdown is great, but we're using MDX so we get the added benefit of being able to include React components in our content. In this installment of the Build a Blog with Next, MDX, and Tailwind series, we'll create a React component to include in our MDX content. We'll also use that same mechanism to replace a standard HTML tag with our own version with our own styling applied with Tailwind.

Building the "Hot Tip" React Component

We're going to build a component so we can call out and highlight tips in our content. We'll start with a HotTip.tsx file in our components directory.

import { FunctionComponent } from 'react'

export const HotTip: FunctionComponent = ({ children }) => {
  return (
      <div>🔥 Hot Tip!</div>

We're adding a bit of markup around children, so we'll use HotTop by wrapping some content with it like <HotTip>important message here</HotTip>. Let's apply some styling so it serves a purpose.

Let's look at the styled version and we'll just quickly touch on the styles that have been applied.

import { FunctionComponent } from 'react'

export const HotTip: FunctionComponent = ({ children }) => {
  return (
    <div className="p-3 bg-orange-50 border-4 border-orange-400 rounded-lg">
      <div className="text-2xl font-extrabold text-orange-500">🔥 Hot Tip!</div>
      <p className="font-medium">{children}</p>

The structure of the component has remained the same. We've added a subtle background to the wrapping div, and put a thick orange border with rounded corners around the div. We'll bumped up the font weights and size for both the "Hot Tip!" title and the paragraph that will wrap the children content.

Expose the Component to MDXRemote

Now that we have our component built, we need to expose it to the MDXRemote component in pages/blog/[slug].tsx.

Let's add an import for our new component

import type { GetStaticPaths, GetStaticProps } from 'next'
import { getAllPostSlugs, getPostBySlug } from '../../utils'
import { MDXRemoteSerializeResult } from 'next-mdx-remote/dist/types'
import { MDXRemote } from 'next-mdx-remote'
+import { HotTip } from '../../components/HotTip'

And then right below our imports, we can create a components object. We'll include HotTip using the shorthand syntax.

const components = {

Now we'll pass this components object along to our MDXRemote instance.

<article className="prose prose-xl prose-sky mx-auto mt-2 py-4">
- <MDXRemote {...mdxSource} />
+ <MDXRemote components={components} {...mdxSource} />

Using HotTip in the Content

Now that we've created our component and made it available, let's use it in some content and see the results. I'm going to test this out in the content/ file. The 4th paragraph is fairly short, so I'm just going to wrap that in the new HotTip component.

It takes dark in order to show light. Nothing wrong with washing your brush. You can do anything here - the only pre-requisite is that it makes you happy. You're meant to have fun in life.

At this point, you should see this "Hot Tip" being called out in your content!

Replacing Standard HTML

Now that we're providing a components object to MDXRemote, we can use that to replace any markup we want. In an upcoming post, we'll see other ways to use this functionality in pretty powerful ways. For now, let's look at a small example just to cover the general idea.

In my content/, I added a small bit of formatting in the first sentence

Maybe there's a **happy little bush** that lives right there.

When that is converted to JSX, it renders

Maybe there's a <strong>happy little bush</strong> that lives right there.

The Tailwind typography plugin does a great job of styling this for us, but just for the purposes of this exercise let's replace it so we can apply our own styling.

Back in pages/blog/[slug].tsx, let's update our components object to include a strong key with a custom component defined for it.

const components = {
+ strong: ({ children }) => (
+   <strong className="text-orange-500 font-bold">{children}</strong>
+ ),

And that's it. When the MDX is rendered, any instance of a strong element will use the version we supplied instead.

As I alluded to earlier, we'll use this capability later to add some optimizations to our blog.

The examples we used here were very focused on superficial styling changes, but we can use more interactive React components in the same way. You could, for example, add a component with a form that accepts input, tracks internal state and all the usual interactivity you expect from React components.

Hot Tip
Check back next week for the next article in the series, Improve Blog Listing with Post Excerpts!