Creating a custom React component to use in the (mdx) blog

Updated on

MDX's power comes from the ability to create custom components that can be used in the markdown files. This allows you to create resusable sections to display images, layouts or even more advanced visualizations.

Adding a component to the Shipixen blog follows these steps in general:

  • create a React component
  • import component in MDXComponents.tsx
  • use component in markdown file

Let's walk through a couple examples.

Example 1 - adding a Protip component

Let's add a component that displays an important tip. We can use these in most blog articles to highlight important information.

Example 1 - Creating the component

Start by creating a new React component under components/blog/Protip.tsx:

components/blog/Protip.tsx
import { cn } from '@/lib/utils';
import { SparklesIcon } from 'lucide-react';

export const Protip = ({
  className,
  title,
  description,
}: {
  className?: string;
  title?: string;
  description: string;
}) => {
  return (
    <div
      className={cn(
        'flex flex-col text-sm p-6 bg-primary-100/30 dark:bg-purple-900/30 rounded-md',
        className,
      )}
    >
      {title ? <p className="m-0 text-lg font-semibold">{title}</p> : null}
      <p className="flex gap-1 items-center my-2">
        <SparklesIcon className="inline-block w-4 h-4 text-primary-500 dark:text-primary-400" />
        {description}
      </p>
    </div>
  );
};

We can use this component by passing a title and description prop. For example:

<Protip
  title="Heads up!"
  description="This is a protip. A little bit of extra attention. It usually contains a useful piece of information."
/>

It'll look something like this:

Heads up!

This is a protip. A little bit of extra attention. It usually contains a useful piece of information.

Example 1 - Adding the component to MDXComponents.tsx

For this component to be available in the blog, we need to register it in components/MDXComponents.tsx:

components/MDXComponents.tsx
import TOCInline from '@shipixen/pliny/ui/TOCInline';
import Pre from '@shipixen/pliny/ui/Pre';
import BlogNewsletterForm from '@shipixen/pliny/ui/BlogNewsletterForm';
import type { MDXComponents } from 'mdx/types';
import Image from './shared/Image';
import CustomLink from './shared/Link';
+ import { Protip } from '@/components/blog/Protip';

export const components: MDXComponents = {
  Image,
  TOCInline,
  a: CustomLink,
  pre: Pre,
  BlogNewsletterForm,
+  Protip,
};

Example 1 - Using the component in a markdown file

Now the easy part. We can use the component in any markdown file without importing it. Any .mdx file will pick it up automatically!

data/post-example.mdx
[...]

Example of markdown file usage.

<Protip
  title="Heads up!"
  description="This is a protip. A little bit of extra attention. It usually contains a useful piece of information."
/>

[...]

That's it! You now know how to create a custom component for the blog.

Example 2 - adding a dougnut chart

In this example we'll create a component that displays a doughnut chart. We'll use react-chartjs-2 to create the chart.

Example 2 - Creating the component

Start by creating a new React component under components/blog/DoughnutChart.tsx:

components/blog/DoughnutChart.tsx
'use client';

import { Doughnut } from 'react-chartjs-2';
import { Chart as ChartJS, ArcElement, Tooltip, Legend } from 'chart.js';

ChartJS.register(ArcElement, Tooltip, Legend);

const DonutChart = ({ data }) => {
  return <Doughnut data={data} />;
};

export default Doughnut;

Since the underlying Doughnut component uses React hooks, we add the 'use client' directive to specify that it is a client side component. Also, there is an existing issue which prevents named components from being used, so we need to export the component as the default export.

Example 2 - Adding the component to MDXComponents.tsx

For this component to be available in the blog, we need to register it in components/MDXComponents.tsx:

components/MDXComponents.tsx
...
+ import DonutChart from './DonutChart'

export const components: MDXComponents = {
  Image,
  TOCInline,
  a: CustomLink,
  pre: Pre,
  BlogNewsletterForm,
+  DonutChart,
}

Example 2 - Using the component in a markdown file

You can now use the component in any markdown file without importing it. Any .mdx file will pick it up automatically!

data/post-example.mdx
[...]

## Example Donut Chart

export const data = {
  labels: ['Red', 'Blue', 'Yellow'],
  datasets: [
    {
      label: '# of Votes',
      data: [12, 19, 3],
      backgroundColor: [
        'rgba(255, 99, 132, 0.2)',
        'rgba(54, 162, 235, 0.2)',
        'rgba(255, 206, 86, 0.2)',
      ],
      borderColor: [
        'rgba(255, 99, 132, 1)',
        'rgba(54, 162, 235, 1)',
        'rgba(255, 206, 86, 1)',
      ],
      borderWidth: 1,
    },
  ],
};

<DonutChart data={data} />

[...]