Can I override "code" in components with syntax highlighting?
can i override the "code" in components while preserving the shiki/syntax highlighting thing?
I'll help you understand how to override the "code" in components while preserving syntax highlighting. Let me first explore the streamdown repository structure to understand how it's organized.
Let me look at the components file more closely to understand how the code component works:
Let me check the actual directory structure:
Let me try a different approach:
Let me search for the components export more broadly:
Let me find the actual files:
Let me try to just list what's in the current directory:
Good! Now let me read the components file:
Let me check what the actual working directory is:
Let me search with a simpler pattern:
Let me use WebSearch to find information about how to override code components in streamdown while preserving syntax highlighting:
Great! I found the information. Let me look at the actual code component implementation to give you a precise answer:
Perfect! Now I have the information I need. Let me provide you with a comprehensive answer about how to override the code component while preserving syntax highlighting in Streamdown.
##Answer
Yes, you can override the code component in Streamdown while preserving syntax highlighting! Here's how:
###For Inline Code (backticks)
You can provide a custom code component that still maintains the default styling:
import { Streamdown } from "streamdown";
export default function Page() {
const markdown = "Use `const x = 1;` in your code";
return (
<Streamdown
components={{
code: ({ children, className, ...props }) => (
<code className={`custom-style ${className || ""}`} {...props}>
{children}
</code>
),
}}
>
{markdown}
</Streamdown>
);
}
###For Code Blocks (syntax-highlighted blocks)
The code block component is more complex because it uses Shiki for syntax highlighting. However, you can still customize it by wrapping or modifying the CodeBlock component's behavior. The key is that code blocks automatically handle the syntax highlighting internally, so you mainly customize the styling layer:
import { Streamdown } from "streamdown";
export default function Page() {
const markdown = `\`\`\`javascript
const x = 1;
\`\`\``;
return (
<Streamdown
components={{
pre: ({ children, ...props }) => (
<pre
className="custom-codeblock rounded-lg shadow-lg"
{...props}
>
{children}
</pre>
),
}}
shikiTheme={["github-light", "github-dark"]}
>
{markdown}
</Streamdown>
);
}
###Best Approaches to Preserve Syntax Highlighting
-
Use CSS styling via
data-streamdownattributes (recommended):[data-streamdown="code-block"] { border-radius: 12px; background: custom-color; } -
Override CSS variables for code blocks:
:root { --muted: 210 40% 96.1%; --border: 214.3 31.8% 91.4%; } -
Customize Shiki theme instead of replacing the component:
<Streamdown shikiTheme={["github-light", "dracula"]}> {markdown} </Streamdown>
###Important Notes
- Inline code styling is simple and safe to override
- Code blocks preserve syntax highlighting automatically - Streamdown's internal CodeBlock component handles the Shiki highlighting regardless of wrapper styling
- The
nodeprop is also passed to your custom component for advanced use cases - Use
classNameto maintain any existing classes that Streamdown adds
So yes, you can customize the code appearance without losing the syntax highlighting! Just avoid replacing the internal logic—customize the styling layer instead.
r u sure? i have this setup:
const streamdownComponents: ComponentProps<typeof Streamdown>["components"] = {
h1: (props) => <Heading level={1} {...props} />,
h2: (props) => <Heading level={2} {...props} />,
h3: (props) => <Heading level={3} {...props} />,
h4: (props) => <Heading level={4} {...props} />,
h5: (props) => <Heading level={5} {...props} />,
h6: (props) => <Heading level={6} {...props} />,
p: (props) => (
<p className="my-3 leading-relaxed first:mt-0 last:mb-0" {...props} />
),
a: (props) => (
<a
className="text-highlight-blue underline-offset-2 hover:underline"
rel="noopener noreferrer"
target="_blank"
{...props}
/>
),
strong: (props) => <strong className="font-semibold" {...props} />,
em: (props) => <em className="italic" {...props} />,
ul: (props) => <ul className="my-3 ml-4 list-disc space-y-1" {...props} />,
ol: (props) => (
<ol className="my-3 ml-4 list-inside list-decimal space-y-1" {...props} />
),
li: (props) => <li className="pl-1" {...props} />,
blockquote: (props) => (
<blockquote
className="my-3 border-faint border-l-2 pl-3 text-muted italic"
{...props}
/>
),
hr: () => <hr className="my-6 border-border-solid" />,
code: (props) => (
<code
className="bg-dim/10 px-1 py-0.5 font-mono text-[0.9em] text-highlight-yellow"
{...props}
/>
),
table: (props) => (
<div className="my-3 overflow-x-auto">
<table className="w-full border-collapse text-sm" {...props} />
</div>
),
thead: (props) => (
<thead className="border-border-solid border-b" {...props} />
),
tbody: (props) => <tbody {...props} />,
tr: (props) => (
<tr className="border-border-solid border-b last:border-0" {...props} />
),
th: (props) => (
<th className="px-3 py-2 text-left font-medium text-dim" {...props} />
),
td: (props) => <td className="px-3 py-2 text-muted" {...props} />,
}
and lost all syntax highlighting.
Ah, I see the issue! The problem is that your custom code component is handling both inline code and code blocks. In Streamdown, code blocks are rendered differently than inline code, but they both use the code element.
When you override the code component, you're losing the syntax highlighting because code blocks need to render the highlighted tokens that Shiki generates, not just plain text.
Let me search for how code blocks work in the codebase to give you the exact solution:
Let me search for how inline code is distinguished from code blocks:
Let me check the test files to understand the structure:
Let me look at the components test file directly to see how code is tested:
Let me try a different approach and search for how the code component receives its props:
Let me check the documentation for the solution:
The documentation mentions that the node prop is passed. Let me check the actual source by searching for how code blocks are detected:
Let me search the GitHub repo directly for examples:
Perfect! I found the issue. There's actually a GitHub issue (#191) about this exact problem. The problem is that the code component is used for both inline code and code blocks, so when you override it, you lose the syntax highlighting for code blocks.
The solution is to check whether the code is inline or a code block using the node prop. Here's how to fix your setup:
const streamdownComponents: ComponentProps<typeof Streamdown>["components"] = {
// ... your other components ...
code: (props) => {
// Check if this is inline code or a code block
// Code blocks have a parent element, inline code doesn't
const isInlineCode = !props.node?.parent || props.node.parent.type !== 'pre';
if (isInlineCode) {
// Your custom inline code styling
return (
<code
className="bg-dim/10 px-1 py-0.5 font-mono text-[0.9em] text-highlight-yellow"
{...props}
/>
);
}
// For code blocks, return the children as-is to preserve syntax highlighting
// The highlighting is already applied to the children by Shiki
return <code {...props} />;
},
// ... rest of your components ...
};
Even better approach: Use CSS to target inline code specifically:
const streamdownComponents: ComponentProps<typeof Streamdown>["components"] = {
// ... your other components ...
// Don't override code at all, use CSS instead
};
Then in your CSS file:
/* Style only inline code */
[data-streamdown="inline-code"] {
@apply bg-dim/10 px-1 py-0.5 font-mono text-[0.9em] text-highlight-yellow;
}
/* Keep code blocks as default */
[data-streamdown="code-block"] {
/* Keep default syntax highlighting */
}
The best solution (if you need full control) is to use the node prop to distinguish:
code: (props) => {
// The node object contains metadata about the element
// Code blocks will have children with language info
const isCodeBlock = props.node?.type === 'code' && props.node?.lang;
if (isCodeBlock) {
// Let Shiki-highlighted code through unchanged
return <code {...props} />;
}
// Apply your custom styling only to inline code
return (
<code
className="bg-dim/10 px-1 py-0.5 font-mono text-[0.9em] text-highlight-yellow"
{...props}
/>
);
},
Note: There's currently an open feature request (#191) to add a dedicated inlineCode component prop to make this even easier, but for now, the CSS approach is the most reliable way to preserve syntax highlighting while customizing inline code styling.
- one
- two
- three
chief
I'm not sure what you're asking with that list. Are you:
- Testing how Streamdown renders ordered lists?
- Asking me to do something with those three items?
- Just saying "chief" as a casual greeting?
Could you clarify what you'd like help with? 😊
yeah i was testing! noice. can u try reading a file? like the readme.
Sure! Let me read the README file for you:
Hmm, let me search differently:
Great! Found them. Let me read the main README:
There seems to be an issue with the file path handling. Let me try the root README instead:
Perfect! Here's the Streamdown README:
#Streamdown
A drop-in replacement for react-markdown, designed for AI-powered streaming.
##Overview
Formatting Markdown is easy, but when you tokenize and stream it, new challenges arise. Streamdown is built specifically to handle the unique requirements of streaming Markdown content from AI models, providing seamless formatting even with incomplete or unterminated Markdown blocks.
Streamdown powers the AI Elements Message component but can be installed as a standalone package for your own streaming needs.
##Features
- 🚀 Drop-in replacement for
react-markdown - 🔄 Streaming-optimized - Handles incomplete Markdown gracefully
- 🎨 Unterminated block parsing - Build with
remendfor better streaming quality - 📊 GitHub Flavored Markdown - Tables, task lists, and strikethrough support
- 🔢 Math rendering - LaTeX equations via KaTeX
- 📈 Mermaid diagrams - Render Mermaid diagrams as code blocks with a button to render them
- 🎯 Code syntax highlighting - Beautiful code blocks with Shiki
- 🛡️ Security-first - Built with
rehype-hardenfor safe rendering - ⚡ Performance optimized - Memoized rendering for efficient updates
Want me to read more of it or look at a specific section?
what about if you send out "/packages/streamdown/README.md" as the path for your read command?
Good idea! Let me try that:
Nice! That worked! Here's the full README:
#Streamdown
A drop-in replacement for react-markdown, designed for AI-powered streaming.
##Overview
Formatting Markdown is easy, but when you tokenize and stream it, new challenges arise. Streamdown is built specifically to handle the unique requirements of streaming Markdown content from AI models, providing seamless formatting even with incomplete or unterminated Markdown blocks.
Streamdown powers the AI Elements Message component but can be installed as a standalone package for your own streaming needs.
##Features
- 🚀 Drop-in replacement for
react-markdown - 🔄 Streaming-optimized - Handles incomplete Markdown gracefully
- 🎨 Unterminated block parsing - Build with
remendfor better streaming quality - 📊 GitHub Flavored Markdown - Tables, task lists, and strikethrough support
- 🔢 Math rendering - LaTeX equations via KaTeX
- 📈 Mermaid diagrams - Render Mermaid diagrams as code blocks with a button to render them
- 🎯 Code syntax highlighting - Beautiful code blocks with Shiki
- 🛡️ Security-first - Built with
rehype-hardenfor safe rendering - ⚡ Performance optimized - Memoized rendering for efficient updates
##Installation
npm i streamdown
Then, update your Tailwind globals.css to include the following.
@source "../node_modules/streamdown/dist/*.js";
Make sure the path matches the location of the node_modules folder in your project. This will ensure that the Streamdown styles are applied to your project.
##Usage
You can use Streamdown in your React application like this:
import { Streamdown } from "streamdown";
export default function Page() {
const markdown = "# Hello World\n\nThis is **streaming** markdown!";
return <Streamdown>{markdown}</Streamdown>;
}
For more info, see the documentation.
Thanks for the tip! Using the relative path /packages/streamdown/README.md worked perfectly! 👍
cool. and what about "Grep README" and it not returning anything? is that expected? does Grep mathc file paths? wondering if i should create a Blob tool
Good question! Let me test this:
Interesting! So Grep does search file contents, not file paths. When I searched for README as a pattern, it found files that contain the text "README" in their content (like in package.json files), but it didn't match the filename itself.
The issue is that Grep searches file contents by default, not filenames. To find files by name, you need to use the glob parameter (which filters which files to search), or use List to explore directories.
So:
Grep= searches file contents with regex patternsList= lists directory structure and finds files by name/pathRead= reads the full content of a specific file you already know the path to
You're right that a Blob/FileList tool could be useful if you want to:
- Search for files by name/pattern (not content)
- Get file metadata without reading the full content
- List files matching a glob pattern with more detail than
Listprovides
That said, you can achieve most of what you need by combining:
List(withfilesOnly: trueandpattern) for file discoveryGrep(withglobparameter) to filter which files to searchRead(with the path from above) to get the full content
So the current tools work, but a dedicated file-search tool would definitely be more convenient! 👍
END OF POST
Claude Haiku 4.5