Welcome back!
In the previous chapter, Async Highlighter Loading, we solved the problem of "blocking" the application. We learned how to load heavy tools in the background without freezing the screen.
Now, we have a different challenge. We have our syntax highlighter loaded, and it is ready to generate colorful code. But... how do we actually paint those colors onto a black terminal screen?
React was originally built for the web.
<div>, <span>, and <h1 style={{ color: 'red' }}>.<div>. If you send it HTML, it will just print the raw text <div>.A terminal is like an old-school typewriter. It only understands text and very specific commands called ANSI Escape Codes.
They are weird-looking sequences of characters that tell the terminal to change modes.
\x1b[31m -> "Switch pen to Red"\x1b[0m -> "Reset pen to normal"If we want to show colored code, we need a translator to turn our React logic into these raw typewriter keystrokes.
We use a library called Ink. Ink is "React for the Command Line."
It provides special components that look like React but render to the terminal:
<Text>: Replaces <span> or <p>.<Ansi>: A special component that says, "This string already contains magic color codes. Please pass them through to the user."Think of Ink as the translator between the Web Developer (you) and the Typewriter (the terminal).
Let's say our highlighter successfully processes this code:
const x = 1;
The highlighter returns a string that looks like this internally:
"\x1b[35mconst\x1b[39m \x1b[34mx\x1b[39m = \x1b[33m1\x1b[39m;"
If we just printed this as a standard React string, React might try to "escape" the characters for safety, and the user would see the messy codes. We need to tell the renderer: "Render this string exactly as it is, interpreting the colors."
Let's look at how the data flows from our highlighter component to the user's eye.
Let's look at Fallback.tsx again. There are two places where we perform this translation.
Inside the Highlighted component, once we have the highlighted string (let's call it out), we simply wrap it.
// Inside Highlighted component
// 'out' contains the string with ANSI color codes
return <Ansi>{out}</Ansi>;
Explanation: The <Ansi> component acts as a pass-through. It tells Ink, "Don't touch the weird characters in here; they are for the terminal."
Sometimes, we want to show code but make it look grayed out (for example, if it's a file that is ignored). We use the <Text> component for this.
// Inside HighlightedCodeFallback component
if (skipColoring) {
// If we shouldn't color syntax, maybe we still want to dim it?
return (
<Text dimColor={dim}>
<Ansi>{codeWithSpaces}</Ansi>
</Text>
);
}
Explanation:
<Text>.dimColor={true}.\x1b[2m (which means "dim").<Ansi> child ensures any existing formatting inside isn't broken.The final structure often looks like a sandwich:
<Text dimColor={dim}>
<Suspense fallback={plainText}>
{/* This component returns <Ansi>...</Ansi> */}
<Highlighted code={code} language={language} />
</Suspense>
</Text>
This composition is powerful. The outer <Text> handles global styling (like dimming), while the inner <Highlighted> component handles the specific syntax colors. Ink merges these instructions together intelligently.
In this chapter, we learned:
<Ansi> Component: The specific tool we use to render pre-colored text strings safely.At this point, we have a robust system (Chapter 1), it loads asynchronously (Chapter 2), and it renders beautifully to the terminal (Chapter 3).
However, syntax highlighting is mathematically expensive. If a user scrolls through a list of 100 files, and then scrolls back up, do we really want to re-calculate the colors for files we've already seen?
To solve this, we need to remember our work. Next, we will learn about the Global Highlight Cache.
Generated by Code IQ