Markdown Renderer

AI & Chat

Markdown-to-HTML content renderer.

Preview

Hello World


This is bold and italic text.


const greeting = "Hello!";
console.log(greeting);

  • Item one
  • Item two
  • Item three
  • Usage

    example.jsx
    import { MarkdownRenderer } from "@/components/ui/markdown-renderer";
    
    export default function Example() {
      return <MarkdownRenderer />;
    }

    Source Code

    Copy this file into components/ui/markdown-renderer.jsx in your project.

    markdown-renderer.jsx
    import { forwardRef } from "react";
    import { cn } from "@/lib/utils";
    
    function renderInline(text) {
      return text
        .replace(/\*\*(.*?)\*\*/g, "<strong>$1</strong>")
        .replace(/\*(.*?)\*/g, "<em>$1</em>")
        .replace(/`(.*?)`/g, '<code class="rounded bg-muted px-1 py-0.5 font-mono text-sm">$1</code>')
        .replace(/\[(.*?)\]\((.*?)\)/g, '<a href="$2" class="text-primary underline">$1</a>');
    }
    
    const MarkdownRenderer = forwardRef(({ className, content = "", ...props }, ref) => {
      const lines = content.split("\n");
      const html = [];
      let inCode = false, codeBuf = [], codeLang = "";
    
      for (const line of lines) {
        if (line.startsWith("```")) {
          if (!inCode) { inCode = true; codeLang = line.slice(3); codeBuf = []; }
          else { html.push(`<pre class="overflow-x-auto rounded-lg bg-muted p-4 font-mono text-sm my-2"><code>${codeBuf.join("\n")}</code></pre>`); inCode = false; }
          continue;
        }
        if (inCode) { codeBuf.push(line.replace(/</g, "&lt;")); continue; }
        if (line.startsWith("### ")) html.push(`<h3 class="mt-4 mb-2 text-lg font-semibold">${line.slice(4)}</h3>`);
        else if (line.startsWith("## ")) html.push(`<h2 class="mt-4 mb-2 text-xl font-semibold">${line.slice(3)}</h2>`);
        else if (line.startsWith("# ")) html.push(`<h1 class="mt-4 mb-2 text-2xl font-bold">${line.slice(2)}</h1>`);
        else if (line.startsWith("- ") || line.startsWith("* ")) html.push(`<li class="ml-4 list-disc">${renderInline(line.slice(2))}</li>`);
        else if (/^\d+\.\s/.test(line)) html.push(`<li class="ml-4 list-decimal">${renderInline(line.replace(/^\d+\.\s/, ""))}</li>`);
        else if (line.startsWith("> ")) html.push(`<blockquote class="border-l-4 border-muted pl-4 italic text-muted-foreground my-2">${renderInline(line.slice(2))}</blockquote>`);
        else if (line.trim() === "") html.push("<br/>");
        else html.push(`<p class="my-1">${renderInline(line)}</p>`);
      }
    
      return <div ref={ref} className={cn("prose prose-sm max-w-none dark:prose-invert", className)} dangerouslySetInnerHTML={{ __html: html.join("") }} {...props} />;
    });
    MarkdownRenderer.displayName = "MarkdownRenderer";
    
    export { MarkdownRenderer };
    

    Quick Install

    Make sure you have the cn() utility set up. It requires clsx and tailwind-merge.

    npm install clsx tailwind-merge