Implementing a Syntax Highlighter in React

––– views

3 min read

Syntax highlighter with Prism

The highlighter you will get is the one that you see belo, feel free to change the color scheme to match your style.

From your CMS you will receive the code values which you need to pass on to the component.

TS

<CodeBlock
fileName={fileName} // received from CMS
language={language} // received from CMS
code={code} // received from CMS
/>

Code snippet

The two functions I use useCopyToClipboard and getLanguageFormatted can be found below.

TS

import React from "react";
import Highlight, { Prism } from "prism-react-renderer";
import { FaCopy } from "react-icons/fa";
import vsDark from "prism-react-renderer/themes/vsDark"
import { useCopyToClipboard } from "../lib/hooks/useCopyToClipboard";
export const CodeBlock = ({ code, language, metastring = "", fileName }) => {
const [isCopied, handleCopy] = useCopyToClipboard(1000, code);
const { languageFormatted, color } = getLanguageFormatted(language);
return (
<Highlight Prism={Prism} code={code} language={language} theme={vsDark}>
{({ className, style, tokens, getLineProps, getTokenProps }) => (
<div className={`relative not-prose my-10 `}>
<pre
className={`rounded-xl relative overflow-hidden bg-slate-800 ${className}`}
style={style}
>
<div className="relative flex text-xs leading-6 ">
<div className="flex items-center px-4 pt-1 mt-2 border-t border-b border-t-transparent border-b-slate-400 w-full justify-between">
<p
className={`bg-zinc-800
} h-full pt-2 pb-2 px-4`}
>
<span className={color}>
{JSON.stringify(languageFormatted)
.replace(/['"]+/g, "")
.toUpperCase()}
</span>{" "}
<span className="text-current">{fileName}</span>
</p>
<button
className={`hidden md:inline-block group mb-2 mr-1 ${
isCopied ? "text-secondary" : "text-gray-400"
}`}
onClick={() => handleCopy()}
>
<span className="sr-only">Copy code</span>
<FaCopy />
</button>
</div>
</div>
<div className="relative w-auto p-5 overflow-auto prose text-gray-300 prose-full-width text-sm md:text-md">
<span>
{tokens.map((line, i) => {
const lineProps = getLineProps({ line, key: i });
return (
<div key={i} {...lineProps}>
{line.map((token, key) => (
<span key={key} {...getTokenProps({ token, key })} />
))}
</div>
);
})}
</span>
</div>
{/* Show fade on side of codeblock if content overflows */}
<div className="absolute w-8 top-[45px] right-0 bg-gradient-to-l from-midnight code-fade"></div>
</pre>
</div>
)}
</Highlight>
);
};
export default CodeBlock;

It takes language as an arg and returns a nicely formatted version of it along with the color.

TS getLanguageFormatted.ts

const getLanguageFormatted = (language) => {
let color, languageFormatted;
switch (language) {
case "javascript":
languageFormatted = "JS";
color = "text-yellow-500";
break;
case "typescript":
languageFormatted = "TS";
color = "text-blue-400";
break;
case "css":
languageFormatted = "CSS";
color = "text-blue-400";
break;
case "markup":
languageFormatted = "HTML";
color = "text-orange-600";
break;
default:
languageFormatted = language;
color = "text-secondary";
break;
}
return {
languageFormatted,
color,
};
};

Copies everything from the code snippet to user’s clipboard

TS useCopyToClipboard.ts

import { useCallback, useEffect, useState } from 'react';
import copy from 'copy-to-clipboard';
// By default will copy URL to clipboard if text is not passed to the hook.
export function useCopyToClipboard(resetInterval = 3000, text = null) {
const [isCopied, setCopied] = useState(false);
const handleCopy = useCallback(() => {
if (window !== undefined) {
copy(text ?? window.location.href);
setCopied(true);
}
}, []);
useEffect(() => {
let timeout;
if (isCopied && resetInterval) {
timeout = setTimeout(() => setCopied(false), resetInterval);
}
return () => {
clearTimeout(timeout);
};
}, [isCopied, resetInterval]);
return [isCopied, handleCopy] as const;
}

Conclusion and links

That’s all the pieces you need to display code nicely on your website, if you want to see an implentation of this with Notion, check out the code-repo of my website.

To comment please authenticate