Markdown Renderer
Overview
Section titled “Overview”Markdown Renderer is a drop-in TypeScript library that converts Markdown into beautifully styled HTML right in the browser. Import it from a CDN with a single <script type="module"> tag, hand it a string or a URL, and it returns a fully rendered DOM element — complete with syntax-highlighted code blocks and Tailwind Typography prose styling. No bundler, no config, no framework required.
Live Demo
Section titled “Live Demo”See it in action — the demo page renders its own README as a live preview: jadujoel.github.io/markdown-renderer
Features
Section titled “Features”- Single-Import Usage — Add one
<script type="module">tag pointing at the CDN-hosted bundle and you’re done. Nonpm install, no build step for consumers. - Three Render APIs —
render(text)for raw Markdown strings,renderUrl(url)to fetch-and-render in one call, andrenderUrlToElement(url, element)to inject directly into an existing DOM node. - Syntax Highlighting — Code blocks are highlighted via highlight.js with the Monokai Sublime theme, auto-detecting language when possible and falling back to plaintext.
- Tailwind Typography Styling — Output is wrapped in Tailwind’s
prose/dark:prose-invertclasses via the@tailwindcss/typographyplugin, giving Markdown content polished, readable formatting out of the box. - Build-Time CSS Inlining — A Bun macro (
css.macro.ts) fetches the highlight.js theme CSS and runstailwindcssat build time, then injects the combined stylesheet into the page head at runtime — no extra network requests for styling. - GitHub Pages CI/CD — A GitHub Actions workflow builds and deploys the library to GitHub Pages on every push to
main, so the CDN bundle is always up to date.
How It Works
Section titled “How It Works”The core module in src/index.ts wires together marked for Markdown parsing and highlight.js for code highlighting through a custom renderer:
const renderer = new marked.Renderer();renderer.code = ({ text, lang }) => { const language = hljs.getLanguage(lang ?? "plaintext") === undefined ? "plaintext" : lang ?? "plaintext"; const highlighted = hljs.highlight(text, { language }).value; return `<pre><code class="hljs language-${language}">${highlighted}</code></pre>`;};The library exposes three functions — a synchronous render() for strings, and two async helpers that fetch remote Markdown first:
// Render a raw stringconst { html, div } = render(markdownText);document.body.appendChild(div);
// Fetch + render in one stepconst result = await renderUrl("https://example.com/README.md");document.body.appendChild(result.div);
// Fetch + inject into an existing elementawait renderUrlToElement("https://example.com/README.md", myDiv);Build-Time CSS Macro
Section titled “Build-Time CSS Macro”One of the more interesting patterns in this project is css.macro.ts — a Bun macro that runs at build time rather than at runtime:
export async function style() { const monokai = await fetch( "https://cdnjs.cloudflare.com/.../monokai-sublime.min.css" ).then((res) => res.text()); await Bun.$`bunx tailwindcss -i src/tailwind.css -o dist/index.css`; const tailwind = await Bun.file("dist/index.css").text(); return `<style>\n${monokai}\n${tailwind}</style>`;}This is imported with with { type: 'macro' }, so Bun evaluates the function during bundling and replaces the call site with the resulting string. The final bundle ships with all CSS inlined — zero additional HTTP requests for consumers.
Tech Stack
Section titled “Tech Stack”| Language | TypeScript |
| Runtime | Browser (ES Modules) |
| Markdown Parser | marked 14 |
| Syntax Highlighting | highlight.js 11 |
| Styling | Tailwind CSS 3 + @tailwindcss/typography |
| Build Tool | Bun (bundler + macros) |
| Linting | Biome |
| Deployment | GitHub Pages via GitHub Actions |
Source Code
Section titled “Source Code”The source code is available on the project’s GitHub repository.