markdown

An opinionated markdown component that can be used to display markdown content. It was a conscious choice to not include syntax highlighting for code blocks as I wanted to give users freedom to use their preferred syntax highlighter.

Lorem Ipsum

Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.

Installation

npx shadcn add "https://registry.niels.foo/markdown.json"

With Code

You can use the Code component within the Markdown component to display syntax highlighted code properly within a Markdown document.

To do this edit the components/ui/markdown.tsx component and uncomment the code that is there.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 import ReactMarkdown, { Options } from "react-markdown"; import remarkGfm from "remark-gfm"; import { cn } from "@/lib/utils"; export type MarkdownProps = Options; export function Markdown({ children, className, ...props }: MarkdownProps) { return ( <ReactMarkdown className={cn( "prose prose-neutral break-words dark:prose-invert prose-p:leading-relaxed prose-pre:overflow-visible prose-pre:bg-inherit prose-pre:p-0 prose-p:m-0", className )} remarkPlugins={[remarkGfm]} components={{ p({ children }) { return <p className="mb-2 last:mb-0">{children}</p>; }, code({ children, className, ...rest }) { const match = /language-(\w+)/.exec(className || ""); if (!match) { return ( <code {...rest} className={cn("font-semibold text-primary", className)} > {children} </code> ); } const code = String(children).replace(/\n$/, ""); const language = match[1]; return ( <Code language={language}> <CodeHeader> <CodeLanguage /> <CodeCopyButton /> </CodeHeader> <CodeContent showLineNumbers>{code}</CodeContent> </Code> ); }, }} {...props} > {children} </ReactMarkdown> ); }

Source

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 import ReactMarkdown, { Options } from "react-markdown"; import remarkGfm from "remark-gfm"; import { cn } from "@/lib/utils"; export type MarkdownProps = Options; export function Markdown({ children, className, ...props }: MarkdownProps) { return ( <ReactMarkdown className={cn( "prose prose-neutral break-words dark:prose-invert prose-p:leading-relaxed prose-pre:overflow-visible prose-pre:bg-inherit prose-pre:p-0", className )} remarkPlugins={[remarkGfm]} components={{ p({ children }) { return <p className="mb-2 last:mb-0">{children}</p>; }, code({ children, className, ...rest }) { // const match = /language-(\w+)/.exec(className || ""); // if (!match) { // return ( // <code // {...rest} // className={cn("font-semibold text-primary", className)} // > // {children} // </code> // ); // } // const code = String(children).replace(/\n$/, ""); // const language = match[1]; // return ( // <Code language={language}> // <CodeHeader> // <CodeLanguage /> // <CodeCopyButton /> // </CodeHeader> // <CodeContent showLineNumbers> // {code} // </CodeContent> // </Code> // ); return ( <code {...rest} className={cn("font-semibold text-primary", className)} > {children} </code> ); }, }} {...props} > {children} </ReactMarkdown> ); }