Intro
Markdown is great for static content, but what if you want to inject dynamic React components into your Markdown files? With react-markdown
, you can customize how Markdown elements are rendered—including links, images, code blocks, and even custom tags.
In this post, I’ll show you how to:
- Use standard Markdown link syntax
- Map specific links or syntax to custom React components
- Enhance your Markdown content with live React behavior
Why This Matters
This is especially useful for:
- Documentation sites
- Developer blogs
- Interactive tutorials or component previews
- Any site where you want a mix of Markdown and dynamic behavior
1. How to Use Markdown with react-markdown
.
Here's how you can create a basic React component that renders Markdown using the react-markdown
library:
import ReactMarkdown from 'react-markdown';
const markdown = `
## Hello World
Check out this [Google](https://google.com) page.
`;
function MarkdownRenderer() {
return <ReactMarkdown children={markdown} />;
}
2. How to Extend Markdown Syntax with React Components
To achieve our goal, we’ll follow a few simple steps:
- Establish a “vocabulary” for your custom components.
- Create a react-markdown-based component (as in the previous step).
- Map custom Markdown links (your vocabulary) to React components via the components prop of
react-markdown
.
For this example, I’ll use one custom element called custom-button
, but you can define as many as you like—naming conventions are fully up to you.
You can add react-markdown
to your project by running:
npm install react-markdown
Component implementation:
import React from 'react';
import ReactMarkdown from 'react-markdown';
const components = {
a: ({ href, children }) => {
if (href === 'customButton') {
return <CustomButton>{children}</CustomButton>;
}
return <a href={href}>{children}</a>;
},
};
const CustomButton = ({ children }) => {
return <button className="btn btn-primary">{children}</button>;
}
const markdown = `
## Try This!
Click [here](custom-button) to activate the button!
`;
const MarkdownRenderer = () => (
<ReactMarkdown components={components}>{markdown}</ReactMarkdown>;
)
In Markdown, a link ([here](custom-button)
) consists of two parts: the link text (what’s shown to the user) and the URL/href. Knowing this, we can use the components
mapping feature provided by react-markdown
to replace custom link values with corresponding React components.
If a link in your Markdown doesn’t match any custom mapping, a standard <a>
tag will be rendered instead.
3. Scaling the Approach
You can scale this to support:
- Multiple custom components
- Automatic mapping using a component registry
- Support for parameters in your custom link syntax (e.g.
http://some-name?label=some-label
)
Here's an example registry-based approach:
const componentRegistry = {
CustomButton: (props) => <button className="btn">{props.children}</button>,
AlertBox: (props) => <div className="alert">{props.children}</div>,
};
const CustomLinkRenderer = ({ href, children }) => {
if (href) {
const Component = componentRegistry[href];
return Component ? <Component>{children}</Component> : null;
}
return <a href={href}>{children}</a>;
};
const components = {
a: CustomLinkRenderer,
};
4. Caveats and Gotchas
- Always validate URLs or inputs if user-generated Markdown is allowed.
-
react-markdown
sanitizes input by default. Keep this in my mind if you're trying to allow raw HTML. - You can extend beyond just
<a>
: overridecode
,img
,blockquote
, etc.
Conclusion
By using the components
prop from react-markdown
, you gain full control over how Markdown is rendered—allowing you to seamlessly combine Markdown’s simplicity with the power of React components.
Have questions? Let me know in the comments 👇
Top comments (0)