How to add code highlighting to Gatsby and MDX
I've recently dropped markdownRemark and started using MDX to write my posts. MDX is great since it allows you to import components and even write code that will be converted as expected. You can also use graphql queries on your posts which is very useful.
I have been using the gatsby prism plugin to get my code highlighting work, but after moving to MDX this plugin stopped working, so I went ahead in search for a way to get the code highlighting working again. This article did that.
Dependencies
To get the code highlighting work with MDX, we need to install the pristm react renderer package.
shell1npm install prism-react-renderer
The Code Block Component
Following the tutorial, I wrote a code block component. I've done some changes to my code block component since I wanted to use my code highlighting theme, I've also added line numbers to each line.
javascript1import React from "react"2import Highlight, { defaultProps } from "prism-react-renderer"34export default (props) => {56 const className = props.children.props.className || 'language-text' //needed for styling``78 const matches = className.match(/language-(?<lang>.*)/)910 return (11 <div className="gatsby-highlight">12 <Highlight {...defaultProps} 13 code={props.children.props.children.trim()} 14 language={matches && matches.groups 15 && matches.groups.lang 16 ? matches.groups.lang : ''}17 theme=''18 >19 {({ className, tokens, getLineProps, getTokenProps}) => (20 <pre className={className}>21 <code className={className}>22 {tokens.map((line, i) => (23 <div className="code-block" key={i} {...getLineProps({line, key:i})}>24 <span className="line-number">{i + 1}</span>25 {line.map((token, key) => (26 <span key={key} {...getTokenProps({token, key})} />27 ))}28 </div>29 ))}30 </code>31 </pre>32 )}33 </Highlight>34 </div>35 )36}
Notice that I am not passing a theme since I am using my own code highlight theme - you could download one theme from the official PrismJS repository and use it instead.
Or if you want, you can just copy my theme and use it.
Expanding the code block
I've seen Chris Biscardi digital garden and really liked the "header" of his code blocks, so I thought that it could be a nice addition to my code block.
To allow users to copy the code inside a code block, I had to create a function for the copy button and handle the event when someone clicks on it.
javascript1import React, { useState } from "react"2import Highlight, { defaultProps } from "prism-react-renderer"345export default (props) => {6 const className = props.children.props.className || 'language-text'78 const language = className.replace("language-", "")9 const matches = className.match(/language-(?<lang>.*)/)1011 const CopyButton = props => {12 const [text, setText] = useState("Copy")1314 return (15 <button className="code-copy-button" onClick={() => {16 navigator.clipboard.writeText(props.content)17 setText("Copied!")18 setTimeout(() => { setText("Copy")}, 1000)19 }}20 >21 {text}22 </button>23 )24 }2526 return (27 <div className="gatsby-highlight">28 <Highlight {...defaultProps} 29 code={props.children.props.children.trim()} 30 language={matches && matches.groups && matches.groups.lang ? matches.groups.lang : ''}31 theme=''32 >33 {({ className, tokens, getLineProps, getTokenProps}) => (34 <pre className={className}>35 <div className="code-header">36 <span className="language-name">{language}</span>37 <CopyButton content={props.children.props.children} />38 </div>39 <code className={className}>40 {tokens.map((line, i) => (41 <div className="code-block" key={i} {...getLineProps({line, key:i})}>42 <span className="line-number">{i + 1}</span>43 {line.map((token, key) => (44 <span key={key} {...getTokenProps({token, key})} />45 ))}46 </div>47 ))}48 </code>49 </pre>50 )}51 </Highlight>52 </div>53 )54}
Adding Wrapper Component to MDXRenderer
The step 3 of the adding syntax highlighting to Gatsby Mdx with Prism, says that we need to add the code block as a component to the MDXProvider
and also points to the MDXRenderer documentation.
I have to admit that I couldn't understand fully how to do this. Everything that I tried was failing, luckily Will Harris was kind enough to share his repository with me and explained that I should add the component to the gatsby-browser.js
.
javascript1import React from "react"2import { MDXProvider } from "@mdx-js/react"34import CodeBlock from "./src/components/highlight/CodeBlock"56const component = {7 pre: CodeBlock8}910export const wrapRootElement = ({ element }) => {11 return <MDXProvider components={component}>{element}</MDXProvider>12}
By adding this bit of code to the gatsby-browser.js
made the whole code highlighting work.
If you chose a theme, you should have everything working, if you prefer to build your own, then you can keep on reading and use my theme.
Code Highlighting Theme
I am using scss
on this website so the theme that I am going to share with you, will be in scss.
scss1.gatsby-highlight {2 margin-top: 2.5rem;3 margin-bottom: 2.5rem;4}56.code-header {7 display: flex;8 justify-content: flex-end;9 border-bottom: 1px solid #6089bd;10 padding: 1rem;11}1213.language-name {14 padding-right: 1em;15 color: #ff85cb;16}1718.code-copy-button {19 color: #79b3fd;20 background: none;21 border: none;22 cursor: pointer;23}2425code {26 padding: 2px 6px;27 color: #78b2fd;28 border: 1px solid #1b2530;29 background-color: #1b2530;30 border-radius: 5px;31}323334code[class*="language-"],35pre[class*="language-"] {36 padding: 0;37 color: #f8f8f2;38 background: none;39 text-shadow: 0 1px rgba(0, 0, 0, 0.3);40 text-align: left;41 white-space: pre;42 word-spacing: normal;43 word-break: normal;44 word-wrap: normal;45 line-height: 1.5;46 -moz-tab-size: 4;47 -o-tab-size: 4;48 tab-size: 4;49 -webkit-hyphens: none;50 -moz-hyphens: none;51 -ms-hyphens: none;52 hyphens: none;53 font-size: large;54}5556/* Code blocks */57pre[class*="language-"] {58 overflow: auto;59 border-radius: 0.3em;60 border: 1px solid rgb(97, 137, 189);61 scrollbar-width: none;6263 &::-webkit-scrollbar { 64 display: none; /* Chrome Safari */65 }66}6768:not(pre) > code[class*="language-"],69pre[class*="language-"] {70 background: #1A2430;71}7273/* Inline code */74:not(pre) > code[class*="language-"] {75 padding: .1em;76 border-radius: .3em;77 white-space: normal;78}7980.line-number {81 color: #6272a4;82 padding-right: 2rem;83}8485.token-line {86 padding: 0 1em;87}8889.token.comment,90.token.prolog,91.token.doctype,92.token.cdata {93 color: #6272a4;94}9596.token.punctuation {97 color: #f8f8f2;98}99100.namespace {101 opacity: .7;102}103104.token.property,105.token.tag,106.token.constant,107.token.symbol,108.token.deleted {109 color: #ff79c6;110}111112.token.boolean,113.token.number {114 color: #bd93f9;115}116117.token.selector,118.token.attr-name,119.token.string,120.token.char,121.token.builtin,122.token.inserted {123 color: #50fa7b;124}125126.token.operator,127.token.entity,128.token.url,129.language-css .token.string,130.style .token.string,131.token.variable {132 color: #f8f8f2;133}134135.token.atrule,136.token.attr-value,137.token.function,138.token.class-name {139 color: #f1fa8c;140}141142.token.keyword {143 color: #8be9fd;144}145146.token.regex,147.token.important {148 color: #ffb86c;149}150151.token.important,152.token.bold {153 font-weight: bold;154}155156.token.italic {157 font-style: italic;158}159160.token.entity {161 cursor: help;162}
Share your theme!
If you have used my template to get your own code highlighting and have changed it, I would love to see what you have created!
You can add your own theme to my code highlighting theme repository.
References: