react-markdown/SKILL.md
react-markdown — React component for rendering Markdown as React elements. Use when rendering Markdown in React, configuring remark/rehype plugins, customizing components, adding syntax highlighting to code blocks, or handling GFM (tables, strikethrough, task lists). Fetch live documentation for up-to-date details.
npx skillsauth add mikkelkrogsholm/dev-skills react-markdownInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
CRITICAL: Your training data for react-markdown is unreliable. APIs change between versions and memorized patterns may be wrong or deprecated. Before writing any code, you MUST use
WebFetchto read the current README:
WebFetch("https://github.com/remarkjs/react-markdown")If you need a specific remark or rehype plugin, also fetch:
WebFetch("https://github.com/remarkjs/remark/blob/main/doc/plugins.md")WebFetch("https://github.com/rehypejs/rehype/blob/main/doc/plugins.md")Do not proceed without fetching first. Never assume prop names or plugin options — verify against current docs.
react-markdown renders Markdown as React elements using a plugin pipeline: Markdown → remark (AST) → rehype (HTML AST) → React components. No dangerouslySetInnerHTML — everything is React elements.
dangerouslySetInnerHTML — outputs React elementsremark-gfmrehype-highlight or rehype-pretty-codeThe base package renders standard Markdown. For most apps you need two plugins:
import Markdown from 'react-markdown'
import remarkGfm from 'remark-gfm'
import rehypeHighlight from 'rehype-highlight'
<Markdown
remarkPlugins={[remarkGfm]}
rehypePlugins={[rehypeHighlight]}
>
{markdownContent}
</Markdown>
Packages to install: bun add react-markdown remark-gfm rehype-highlight
Don't forget the highlight.js CSS theme for syntax highlighting:
import 'highlight.js/styles/github-dark.css'
Override any HTML element rendered by Markdown. This is how you add custom styling, clickable links, or interactive elements:
<Markdown
components={{
// Custom code blocks
code({ className, children, ...props }) {
const match = /language-(\w+)/.exec(className || '')
return match ? (
<pre className="code-block">
<code className={className} {...props}>{children}</code>
</pre>
) : (
<code className="inline-code" {...props}>{children}</code>
)
},
// Custom links — open external in new tab
a({ href, children, ...props }) {
const isExternal = href?.startsWith('http')
return (
<a href={href} target={isExternal ? '_blank' : undefined} rel={isExternal ? 'noopener noreferrer' : undefined} {...props}>
{children}
</a>
)
},
// Custom images
img({ src, alt, ...props }) {
return <img src={src} alt={alt} loading="lazy" {...props} />
},
}}
>
{content}
</Markdown>
Always include remark-gfm for user-facing Markdown. Without it, tables, strikethrough, autolinks, and task lists silently render as plain text. This is the most common "bug" reported by users — the Markdown looks broken because GFM isn't enabled by default.
The children prop must be a string. Passing JSX or a React element to <Markdown> will not work. If your content is in a variable, ensure it's a string type: <Markdown>{String(content)}</Markdown>.
Distinguish inline code from code blocks in component overrides. The code component receives both inline (code) and block (```) code. Check for the presence of className (which contains language-* for fenced blocks) to differentiate. Inline code has no className.
Plugin order matters. remark plugins run in array order on the Markdown AST, then rehype plugins run on the HTML AST. If a rehype plugin depends on structure created by a remark plugin, ordering is critical. remarkGfm should always come first in remarkPlugins.
Re-renders on every content change. react-markdown parses and re-renders the full Markdown tree whenever the children string changes. For streaming chat (character-by-character updates), this can be expensive. Consider debouncing updates or using React.memo with a custom comparison on the parent component.
development
Zod — TypeScript-first schema validation with static type inference. Use when building with Zod or asking about schema definitions, type inference, parsing, transformations, refinements, coercion, error handling, or integration with forms, APIs, or tRPC. Fetch live documentation for up-to-date details.
tools
Vite — next-generation frontend build tool with instant dev server and optimized production builds. Use when building with Vite or asking about its APIs, configuration, plugins, SSR, environment variables, or integration with frameworks. Fetch live documentation for up-to-date details.
tools
Upstash — serverless Redis, QStash, and Vector database with per-request pricing optimized for edge and serverless environments. Use when building with Upstash or asking about its Redis client, QStash message queuing, rate limiting, workflows, or vector search. Fetch live documentation for up-to-date details.
tools
Turso — edge-hosted SQLite database built on libSQL with embedded replicas, multi-tenancy, and low-latency global distribution. Use when building with Turso or asking about its libSQL client, embedded replicas, database-per-tenant patterns, auth tokens, sync, or integration with Drizzle or other ORMs. Fetch live documentation for up-to-date details.