Raw data to Webpage Convertor
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.

Introduction
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.
What it does
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).
- Real-time conversion — the HTML output regenerates 300ms after every keystroke in the input panel, debounced to avoid unnecessary reprocessing on fast typing.
- Split-screen editing mode — toggle a side-by-side layout with the markdown input on the left and the generated HTML source (or live preview) on the right, keeping both panels visible simultaneously.
- Live iframe preview — toggle from HTML source view to a live rendered preview via a sandboxed
<iframe>loaded withdata:text/html;charset=utf-8,.... The preview is fully interactive — theme toggle, copy buttons, and mobile sidebar all work inside the preview frame. - Fullscreen preview mode — expand the output panel to fill the entire viewport for distraction-free review of the generated page.
- Auto-generated table of contents — every
h1,h2, andh3heading in the input is extracted and added to the sidebar TOC with anchor links. The sidebar collapses on mobile with a hamburger menu overlay. - Prism.js syntax highlighting — fenced code blocks (
```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. - Dark/light theme toggle — the generated page has a fully working theme system using
data-themeon the body element, CSS variables for every color token, andlocalStoragepersistence so the user's preference is remembered across sessions. - Search & replace — a modal dialog (triggered by the toolbar button or
Ctrl+F) lets you search for any string in the markdown input and replace all instances at once via a global regex. - Multiple export formats — download as
.html(Ctrl+S), export the raw markdown as.md, open the print dialog for PDF export, or download a documentation package bundle. - Keyboard shortcuts —
Ctrl+Sdownloads the HTML,Ctrl+Fopens search,Ctrl+Entertriggers replace all,Escapecloses any open dialog or fullscreen mode.
Tech stack
- Next.js 14 (App Router) — framework and routing. Like the other tools in this portfolio, the entire application is a single
'use client'component — Next.js is used for its build pipeline, TypeScript support, and Vercel deployment, not for SSR. - React 18 —
useState,useEffect,useCallback,useRef. No external state management. The entire conversion pipeline is driven by a singleuseEffectwatchingrawContent. - shadcn/ui — component library for the editor UI:
Card,Button,Textarea,Input,Badge,Dialog,Separator. The generated HTML output uses none of these — it's a completely independent vanilla CSS page. - Tailwind CSS — utility styling for the editor application shell. The generated HTML page uses custom CSS variables only — no Tailwind in the output.
- next-themes — theme provider for the editor UI itself (separate from the theme system inside the generated HTML output).
- Prism.js (CDN) — loaded in the generated HTML output via
cdnjs.cloudflare.comfor syntax highlighting. The autoloader plugin handles language detection automatically, so no individual language bundles need to be specified. - Lucide React — icon set for the toolbar buttons.
- Blob + URL.createObjectURL — all downloads (HTML, markdown) are generated client-side with no server round-trip.
- Vercel — deployment and hosting.
The conversion pipeline
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 generated HTML page
The output is a completely self-contained HTML document — no external stylesheets, no framework dependencies. Everything needed to render the page is inlined:
- CSS variable theme system —
:rootdefines 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 whendata-themeis toggled on the body. - Responsive layout — fixed sidebar (280px) on desktop, slide-in drawer with hamburger menu and overlay on mobile. The sidebar hides automatically when a TOC link is clicked on mobile.
- Fluid typography — headings use
clamp()for responsive font sizes:h1scales from 1.8rem to 2.5rem,h2from 1.4rem to 2rem,h3from 1.2rem to 1.5rem. - Scrollable code blocks — code blocks use
overflow-x: autowith custom scrollbar styling (::-webkit-scrollbar) in both light and dark themes. The scrollbar thumb uses the accent color for visual consistency. - localStorage theme persistence — the theme toggle reads from
localStorageon load and writes back on every toggle, so the user's preference persists across page reloads. - Prism.js token overrides — both
[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. - Clipboard API with fallback — the copy button uses
navigator.clipboard.writeText()with adocument.execCommand('copy')fallback for older browsers that don't support the modern Clipboard API.
Export system
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.
Search and replace
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.
Iframe preview sandboxing
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.
Responsive editor layout
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.
What I'd improve
- Full CommonMark support — the current parser handles the most common cases but doesn't support tables, blockquotes, ordered/unordered lists, horizontal rules, or nested inline formatting. Integrating a proper parser like
markedorremarkwould make it production-ready for arbitrary markdown. - Real ZIP export with JSZip — the current "package" export bundles everything into a single HTML file with metadata comments. A proper ZIP containing
index.html,README.md, and apackage.jsonwould be more useful for developers who want a structured deliverable. - Custom theme builder — letting users change the accent color, font family, and sidebar width in the editor and see those changes reflected in both the preview and the exported HTML would make the tool significantly more flexible.
- Local autosave — the content disappears on page refresh. Persisting the textarea content to
localStorageon every change would prevent losing work accidentally. - Multiple document tabs — for users writing multi-page documentation, being able to maintain multiple documents simultaneously in tabs would be a natural workflow improvement.
- Custom CSS injection — an advanced panel that lets users paste additional CSS to override the default documentation styles, without editing the generated HTML manually.
Conclusion
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