6

Is there a way to highlight a code line inside a react-syntax-highlighter component? You can see the highlight I mean in the image below on line 43.

enter image description here

Artur Carvalho
  • 6,901
  • 10
  • 76
  • 105

3 Answers3

3

You can hack on the generated spans. Firstly, without talking about React, let us use plain jQuery to solve the problem:

Open the demo, then run

$(".language-javascript>span:nth-child(3)").css('background-color', 'blue')

and you will see the 3rd line being blue!

enter image description here

Secondly, let us make it work in React. You can use some libraries to inject css codes like .language-javascript>span:nth-child(3) {background-color: blue;} (where 3 is the line number you want to highlight) into React component tree.

ch271828n
  • 15,854
  • 5
  • 53
  • 88
  • 1
    Yeah, I was expecting something simpler but the solution seems to be sort of that. My issue is that I have the highlight inside a markdown file and I want it to server render it. This would probably work well if I convert my .md files to mdx and create a component specifically for this. In any case, this looks like the way to go. Thanks for the reply! – Artur Carvalho Oct 20 '20 at 14:28
3

It is possible using lineProps:

<SyntaxHighlighter 
  language="json"
  style={docco}
  wrapLines={true}
  showLineNumbers={true}
  lineProps={(lineNumber) => {
    const style: any = { display: "block", width: "fit-content" };
    if (this.props.data?.highlight == lineNumber) {
      style.backgroundColor = "#FFDB81";
    }
    return { style };
  }}
  className={"syntax-highlighter"}>

  {code}

</SyntaxHighlighter>

See also: Is it possible to highlight specific characters in a line using react-syntax-highlighter?

kenomo
  • 59
  • 6
2

Extending the answer of kenemo in case you want to utilize this using typescript:

By default the Typescript definitions of @types/react-syntax-highlighter define that either a React.HTMLProps<HTMLElement> need to be passed to lineProps or a lineTagsPropsFunction which is furthermore defined as

type lineTagPropsFunction = (lineNumber: number) => React.HTMLProps<HTMLElement>;

Interface HTMLProps extends from AllHTMLAttributes which furthermore extends from HTMLAttributes that actually defines style as

interface HTMLAttributes<T> extends AriaAttributes, DOMAttributes<T> {
    ...
    style?: CSSProperties | undefined;
    ...
}

and so we now know the type which we should use inside the function to assign properties for. With that knowledge we can add type information to the code presented by kenemo like this:

import { Light as SyntaxHighlighter } from 'react-syntax-highlighter'
import ts from 'react-syntax-highlighter/dist/esm/languages/hljs/typescript';
import { docco as style } from 'react-syntax-highlighter/dist/esm/styles/hljs';

SyntaxHighlighter.registerLanguage('typescript', ts);

...

    <SyntaxHighlighter language="typescript" style={style}
        showLineNumbers={true}
        wrapLines={true}
        lineProps={(lineNumber: number): React.HTMLProps<HTMLElement> => {
            const style: React.CSSProperties = { display: "block", width: "fit-content" };
            if (this.props.data?.highlight === lineNumber) {
                style.backgroundColor = "#FFDB81";
            }
            return { style };
        }}
    >
        {code}
    </SyntaxHighlighter>

Of course, the return type could be inferred and such, though I like it to be as expressive as possible for the future me always wondering what functions actually return.

Note that line highlighting only works if both, showLineNumbers and wrapLines, are enabled. If either of these is disabled, highlighting one or multiple lines will not work.

As I'm also a friend of refactoring out such property assignments into own functions I finally ended up with this code:

const highlightLine = (lineNumber: number, markLines: number[], color: string = "#FFDB81"):
    React.HTMLProps<HTMLElement> => {

    // only works when showLineNumbers and wrapLines are both enabled
    const style: React.CSSProperties = { display: "block", width: "fit-content" };
    if (markLines.includes(lineNumber)) {
        style.backgroundColor = color;
    }
    return { style };
}

which allows to provide an array of line numbers to highlight as well as an option to change the color of the line being highlighted. This function is now used within a lambda expression of the following form:

    ...
    lineProps((line: number) => highlightLine(line, this.props.highlightLines, this.props.highligtColor))
    ...
Roman Vottner
  • 12,213
  • 5
  • 46
  • 63