2023-10-03
Categoriestechtypescript
I decided to rewrite this entire website, migrating from WordPress to a static site generated in NextJS, partly because I wanted to have something more customisable and partly because I wanted to get away from WordPress.
One of the things I knew I had to get working was code highlighting, I wanted to be able to write code in my posts and have it look nice and pretty. The source files for these posts are written in Markdown, and I was already using react-markdown to parse the markdown into HTML, so I needed to find a way to get code highlighting working with react-markdown.
Their docs suggest using react-syntax-highlighter and then using Prism.js (apparently it has better JSX highlighting support over highlight.js) as the highlighter, so after installing the packages I then went looking at the docs to see how to integrate it. The example code in their docs, reads pretty horrifically:
1ReactDom.render(
2 <Markdown
3 children={markdown}
4 components={{
5 code(props) {
6 const {children, className, node, ...rest} = props
7 const match = /language-(\w+)/.exec(className || '')
8 return match ? (
9 <SyntaxHighlighter
10 {...rest}
11 children={String(children).replace(/\n$/, '')}
12 style={dark}
13 language={match[1]}
14 PreTag="div"
15 />
16 ) : (
17 <code {...rest} className={className}>
18 {children}
19 </code>
20 )
21 }
22 }}
23 />,
24 document.body
25)
Now, firstly I find the lack of types disturbing.
Secondly, I don't like the way it's written, it's not very readable, and it's not very maintainable, I don't like the obscure ternary operator, I don't like the shadowed variable names, I don't like the prop spreading. It didn't fill me with hope or happiness. But, I pasted the code in, and it worked, so I decided to try and refactor it to something I was happier with.
I have split the code generator out into a separate function, and then had to do the same with the Pre tags, as it would insist on putting a pre tag around the code block, which I didn't want, I also added a max width to the code blocks, as they were stretching the page out too much especially on mobile.
1'use client';
2
3import Markdown from 'react-markdown';
4import { Prism as SyntaxHighlighter } from 'react-syntax-highlighter';
5import { vscDarkPlus } from 'react-syntax-highlighter/dist/esm/styles/prism';
6
7function generateCodeBlock(
8 props: React.DetailedHTMLProps<
9 React.HTMLAttributes<HTMLElement>,
10 HTMLElement
11 >,
12) {
13 const match = /language-(\w+)/.exec(props.className || '');
14 return match ? (
15 <SyntaxHighlighter
16 style={vscDarkPlus}
17 language={match[1]}
18 showLineNumbers
19 customStyle={{
20 maxWidth: 'calc(100vw - 50px)',
21 }}
22 >
23 {String(props.children).replace(/\n$/, '')}
24 </SyntaxHighlighter>
25 ) : (
26 <code className={props.className}>{props.children}</code>
27 );
28}
29
30function noPreWrap(
31 props: React.DetailedHTMLProps<
32 React.HTMLAttributes<HTMLPreElement>,
33 HTMLPreElement
34 >,
35) {
36 // eslint-disable-next-line react/jsx-no-useless-fragment
37 return <>{props.children}</>;
38}
39
40export default function ReactMarkdown({ children }: { children: string }) {
41 return (
42 <div className='container'>
43 <Markdown
44 className='prose prose-invert break-words text-gray-100 prose-p:break-words prose-p:text-justify prose-a:break-all prose-img:h-1/6'
45 components={{
46 code: generateCodeBlock,
47 pre: noPreWrap,
48 }}
49 >
50 {children}
51 </Markdown>
52 </div>
53 );
54}
I'm much happier with this, it's more readable, it's more maintainable, it's more type safe. I'm sure there are still improvements to be made, and I don't like the linting override, but I'm happy with it for now.
If I get the time, I might clean it up a bit and submit a PR to their docs to see if they want to use this instead. I think it's better to have a more readable example in the docs, and it's definitely clearer to me what is going on in this code.
But mainly this post is for me to remember how I did it, and to check that the code highlighting works as I write this. Also, a friendly reminder to myself that when you are struggling to type something Ctrl + Click or Highlight and F12 is your friend.
This has also made me realise that KBD tags don't work1, so I guess I'll have to fix that next.
They work now! ↩