From 8e2a9ce79294af9786b488b5ba88d18e67ce2191 Mon Sep 17 00:00:00 2001 From: paulj Date: Tue, 11 Nov 2025 11:13:37 -0500 Subject: [PATCH] fix: Resolve hydration mismatch for keyword interpolation in code blocks --- src/components/codeBlock/index.tsx | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/src/components/codeBlock/index.tsx b/src/components/codeBlock/index.tsx index b7cc64c2927e4a..11ce2217f080ab 100644 --- a/src/components/codeBlock/index.tsx +++ b/src/components/codeBlock/index.tsx @@ -57,10 +57,13 @@ export function CodeBlock({filename, language, children}: CodeBlockProps) { // Show the copy button after js has loaded // otherwise the copy button will not work const [showCopyButton, setShowCopyButton] = useState(false); + // Track if component is mounted to prevent hydration mismatch with keyword interpolation + const [isMounted, setIsMounted] = useState(false); const {emit} = usePlausibleEvent(); useEffect(() => { setShowCopyButton(true); + setIsMounted(true); // prevent .no-copy elements from being copied during selection Right click copy or / Cmd+C const noCopyElements = codeRef.current?.querySelectorAll('.no-copy'); const handleSelectionChange = () => { @@ -102,6 +105,15 @@ export function CodeBlock({filename, language, children}: CodeBlockProps) { } } + // Process children to add highlighting + const highlightedChildren = makeHighlightBlocks(children, language); + + // Only apply keyword interpolation after component mounts to prevent hydration mismatch + // Server and client both render raw text initially, then client upgrades after mount + const processedChildren = isMounted + ? makeKeywordsClickable(highlightedChildren) + : highlightedChildren; + return (
@@ -119,9 +131,7 @@ export function CodeBlock({filename, language, children}: CodeBlockProps) { > Copied
-
- {makeKeywordsClickable(makeHighlightBlocks(children, language))} -
+
{processedChildren}
); }