|
| 1 | +import * as React from "react" |
| 2 | +import ReactMarkdown from "react-markdown" |
| 3 | +import remarkGfm from "remark-gfm" |
| 4 | +import rehypeSanitize from "rehype-sanitize" |
| 5 | +import { cn } from "@/lib/utils" |
| 6 | + |
| 7 | +/** |
| 8 | + * Props for the Markdown component. |
| 9 | + * |
| 10 | + * This component renders markdown content using react-markdown with GitHub Flavored Markdown support. |
| 11 | + * All content is sanitized to prevent XSS attacks. |
| 12 | + */ |
| 13 | +export interface MarkdownProps { |
| 14 | + /** |
| 15 | + * The markdown content to render. Supports GitHub Flavored Markdown including: |
| 16 | + * - Headers (H1-H6) |
| 17 | + * - Bold, italic, and inline code |
| 18 | + * - Links and images |
| 19 | + * - Lists (ordered, unordered, and nested) |
| 20 | + * - Tables |
| 21 | + * - Blockquotes |
| 22 | + * - Code blocks |
| 23 | + */ |
| 24 | + content: string |
| 25 | + |
| 26 | + /** |
| 27 | + * Optional CSS class name to apply custom styling to the markdown container. |
| 28 | + * The component uses Tailwind CSS prose classes for typography by default. |
| 29 | + */ |
| 30 | + className?: string |
| 31 | +} |
| 32 | + |
| 33 | +function Markdown({ content, className }: MarkdownProps) { |
| 34 | + return ( |
| 35 | + <div |
| 36 | + data-slot="markdown" |
| 37 | + className={cn( |
| 38 | + "prose prose-sm dark:prose-invert max-w-none", |
| 39 | + "prose-headings:font-semibold prose-headings:tracking-tight", |
| 40 | + "prose-h1:text-3xl prose-h2:text-2xl prose-h3:text-xl", |
| 41 | + "prose-p:leading-relaxed prose-p:text-foreground", |
| 42 | + "prose-a:text-primary prose-a:no-underline hover:prose-a:underline", |
| 43 | + "prose-code:text-foreground prose-code:bg-muted prose-code:px-1 prose-code:py-0.5 prose-code:rounded prose-code:before:content-none prose-code:after:content-none", |
| 44 | + "prose-pre:bg-muted prose-pre:text-foreground prose-pre:border", |
| 45 | + "prose-blockquote:border-l-primary prose-blockquote:text-muted-foreground", |
| 46 | + "prose-strong:text-foreground prose-strong:font-semibold", |
| 47 | + "prose-ul:list-disc prose-ol:list-decimal", |
| 48 | + "prose-li:text-foreground prose-li:marker:text-muted-foreground", |
| 49 | + "prose-table:border prose-th:border prose-th:bg-muted prose-td:border", |
| 50 | + "prose-img:rounded-md prose-img:border", |
| 51 | + className |
| 52 | + )} |
| 53 | + > |
| 54 | + <ReactMarkdown |
| 55 | + remarkPlugins={[remarkGfm]} |
| 56 | + rehypePlugins={[rehypeSanitize]} |
| 57 | + > |
| 58 | + {content} |
| 59 | + </ReactMarkdown> |
| 60 | + </div> |
| 61 | + ) |
| 62 | +} |
| 63 | + |
| 64 | +export { Markdown } |
0 commit comments