Skip to content

Commit ae33426

Browse files
kiwicoppleclaude
andcommitted
Replace react-markdown with Streamdown renderer
- Migrated from react-markdown (v9.0.1) to streamdown (v1.0.11) - Removed dependencies: react-markdown, remark-gfm, rehype-highlight - Updated Markdown component to use Streamdown API - Added transpilePackages config for KaTeX CSS handling - Preserved all custom component mappings and copy functionality Benefits: Built-in GFM support, Shiki syntax highlighting, LaTeX math support, better incomplete markdown handling, and security hardening. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <[email protected]>
1 parent 157a948 commit ae33426

File tree

4 files changed

+595
-84
lines changed

4 files changed

+595
-84
lines changed

website/components/ui/markdown.tsx

Lines changed: 17 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
import { ComponentPropsWithoutRef } from 'react'
2-
import ReactMarkdown from 'react-markdown'
3-
import rehypeHighlight from 'rehype-highlight'
4-
import remarkGfm from 'remark-gfm'
2+
import Streamdown from 'streamdown'
53
import { cn } from '~/lib/utils'
64
import CopyButton from './copy-button'
75
import A from './typography/a'
@@ -12,7 +10,7 @@ import Li from './typography/li'
1210
import P from './typography/p'
1311
import Strong from './typography/strong'
1412

15-
type MarkdownProps = ComponentPropsWithoutRef<typeof ReactMarkdown> & {
13+
type MarkdownProps = ComponentPropsWithoutRef<typeof Streamdown> & {
1614
copyableCode?: boolean
1715
}
1816

@@ -33,50 +31,50 @@ function childrenToText(children: any): string {
3331
}
3432

3533
const DEFAULT_COMPONENTS: MarkdownProps['components'] = {
36-
pre({ node, children, className, ...props }) {
34+
pre({ children, className, ...props }) {
3735
return (
3836
<pre {...props} className={cn('relative', className)}>
3937
{children}
4038
</pre>
4139
)
4240
},
43-
code({ node, className, children, ...props }) {
41+
code({ className, children, ...props }) {
4442
const isInline = !className?.includes('language-')
4543
return (
4644
<code {...props} className={cn(isInline && 'dark:text-white', className)}>
4745
{children}
4846
</code>
4947
)
5048
},
51-
a({ node, children, ...props }) {
49+
a({ children, ...props }) {
5250
return <A {...props}>{children}</A>
5351
},
54-
p({ node, children, ...props }) {
52+
p({ children, ...props }) {
5553
return <P {...props}>{children}</P>
5654
},
57-
li({ node, children, ...props }) {
55+
li({ children, ...props }) {
5856
return <Li {...props}>{children}</Li>
5957
},
60-
strong({ node, children, ...props }) {
58+
strong({ children, ...props }) {
6159
return <Strong {...props}>{children}</Strong>
6260
},
63-
h1({ node, children, ...props }) {
61+
h1({ children, ...props }) {
6462
return <H1 {...props}>{children}</H1>
6563
},
66-
h2({ node, children, ...props }) {
64+
h2({ children, ...props }) {
6765
return <H2 {...props}>{children}</H2>
6866
},
69-
h3({ node, children, ...props }) {
67+
h3({ children, ...props }) {
7068
return <H3 {...props}>{children}</H3>
7169
},
72-
th({ node, className, children, ...props }) {
70+
th({ className, children, ...props }) {
7371
return (
7472
<th className={cn('font-semibold dark:text-white', className)} {...props}>
7573
{children}
7674
</th>
7775
)
7876
},
79-
td({ node, className, children, ...props }) {
77+
td({ className, children, ...props }) {
8078
return (
8179
<td className={cn('dark:text-white', className)} {...props}>
8280
{children}
@@ -86,7 +84,7 @@ const DEFAULT_COMPONENTS: MarkdownProps['components'] = {
8684
}
8785

8886
const COPYABLE_CODE_COMPONENTS: MarkdownProps['components'] = {
89-
code({ node, className, children, ...props }) {
87+
code({ className, children, ...props }) {
9088
const isInline = !className?.includes('language-')
9189
return (
9290
<code {...props} className={cn(isInline && 'dark:text-white', className)}>
@@ -106,16 +104,12 @@ const COPYABLE_CODE_COMPONENTS: MarkdownProps['components'] = {
106104

107105
const Markdown = ({
108106
className,
109-
remarkPlugins = [],
110-
rehypePlugins = [],
111107
components,
112108
copyableCode = true,
113109
children,
114110
...props
115111
}: MarkdownProps) => (
116-
<ReactMarkdown
117-
remarkPlugins={[remarkGfm, ...(remarkPlugins || [])]}
118-
rehypePlugins={[rehypeHighlight, ...(rehypePlugins || [])]}
112+
<Streamdown
119113
className={cn('prose lg:prose-xl max-w-none', className)}
120114
components={{
121115
...DEFAULT_COMPONENTS,
@@ -125,7 +119,7 @@ const Markdown = ({
125119
{...props}
126120
>
127121
{children}
128-
</ReactMarkdown>
122+
</Streamdown>
129123
)
130124

131-
export default Markdown
125+
export default Markdown

website/next.config.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ const cspHeader = `
1111

1212
const nextConfig = {
1313
reactStrictMode: true,
14+
transpilePackages: ['streamdown'],
1415
async headers() {
1516
return [
1617
{

0 commit comments

Comments
 (0)