Hello Folks. And welcome to another blog of everything React! This blog will sadly be more exploratory then actually fact checked (for now).
I need to customize my ReactMarkdown components to render-in customized code in order to add more functionality to blogposts that I render into my Front-End. Blog Post such the one you are reading now! (I use ReactMarkdown to Render my Strapi blogs)
I present some of my findings to how you can custom render your React Markdown code snippets.
Lets suppose that you want to manipulate your ReactMarkdown Strings with customized code. I know... a very specific use case... but that's why this blog is experimental!
In a React application using the react-markdown
library, if you want to search for a specific string of text within the rendered content of a <ReactMarkdown>
component, you can use JavaScript to traverse the DOM and look for the desired text. Here's a basic example of how you might achieve this:
First, make sure you have the react-markdown
library installed. You can install it using:
npm install react-markdown
Then, you can use the following example code to search for a string of text within a <ReactMarkdown>
component:
import React, { useState, useEffect } from 'react';
import ReactMarkdown from 'react-markdown';
const MarkdownComponent = ({ markdownContent, searchString }) => {
const [foundText, setFoundText] = useState(false);
useEffect(() => {
const searchInMarkdown = () => {
const markdownElement = document.getElementById('markdown-content');
if (markdownElement) {
const textInMarkdown = markdownElement.innerText || markdownElement.textContent;
if (textInMarkdown.includes(searchString)) {
setFoundText(true);
} else {
setFoundText(false);
}
}
};
searchInMarkdown();
}, [markdownContent, searchString]);
return (
<div>
<ReactMarkdown id="markdown-content" source={markdownContent} />
{foundText && <p>Text found!</p>}
</div>
);
};
export default MarkdownComponent;
In this example:
ReactMarkdown
component renders the provided markdownContent
inside a div
with the id
"markdown-content".useEffect
hook runs whenever the markdownContent
or searchString
changes.useEffect
, the function searchInMarkdown
gets the text content of the markdownElement
and checks if it includes the specified searchString
.searchString
is found, it sets the foundText
state to true
, and you can then render a message or take any other action.Remember to replace the markdownContent
and searchString
props with your actual markdown content and the string you want to search for. Additionally, you may need to adjust this code based on your specific requirements and the structure of your React application.
You have the option to write your Markdown pages with HTML code. Keep in mind - The react-markdown
library in React is designed to render Markdown content, not raw HTML. By default, it does not render HTML tags directly for security reasons, as rendering raw HTML can expose your application to potential cross-site scripting (XSS) vulnerabilities.
However, if you trust the source of your Markdown content and want to render HTML tags within the Markdown, you can use the escapeHtml
prop in react-markdown
and set it to false
. This tells the library not to escape HTML tags and render them as-is.
Here's an example:
import React from 'react';
import ReactMarkdown from 'react-markdown';
const MarkdownComponent = ({ markdownContent }) => {
return (
<div>
<ReactMarkdown source={markdownContent} escapeHtml={false} />
</div>
);
};
export default MarkdownComponent;
Keep in mind that enabling escapeHtml={false}
should be done with caution, especially if the Markdown content comes from user input, as it might expose your application to security risks. Ensure that you trust the source of the content or sanitize it appropriately to avoid potential security issues.
If you need to render raw HTML safely, you might want to consider using a dedicated HTML rendering library or a sanitizer library in addition to or instead of react-markdown
.
This was an Idea I got reading MarkdownMonster
They suggest adding Generic attributes as a Markdown extension to support code such as this:
# CSS Classes{.WARNING}
Using `.css-class` syntax lets you apply custom CSS classes.
## Id Values{#Updates}
Using `#id` lets you apply custom ID values.
## Custom Styling{style="color:darkgoldenrod"}
For everything else you can use attributes
To my knowledge the react-markdown
library doesn't have native support for parsing and applying custom attributes. By default, it focuses on standard Markdown syntax.
However, if you need to handle custom attributes in your Markdown content, you might want to consider using a custom renderer. You can create a custom renderer that extends the default renderer and allows you to handle additional properties or attributes.
Here's a simplified example of how you might approach this:
import React from 'react';
import ReactMarkdown from 'react-markdown';
const MyCustomMarkdownRenderer = ({ node, ...props }) => {
if (node.type === 'heading') {
// Check for CSS class attribute
const className = node.data && node.data.hProperties && node.data.hProperties.className;
const id = node.data && node.data.hProperties && node.data.hProperties.id;
const style = node.data && node.data.hProperties && node.data.hProperties.style;
if (className || id || style) {
return React.createElement(
`h${node.depth}`,
{
className: className || '',
id: id || '',
style: style || {},
},
props.children
);
}
}
// For other elements, you can extend this logic
// Fallback to the default renderer
return ReactMarkdown.renderers.heading(node, props);
};
const MarkdownComponent = ({ markdownContent }) => {
return (
<div>
<ReactMarkdown
source={markdownContent}
renderers={{ heading: MyCustomMarkdownRenderer }}
/>
</div>
);
};
export default MarkdownComponent;
This example is focused on handling custom attributes for headings, but you can extend this logic to other Markdown elements as needed. Keep in mind that this is a basic example, and you may need to adjust it based on your specific requirements.
Please note that using custom attributes in Markdown might make your content less portable across different Markdown parsers or platforms. If portability is a concern, you may want to consider using standard Markdown syntax and applying styling or attributes programmatically in your React application based on the parsed Markdown content.
I was reading a StackOverflowReactMarkdown blog that had a code example (non functioning) of a user importing a plugin attempting to correct his particular issue:
const ReadMePreviewer = ({ readMeCode }) => {
return (
<ReactMarkdown
plugins={[[gfm, { singleTilde: false }]]}
className={style.reactMarkDown}
// className={style.reactMarkDownRemovingExtraGlobal}
renderers={renderers}
source={readMeCode}
/>
);
};
I had no idea that you can import plugins into ReactMarkdown, or what the end answer was for this use case, but that blogpost is worth further investigation. https://www.bayanbennett.com/posts/markdown-with-custom-components-in-nextjs-devlog-007/ is another blog worth investigating.
I will probably try HTML edits first, as that seems to be the most portable codewise speaking - although drawbackwise are that it could lead to XSS exploits if you happen process any of the Information on the Backend. I will be sure to take messures to restrict those components to the Front-End (Next.js makes SSR rendering and client side rendering easy with the 'use server' and 'use client' tags.
While the other Methods seem to have merit as well, I will be sure to update this blogpost once I run through all the methods!