Is there a simple way to apply a custom theme to react-syntax-highlighter?

8.3k views Asked by At

I've been trying to generate a snippet of code on my website on a rendered text editor using react-syntax-highlighter. I've spent a while using this plugin and love it, however I've come across an issue I can't find an answer to. I want to change some of the colors of the generated code but cannot find any other way to do this other than using their default selection of themes. They allow you to apply styles to the background and the code but not the spans that hold the colors, is there any simple way to create and apply a custom theme, or to simply target the specific classes and change their colors?

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

const CodeDisplay = ({ active }) => {

const templateString = `
    const NewObject = New Shop(
        console.log('shop');
    )
`
    return (
        <>
            <SyntaxHighlighter
                language="jsx"
                style={atomDark}
                wrapLongLines
                customStyle={{
                    backgroundColor: "transparent",
                    opacity: "1",
                    marginTop: "-2rem",
                }}
                codeTagProps={{
                    style: {
                        color: "white",
                    },
                }}>
                {templateString}
            </SyntaxHighlighter>
        </>
    );
};

Output Screenshot

2

There are 2 answers

0
Roman Vottner On

The way that worked best for me in order to style the code similar to the dark+ theme in Visual Studio Code is to specify useInlineStyles={false} to prevent react-syntax-highlighter (RSH) to add inline css style information to each span element produced. RSH however will specify a certain class name to that object that furthermore allows you to define your own CSS styles.

My configuration i.e. looks like this:

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

import './rsh-style.css';

SyntaxHighlighter.registerLanguage('typescript', ts);


const calcMinLineNumberWidth = (code: string): string | null => {
    const lines = [...code].reduce((prev, current) => prev + (current === '\n' ? 1 : 0), 1);
    if (lines > 99) {
        return "50px";
    } else if (lines > 9) {
        return "40px";
    } else {
        return null;
    }
}

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 };
}

const CodeList: FunctionComponent = (props: CodeListProps) => {
    const minWidth = calcMinLineNumberWidth(code);
    return (
    ...
        <div className="container">
            <div className="divider" />
            <SyntaxHighlighter language="typescript" style={style}
                className={"syntax-highlighter"}
                useInlineStyles={false}
                showLineNumbers={true}
                lineNumberStyle={{minWidth: minWidth}}
                wrapLines={true}
                lineProps={(line: number) => highlightLine(line, props.highlightLines, props.highlightColor)}
            >
                {code}
            </SyntaxHighlighter>
        </div>
    ...
    )
}

this will produce an output like visible in the snippet added at the bottom of this post.

The lexer react-syntax-highlighter uses has clearly some issues as in case of multi-line comments a new line start indicated by a new line number is annotated here with a class="hljs-comment" when it clearly shouldn't. Also, there are multiple things that aren't recognized and therefore tagged properly and thus a code highlighting like in your editor of choice might not be doable.

With a CSS file also added as snippet below that basically defines the colors VSCode uses for certain key elements I obtain an output like in the right part of the picture below. For comparison purposes I also included the original styling of dark+ style of VSCode for reference purposes. Some changes like the more greish-purple background are on purpose as it fits the theme of the page better.

As further can be seen, the output RSH returns is not quite able to format the code like VSCode does though most of the colors should be similar to the one VSCode uses in that theme, at least a color picker did return these color values.

enter image description here

Line 19-21 in the image to the right showcases the line highlighting via the respective function used in the lineProps property, just in case if anybody wonders why they have a different color.

.container {
    display: inline-block;
    position: relative;
    left: 0px;
    width: 760px;
}

.container > .syntax-highlighter {
    width:100%;
    height: 100%;
    color: #D4D4D4;
    /*background-color: #030003;*/
    background-color: rgb(40, 44, 52) !important;
    font-family: Droid Sans Mono;
    letter-spacing: 1.5px;
    border-radius: 15px;
    padding-top: 10px;
    padding-bottom: 10px;
    margin-left: -10px;
    font-weight: 500;
}

.container > .syntax-highlighter .language-typescript > span:hover {
    background-color: rgba(224, 224, 173, 0.2) !important;
}

.container > .syntax-highlighter .language-typescript .linenumber {
    color: rgb(126, 120, 135) !important;
    margin-left: 15px;
    margin-right: 15px;
    background-color: transparent;
    border-right: 2px solid rgb(126, 120, 135);
}

.container > .syntax-highlighter .language-typescript .linenumber:hover {
    color: rgb(213, 214, 133) !important;
    margin-left: 15px;
    margin-right: 15px;
}

.container > .syntax-highlighter .language-typescript .hljs-comment {
    color: #48a454;
    font-weight: 450;
}

.container > .syntax-highlighter .language-typescript .hljs-keyword {
    color: #d86fc0;
}

.container > .syntax-highlighter .language-typescript .hljs-function {
    color: #4098d7;
}

.container > .syntax-highlighter .language-typescript .hljs-title {
    color: #d8e2a9;
}

.container >.syntax-highlighter .language-typescript .hljs-tag {
    color: #d4d4d4;
}

.container >.syntax-highlighter .language-typescript .hljs-name {
    color: #4098d7;
}

.container >.syntax-highlighter .language-typescript .hljs-attr {
    color: #86ddff;
}

.container >.syntax-highlighter .language-typescript .hljs-string {
    color: #dc8a77;
}

.container >.syntax-highlighter .language-typescript .hljs-built_in {
    color: #00d5b0;
}
<div class="container"><pre class="hljs syntax-highlighter"><code class="language-typescript" style="white-space: pre;"><span style="display: block;" class=""><span class="comment linenumber react-syntax-highlighter-line-number" style="display: inline-block; padding-right: 1em; text-align: right; user-select: none; min-width: 40px;">1</span><span class="hljs-comment">/**
</span></span><span style="display: block;" class="hljs-comment"><span class="comment linenumber react-syntax-highlighter-line-number" style="display: inline-block; padding-right: 1em; text-align: right; user-select: none; min-width: 40px;">2</span> * Some multi-line comment
</span><span style="display: block;" class=""><span class="comment linenumber react-syntax-highlighter-line-number" style="display: inline-block; padding-right: 1em; text-align: right; user-select: none; min-width: 40px;">3</span><span class="hljs-comment"> */</span><span class="">
</span></span><span style="display: block;" class=""><span class="comment linenumber react-syntax-highlighter-line-number" style="display: inline-block; padding-right: 1em; text-align: right; user-select: none; min-width: 40px;">4</span><span class=""></span><span class="hljs-keyword">import</span><span class=""> React </span><span class="hljs-keyword">from</span><span class=""> </span><span class="hljs-string">'react'</span><span class="">;
</span></span><span style="display: block;" class=""><span class="comment linenumber react-syntax-highlighter-line-number" style="display: inline-block; padding-right: 1em; text-align: right; user-select: none; min-width: 40px;">5</span><span class=""></span><span class="hljs-keyword">import</span><span class=""> logo </span><span class="hljs-keyword">from</span><span class=""> </span><span class="hljs-string">'./logo.svg'</span><span class="">;
</span></span><span style="display: block;" class=""><span class="comment linenumber react-syntax-highlighter-line-number" style="display: inline-block; padding-right: 1em; text-align: right; user-select: none; min-width: 40px;">6</span><span class=""></span><span class="hljs-keyword">import</span><span class=""> </span><span class="hljs-string">'./App.css'</span><span class="">;
</span></span><span style="display: block;" class=""><span class="comment linenumber react-syntax-highlighter-line-number" style="display: inline-block; padding-right: 1em; text-align: right; user-select: none; min-width: 40px;">7</span>
</span><span style="display: block;" class=""><span class="comment linenumber react-syntax-highlighter-line-number" style="display: inline-block; padding-right: 1em; text-align: right; user-select: none; min-width: 40px;">8</span><span class=""></span><span class="hljs-keyword">interface</span><span class=""> SomeInterface {
</span></span><span style="display: block;" class=""><span class="comment linenumber react-syntax-highlighter-line-number" style="display: inline-block; min-width: 40px; padding-right: 1em; text-align: right; user-select: none;">9</span><span class="">    </span><span class="hljs-attr">items</span><span class="">: </span><span class="hljs-built_in">string</span><span class="">[];
</span></span><span style="display: block;" class=""><span class="comment linenumber react-syntax-highlighter-line-number" style="display: inline-block; min-width: 40px; padding-right: 1em; text-align: right; user-select: none;">10</span>}
</span><span style="display: block;" class=""><span class="comment linenumber react-syntax-highlighter-line-number" style="display: inline-block; min-width: 40px; padding-right: 1em; text-align: right; user-select: none;">11</span>
</span><span style="display: block;" class=""><span class="comment linenumber react-syntax-highlighter-line-number" style="display: inline-block; min-width: 40px; padding-right: 1em; text-align: right; user-select: none;">12</span><span class=""></span><span class="hljs-keyword">type</span><span class=""> SomeType = {
</span></span><span style="display: block;" class=""><span class="comment linenumber react-syntax-highlighter-line-number" style="display: inline-block; min-width: 40px; padding-right: 1em; text-align: right; user-select: none;">13</span><span class="">    </span><span class="hljs-attr">items</span><span class="">: </span><span class="hljs-built_in">string</span><span class="">[];
</span></span><span style="display: block;" class=""><span class="comment linenumber react-syntax-highlighter-line-number" style="display: inline-block; min-width: 40px; padding-right: 1em; text-align: right; user-select: none;">14</span>}
</span><span style="display: block;" class=""><span class="comment linenumber react-syntax-highlighter-line-number" style="display: inline-block; min-width: 40px; padding-right: 1em; text-align: right; user-select: none;">15</span>
</span><span style="display: block;" class=""><span class="comment linenumber react-syntax-highlighter-line-number" style="display: inline-block; min-width: 40px; padding-right: 1em; text-align: right; user-select: none;">16</span><span class=""></span><span class="hljs-comment">// line comment</span><span class="">
</span></span><span style="display: block;" class=""><span class="comment linenumber react-syntax-highlighter-line-number" style="display: inline-block; min-width: 40px; padding-right: 1em; text-align: right; user-select: none;">17</span><span class=""></span><span class="hljs-function hljs-keyword">function</span><span class="hljs-function"> </span><span class="hljs-function hljs-title">App</span><span class="hljs-function">(</span><span class="hljs-function">) </span><span class="">{
</span></span><span style="display: block;" class=""><span class="comment linenumber react-syntax-highlighter-line-number" style="display: inline-block; min-width: 40px; padding-right: 1em; text-align: right; user-select: none;">18</span><span class="">  </span><span class="hljs-keyword">return</span><span class=""> (
</span></span><span style="display: block; background-color: rgb(57, 61, 65);" class=""><span class="comment linenumber react-syntax-highlighter-line-number" style="display: inline-block; min-width: 40px; padding-right: 1em; text-align: right; user-select: none;">19</span><span class="">    </span><span class="xml hljs-tag">&lt;</span><span class="xml hljs-tag hljs-name">div</span><span class="xml hljs-tag"> </span><span class="xml hljs-tag hljs-attr">className</span><span class="xml hljs-tag">=</span><span class="xml hljs-tag hljs-string">"App"</span><span class="xml hljs-tag">&gt;</span><span class="xml">
</span></span><span style="display: block; background-color: rgb(57, 61, 65);" class=""><span class="comment linenumber react-syntax-highlighter-line-number" style="display: inline-block; min-width: 40px; padding-right: 1em; text-align: right; user-select: none;">20</span><span class="xml">      </span><span class="xml hljs-tag">&lt;</span><span class="xml hljs-tag hljs-name">header</span><span class="xml hljs-tag"> </span><span class="xml hljs-tag hljs-attr">className</span><span class="xml hljs-tag">=</span><span class="xml hljs-tag hljs-string">"App-header"</span><span class="xml hljs-tag">&gt;</span><span class="xml">
</span></span><span style="display: block; background-color: rgb(57, 61, 65);" class=""><span class="comment linenumber react-syntax-highlighter-line-number" style="display: inline-block; min-width: 40px; padding-right: 1em; text-align: right; user-select: none;">21</span><span class="xml">        </span><span class="xml hljs-tag">&lt;</span><span class="xml hljs-tag hljs-name">img</span><span class="xml hljs-tag"> </span><span class="xml hljs-tag hljs-attr">src</span><span class="xml hljs-tag">=</span><span class="xml hljs-tag hljs-string">{logo}</span><span class="xml hljs-tag"> </span><span class="xml hljs-tag hljs-attr">className</span><span class="xml hljs-tag">=</span><span class="xml hljs-tag hljs-string">"App-logo"</span><span class="xml hljs-tag"> </span><span class="xml hljs-tag hljs-attr">alt</span><span class="xml hljs-tag">=</span><span class="xml hljs-tag hljs-string">"logo"</span><span class="xml hljs-tag"> /&gt;</span><span class="xml">
</span></span><span style="display: block;" class=""><span class="comment linenumber react-syntax-highlighter-line-number" style="display: inline-block; min-width: 40px; padding-right: 1em; text-align: right; user-select: none;">22</span><span class="xml">        </span><span class="xml hljs-tag">&lt;</span><span class="xml hljs-tag hljs-name">p</span><span class="xml hljs-tag">&gt;</span><span class="xml">
</span></span><span style="display: block;" class=""><span class="comment linenumber react-syntax-highlighter-line-number" style="display: inline-block; min-width: 40px; padding-right: 1em; text-align: right; user-select: none;">23</span><span class="xml">          Edit </span><span class="xml hljs-tag">&lt;</span><span class="xml hljs-tag hljs-name">code</span><span class="xml hljs-tag">&gt;</span><span class="xml">src/App.tsx</span><span class="xml hljs-tag">&lt;/</span><span class="xml hljs-tag hljs-name">code</span><span class="xml hljs-tag">&gt;</span><span class="xml"> and save to reload.
</span></span><span style="display: block;" class=""><span class="comment linenumber react-syntax-highlighter-line-number" style="display: inline-block; min-width: 40px; padding-right: 1em; text-align: right; user-select: none;">24</span><span class="xml">        </span><span class="xml hljs-tag">&lt;/</span><span class="xml hljs-tag hljs-name">p</span><span class="xml hljs-tag">&gt;</span><span class="xml">
</span></span><span style="display: block;" class=""><span class="comment linenumber react-syntax-highlighter-line-number" style="display: inline-block; min-width: 40px; padding-right: 1em; text-align: right; user-select: none;">25</span><span class="xml">        </span><span class="xml hljs-tag">&lt;</span><span class="xml hljs-tag hljs-name">a</span><span class="xml hljs-tag">
</span></span><span style="display: block;" class=""><span class="comment linenumber react-syntax-highlighter-line-number" style="display: inline-block; min-width: 40px; padding-right: 1em; text-align: right; user-select: none;">26</span><span class="xml hljs-tag">          </span><span class="xml hljs-tag hljs-attr">className</span><span class="xml hljs-tag">=</span><span class="xml hljs-tag hljs-string">"App-link"</span><span class="xml hljs-tag">
</span></span><span style="display: block;" class=""><span class="comment linenumber react-syntax-highlighter-line-number" style="display: inline-block; min-width: 40px; padding-right: 1em; text-align: right; user-select: none;">27</span><span class="xml hljs-tag">          </span><span class="xml hljs-tag hljs-attr">href</span><span class="xml hljs-tag">=</span><span class="xml hljs-tag hljs-string">"https://reactjs.org"</span><span class="xml hljs-tag">
</span></span><span style="display: block;" class=""><span class="comment linenumber react-syntax-highlighter-line-number" style="display: inline-block; min-width: 40px; padding-right: 1em; text-align: right; user-select: none;">28</span><span class="xml hljs-tag">          </span><span class="xml hljs-tag hljs-attr">target</span><span class="xml hljs-tag">=</span><span class="xml hljs-tag hljs-string">"_blank"</span><span class="xml hljs-tag">
</span></span><span style="display: block;" class=""><span class="comment linenumber react-syntax-highlighter-line-number" style="display: inline-block; min-width: 40px; padding-right: 1em; text-align: right; user-select: none;">29</span><span class="xml hljs-tag">          </span><span class="xml hljs-tag hljs-attr">rel</span><span class="xml hljs-tag">=</span><span class="xml hljs-tag hljs-string">"noopener noreferrer"</span><span class="xml hljs-tag">
</span></span><span style="display: block;" class=""><span class="comment linenumber react-syntax-highlighter-line-number" style="display: inline-block; min-width: 40px; padding-right: 1em; text-align: right; user-select: none;">30</span><span class="xml hljs-tag">        &gt;</span><span class="xml">
</span></span><span style="display: block;" class="xml"><span class="comment linenumber react-syntax-highlighter-line-number" style="display: inline-block; min-width: 40px; padding-right: 1em; text-align: right; user-select: none;">31</span>          Learn React
</span><span style="display: block;" class=""><span class="comment linenumber react-syntax-highlighter-line-number" style="display: inline-block; min-width: 40px; padding-right: 1em; text-align: right; user-select: none;">32</span><span class="xml">        </span><span class="xml hljs-tag">&lt;/</span><span class="xml hljs-tag hljs-name">a</span><span class="xml hljs-tag">&gt;</span><span class="xml">
</span></span><span style="display: block;" class=""><span class="comment linenumber react-syntax-highlighter-line-number" style="display: inline-block; min-width: 40px; padding-right: 1em; text-align: right; user-select: none;">33</span><span class="xml">      </span><span class="xml hljs-tag">&lt;/</span><span class="xml hljs-tag hljs-name">header</span><span class="xml hljs-tag">&gt;</span><span class="xml">
</span></span><span style="display: block;" class=""><span class="comment linenumber react-syntax-highlighter-line-number" style="display: inline-block; min-width: 40px; padding-right: 1em; text-align: right; user-select: none;">34</span><span class="xml">    </span><span class="xml hljs-tag">&lt;/</span><span class="xml hljs-tag hljs-name">div</span><span class="xml hljs-tag">&gt;</span><span class="">
</span></span><span style="display: block;" class=""><span class="comment linenumber react-syntax-highlighter-line-number" style="display: inline-block; min-width: 40px; padding-right: 1em; text-align: right; user-select: none;">35</span>  );
</span><span style="display: block;" class=""><span class="comment linenumber react-syntax-highlighter-line-number" style="display: inline-block; min-width: 40px; padding-right: 1em; text-align: right; user-select: none;">36</span>}
</span><span style="display: block;" class=""><span class="comment linenumber react-syntax-highlighter-line-number" style="display: inline-block; min-width: 40px; padding-right: 1em; text-align: right; user-select: none;">37</span>
</span><span style="display: block;" class=""><span class="comment linenumber react-syntax-highlighter-line-number" style="display: inline-block; min-width: 40px; padding-right: 1em; text-align: right; user-select: none;">38</span><span class=""></span><span class="hljs-keyword">export</span><span class=""> </span><span class="hljs-keyword">default</span><span class=""> App;</span></span></code></pre></div>

0
Willow On

I'm thinking that the best way to do this would just be to copy the theme and edit it.

The theme you're importing in your code is available at https://github.com/react-syntax-highlighter/react-syntax-highlighter/blob/master/src/styles/prism/atom-dark.js

You could download that file, add it to your source control and modify it all you want. Then just import your edited file wherever you use SyntaxHighlighter.

If you make a lot of changes that you think are really cool, consider making a PR to the library so other people can use it as well!