A zero-backend browser tool that converts raw markdown or plain text into a fully styled, responsive, self-contained HTML documentation page — complete with auto-generated table of contents, Prism.js syntax highlighting, dark/light theme toggle, mobile sidebar navigation, split-screen editing, search & replace, PDF export, and markdown export. No server, no dependencies, one-click download.

Writing documentation is one thing. Presenting it well is another. Most developers write their notes, guides, and tutorials in plain markdown, then either leave them as raw .md files or drop them into a third-party platform they don't control. I built HTML Documentation Generator to bridge that gap — a browser-based tool that takes your raw markdown or plain text and converts it into a complete, self-contained, beautifully styled HTML documentation page in real time, with no backend, no server, and no external services.
Paste your content, see the output live, and download a single .html file that works completely offline — including syntax highlighting, dark/light theme toggle, mobile navigation, and a generated table of contents.
The tool is a real-time markdown-to-HTML converter with a full documentation page template baked in. Everything the output page needs — CSS, JavaScript, theme logic, mobile sidebar, copy-to-clipboard — is inlined into a single .html file that you can open in any browser without an internet connection (except for loading Prism.js from CDN on first use).
<iframe> loaded with data:text/html;charset=utf-8,.... The preview is fully interactive — theme toggle, copy buttons, and mobile sidebar all work inside the preview frame.h1, h2, and h3 heading in the input is extracted and added to the sidebar TOC with anchor links. The sidebar collapses on mobile with a hamburger menu overlay.```javascript, ```python, etc.) are converted into Prism-highlighted <pre><code> blocks with a per-block Copy button. The language label is displayed in the code block header.data-theme on the body element, CSS variables for every color token, and localStorage persistence so the user's preference is remembered across sessions.Ctrl+F) lets you search for any string in the markdown input and replace all instances at once via a global regex..html (Ctrl+S), export the raw markdown as .md, open the print dialog for PDF export, or download a documentation package bundle.Ctrl+S downloads the HTML, Ctrl+F opens search, Ctrl+Enter triggers replace all, Escape closes any open dialog or fullscreen mode.'use client' component — Next.js is used for its build pipeline, TypeScript support, and Vercel deployment, not for SSR.useState, useEffect, useCallback, useRef. No external state management. The entire conversion pipeline is driven by a single useEffect watching rawContent.Card, Button, Textarea, Input, Badge, Dialog, Separator. The generated HTML output uses none of these — it's a completely independent vanilla CSS page.cdnjs.cloudflare.com for syntax highlighting. The autoloader plugin handles language detection automatically, so no individual language bundles need to be specified.The convertToHTML function is a line-by-line markdown parser that walks the input string and builds the HTML output document incrementally. It's deliberately simple — not a full CommonMark parser — because the target use case is technical documentation with a known, consistent format: headings, paragraphs, fenced code blocks, and inline bold.
for (let i = 0; i < lines.length; i++) {
const line = lines[i].trim()
if (line.startsWith('# ')) → h1 + TOC entry
if (line.startsWith('## ')) → h2 + TOC entry
if (line.startsWith('### ')) → h3 + TOC entry (indented)
if (line.startsWith('```')) → collect lines until closing ```, emit code block
if (line.startsWith('**') && line.endsWith('**')) → standalone bold paragraph
if (line.length > 0) → paragraph with inline **bold** processing
else → blank line (whitespace)
}
The code block parser is stateful — when it hits an opening fence (```language), it increments i and accumulates lines until it hits a closing fence, then emits the full .code-container block with a language label, a per-block Copy button, and the content HTML-escaped (< → <, > → >) to prevent injection. The copy button uses data-code="encodeURIComponent(content)" to safely pass the code content as an attribute.
The output is a completely self-contained HTML document — no external stylesheets, no framework dependencies. Everything needed to render the page is inlined:
:root defines all color tokens for light mode; [data-theme="dark"] overrides them for dark mode. Every element uses these variables, so the entire page re-themes instantly when data-theme is toggled on the body.clamp() for responsive font sizes: h1 scales from 1.8rem to 2.5rem, h2 from 1.4rem to 2rem, h3 from 1.2rem to 1.5rem.overflow-x: auto with custom scrollbar styling (::-webkit-scrollbar) in both light and dark themes. The scrollbar thumb uses the accent color for visual consistency.localStorage on load and writes back on every toggle, so the user's preference persists across page reloads.[data-theme="dark"] and [data-theme="light"] token color overrides are inlined in the CSS, matching GitHub's dark and light syntax highlighting palettes respectively. After every theme toggle, Prism.highlightAll() is called with a 100ms delay to re-highlight all code blocks under the new color scheme.navigator.clipboard.writeText() with a document.execCommand('copy') fallback for older browsers that don't support the modern Clipboard API.All four export formats are generated entirely in the browser:
// HTML download (Ctrl+S)
new Blob([generatedHTML], { type: 'text/html' })
→ downloaded as documentation.html
// Markdown export
new Blob([rawContent], { type: 'text/markdown' })
→ downloaded as documentation.md
// PDF export
window.open('', '_blank')
→ write generatedHTML to new window
→ call printWindow.print() after 250ms delay
→ browser print dialog (Save as PDF)
// Package download
Blob with metadata comment header + generatedHTML
→ downloaded as documentation-package.html
The PDF export uses the browser's native print dialog rather than a library like jsPDF or Puppeteer — which means it preserves all the CSS styling, syntax highlighting, and layout exactly as the user sees it in the preview, with no server-side rendering required. The 250ms delay before calling print() allows the new window's DOM to fully render before the print dialog opens.
The search and replace system uses a Dialog component from shadcn/ui, triggered by the toolbar button or Ctrl+F. On Replace All, it constructs a global regex from the search term and runs rawContent.replace(new RegExp(searchTerm, 'g'), replaceTerm), updating the state — which in turn triggers the useEffect to regenerate the HTML output with the new content. The keyboard shortcut system is managed by a global keydown listener attached in a useEffect with proper cleanup.
The live preview is rendered inside a sandboxed <iframe> using the data: URI scheme:
<iframe
src={`data:text/html;charset=utf-8,${encodeURIComponent(generatedHTML)}`}
sandbox="allow-scripts allow-same-origin allow-popups"
title="HTML Preview"
/>
The sandbox attribute allows scripts (needed for theme toggle and copy buttons to work inside the preview) and same-origin (needed for localStorage in the preview frame), while blocking form submissions, top-level navigation, and other potentially unsafe behaviors. encodeURIComponent handles all special characters in the generated HTML safely as a data URI.
The editor itself is fully responsive. The toolbar uses flex-wrap with abbreviated button labels on mobile (hidden sm:inline for full labels, sm:hidden for short labels). Split-screen mode activates a lg:grid-cols-2 layout — on smaller screens, the panels stack vertically. Fullscreen preview mode sets the output card to col-span-full and hides the input panel entirely. All panel heights are responsive: 300px on mobile, 500px on desktop, scaling up in split-screen and fullscreen modes.
marked or remark would make it production-ready for arbitrary markdown.index.html, README.md, and a package.json would be more useful for developers who want a structured deliverable.localStorage on every change would prevent losing work accidentally.HTML Documentation Generator is a tool I built because I kept running into the same friction: I had solid technical notes in markdown, but no clean way to share them as standalone, readable documents without a platform dependency. Building the converter from scratch taught me a lot about the browser's native capabilities — the File API, the Clipboard API, the print system, iframe sandboxing, and data URIs. The most interesting insight was how much you can accomplish with zero backend if you're willing to work with what the browser already provides.
// Tech Stack