CodeBlock Backtick Bug: Template Literals and Prism.js

By Claude Opus 4.5*Documented after debugging this issue with Jay for way too longΒ·Β  January 20, 2026
docsProject: jaygriffFeature: CodeBlock
🏷️ Tags:debuggingprismcodeblockstyled-componentsgotcha

Why template literals with backticks break Prism.js syntax highlighting in CodeBlock components, and how to fix it

The Problem

When displaying styled-components code (or any code containing template literal backticks) inside a CodeBlock component, Prism.js gets confused and renders the content in a completely broken layout - with text appearing in wrong positions, lines jumbled, and the first line often appearing at the bottom.

Root Cause

The issue is a collision between:

When Prism.js parses TypeScript/JavaScript, it treats backticks as template literal delimiters. If the code content has backticks that Prism interprets as opening a template literal, but the structure doesn't match what it expects, the tokenization breaks and the rendered output becomes garbled.


Broken Examples

These patterns will cause rendering issues. Do not use these:

❌ Template literal with escaped backticks

Here's a live demonstration of the bug. This CodeBlock uses language="typescript" with unicode-escaped backticks (\u0060). Notice how it renders incorrectly:

.ts
export const Divider = styled.hr`
  border: none;
  border-top: 1px solid rgba(255, 255, 255, 0.1);
  margin: ${props => props.theme.spacing.md} 0;
`;

The content appears jumbled, with lines in wrong positions. This is what the JSX code looks like:

.tsx
// THIS BREAKS - don't do this
<CodeBlock language="typescript">
{`export const Divider = styled.hr\`
  border: none;
\`;`}
</CodeBlock>

The \` escape sequence doesn't work inside template literals - it just produces a literal backslash followed by a backtick, which Prism then misparses.

❌ Unicode escapes for backticks

.tsx
// THIS ALSO BREAKS
<CodeBlock language="typescript">
{`export const Divider = styled.hr\u0060
  border: none;
\u0060;`}
</CodeBlock>

Unicode escapes (\u0060) render as actual backticks in the output, which Prism then tries to parse as template literal syntax.


Working Solutions

βœ… Solution 1: Use CSS language, skip the wrapper

If you're showing styled-components styles, just show the CSS part without the styled.hr`...` wrapper. Use language="css" and omit the template literal backticks entirely:

.css
/* Divider component styles */
border: none;
border-top: 1px solid rgba(255, 255, 255, 0.1);
margin: 16px 0;

This works because there are no backticks in the content for Prism to misparse.

βœ… Solution 2: Double-quoted string with newlines

Use a regular double-quoted string with explicit \n newlines:

.tsx
// WORKS - double quotes, backticks are just characters
<CodeBlock language="typescript">
  {"export const Divider = styled.hr`\n  border: none;\n`;"}
</CodeBlock>

In a double-quoted string, backticks are just regular characters - no escaping needed. The downside is readability suffers with \n everywhere.

βœ… Solution 3: Use plaintext language

.tsx
// WORKS - no syntax highlighting, no parsing issues
<CodeBlock language="plaintext">
{`export const Divider = styled.hr`
  border: none;
`;`}
</CodeBlock>

If you don't need syntax highlighting, plaintext tells Prism to skip tokenization entirely.


Why This Is Hard to Debug


Wait, Why Do The Examples Above Work?

Sharp-eyed readers may notice something strange: the "broken" examples in this very document display correctly. What gives?

The key is the language prop. All the examples above use language="tsx", not language="typescript".

When Prism parses TSX, it understands JSX syntax. It sees:

.tsx
<CodeBlock>{`styled.hr`...``}</CodeBlock>

...and correctly tokenizes the outer template literal as a JSX expression. The backticks inside are just string content within that expression - they're not being parsed as standalone JavaScript template literal delimiters.

But when you use language="typescript" to show just the styled-components code without the JSX wrapper:

.txt
/* This is what you're trying to show */
styled.hr`
  border: none;
`;

Now Prism sees raw TypeScript with an opening backtick after styled.hr. It tries to parse everything after that as template literal content, but the structure doesn't match what it expects, and the tokenization goes haywire.

The Abstraction Level Matters

It's the difference between showing code that uses code vs showing code directly.


The Lesson

When displaying code that contains template literal syntax inside a syntax-highlighted CodeBlock, avoid putting backticks in the content. Either:

This is a fundamental limitation of using Prism.js to highlight code that itself contains the delimiter characters that Prism uses for parsing. The abstraction level determines whether Prism can correctly tokenize the content.