7

I'm using the markdown-to-jsx package in order to render documentation content inside my react project. This package provides a Markdown component, which accepts an options prop to override HTML elements's default style, and more.

const markdownOptions = {
    wrapper: DocsContentWrapper, 
    forceWrapper: true,
    overrides: {
        h1: LeadTitle,
        h2: SecondaryTitle,
        h3: ThirdTitle,
        p: Paragraph, 
        pre: CodeWrapper,
        ol: ListWrapper,
        li: ListItem,
    },
};

<Markdown 
    options={MarkdownOptions}
>
    {MockDoc}
</Markdown>

Now, the Markdown component accept a markdown, so I pass it a string which is formatted accoring to markdown rules.

It contains some code blocks, like the following, which I want to add colors to:

enter image description here

I have created a component using 'react-syntax-highlighter' package, and it looks like the following:

import React from 'react';

import { Prism as SyntaxHighlighter } from "react-syntax-highlighter"
import { tomorrow } from "react-syntax-highlighter/dist/esm/styles/prism"

const SyntaxHighligher = ({ language, markdown }) => {
    
    return (
        <SyntaxHighlighter 
            language={language} 
            style={tomorrow}
        >
            {markdown}
        </SyntaxHighlighter>
    );
};

export default SyntaxHighligher;

And here comes the question - how can I integrate the two? I was thinking that it would have made sense if the options object would accept such configuration, but looking at 'markdown-to-jsx' docs via their GitHub page, shows no option.

I have seen a package called 'react-markdown' that is able to accept my SyntaxHighligher component and to the task, but I want to apply the same functionality with 'markdown-to-jsx' package.

3 Answers 3

4

markdown-to-jsx generates code blocks as <pre><code>...</code></pre>, but we can't simply override code tag since inline code uses it as well. The README from markdown-to-jsx suggests that we can override pre > code somehow:

Some element mappings are a bit different from other libraries, in particular:

span: Used for inline text.
code: Used for inline code.
pre > code: Code blocks are a code element with a pre as its direct ancestor.

But based on my experiments and reading of the source code, I think the only way is to override pre and check for code in its children. For example:

import {Prism as SyntaxHighlighter} from 'react-syntax-highlighter';
import {materialDark as CodeStyle} from 'react-syntax-highlighter/dist/esm/styles/prism';

const CodeBlock = ({className, children}) => {
  let lang = 'text'; // default monospaced text
  if (className && className.startsWith('lang-')) {
    lang = className.replace('lang-', '');
  }
  return (
    <SyntaxHighlighter language={lang} style={CodeStyle}>
      {children}
    </SyntaxHighlighter>
  );
}

// markdown-to-jsx uses <pre><code/></pre> for code blocks.
const PreBlock = ({children, ...rest}) => {
  if ('type' in children && children ['type'] === 'code') {
    return CodeBlock(children['props']);
  }
  return <pre {...rest}>{children}</pre>;
};

const YourComponent = () => {
  return (
    <Markdown
      options={{
        overrides: {
          pre: PreBlock,
        },
      }}
    >
      {yourMarkdown}
    </Markdown>
  );
};

Unfortunately, I don't have a good solution if you want to override BOTH inline code and code blocks. When overriding both pre and code tags, the code tag generated by SyntaxHighlighter also gets overridden, so the inline code and code blocks are rendered identically.

Sign up to request clarification or add additional context in comments.

Comments

0

I had the same problem with the OP. Here is my approach for it

import { PrismLight as SyntaxHighlighter } from "react-syntax-highlighter";
import { materialDark } from "react-syntax-highlighter/dist/esm/styles/prism";
import Markdown from "markdown-to-jsx";

function Code({ className, children }) {
  const language = className.replace("lang-", "");
  return (
    <div className="codeBlock">
      <SyntaxHighlighter language={language.toLowerCase()} style={materialDark}>
        {children}
      </SyntaxHighlighter>
    </div>
  );
}

function JsxFromMarkdown() {
  return (
    <div className="markdownContainer">
      <Markdown
        options={{
          overrides: {
            code: {
              component: Code,
            },
          },
        }}
      >
        {markdownContent}
      </Markdown>
    </div>
  );
}

I use language={language.toLowerCase()} inside Code component as my code block in markdown file is written as ```PYTHON a = 1 + 2 ```. If your code is written as ```python a = 1 + 2 ``` inside markdown file, you should use language={language} inside Code component.

Comments

0

Well, the official GitHub documentation says they used third party for language detection and code hilights.

https://docs.github.com/en/get-started/writing-on-github/working-with-advanced-formatting/creating-and-highlighting-code-blocks

"We use Linguist to perform language detection and to select third-party grammars for syntax highlighting. You can find out which keywords are valid in the languages YAML file."

You may try to do the same thing.

Actually, I wonder how this page, StackOverflow may do it, since the code you paste here, is well highligted.

You may think in how to install the third party libraries and use it in your own project. My recommendation would be to:

The most common and effective way to render Markdown with syntax highlighting (including for JSX) in a React application is to combine the react-markdown library with react-syntax-highlighter.

You're correct that Markdown itself doesn't highlight code; it just identifies code blocks. You may need to use a separate library to parse and style that code. react-syntax-highlighter is a popular choice because it bundles highlighting libraries like Prism and Highlight.js for easy use in React.

An useful example might be:

Step 1: Install Dependencies

First, you need to install the necessary packages:

npm install react-markdown react-syntax-highlighter
# Optional, but recommended for GitHub-style markdown (tables, etc.)
npm install remark-gfm

Step 2: Create the Component

Now, create a component that renders the Markdown. The key is to use the components prop in react-markdown to override the default renderer for code blocks.

import React from 'react';
import ReactMarkdown from 'react-markdown';
import { Prism as SyntaxHighlighter } from 'react-syntax-highlighter';
// You can choose any theme you like
import { vscDarkPlus } from 'react-syntax-highlighter/dist/esm/styles/prism';
import remarkGfm from 'remark-gfm';

// The markdown string you want to render
const markdownString = `
Here's some regular text.

And here is a JSX code block:

\`\`\`jsx
import React from 'react';

function MyComponent() {
  return (
    <div className="container">
      <h1>Hello, React!</h1>
    </div>
  );
}
\`\`\`

We also support inline \`code\` elements.

And other languages like JavaScript:

\`\`\`javascript
console.log('Hello, world!');
\`\`\`
`;

function MarkdownRenderer() {
  return (
    <ReactMarkdown
      remarkPlugins={[remarkGfm]} // Adds GFM support
      children={markdownString}
      components={{
        code(props) {
          const { children, className, node, ...rest } = props;
          const match = /language-(\w+)/.exec(className || '');
          return match ? (
            <SyntaxHighlighter
              {...rest}
              PreTag="div"
              children={String(children).replace(/\n$/, '')}
              language={match[1]} // e.g., 'jsx', 'javascript'
              style={vscDarkPlus}  // The theme to use
            />
          ) : (
            <code {...rest} className={className}>
              {children}
            </code>
          );
        },
      }}
    />
  );
}

export default MarkdownRenderer;

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.