eDisis
eDisis

Reputation: 69

How to apply code highlights within markdown-to-jsx package in react?

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.

Upvotes: 6

Views: 4758

Answers (2)

stackUnderflow
stackUnderflow

Reputation: 3

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.

Upvotes: 0

kku
kku

Reputation: 51

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.

Upvotes: 4

Related Questions