Plain text files are everywhere. Log outputs, user-submitted content, legacy exports from older systems, email bodies stripped of their markup — a huge portion of the text that flows through applications is unformatted .txt content. Displaying it on the web requires converting it to HTML, which turns out to be a surprisingly nuanced problem.
This guide explains the challenges of plain text to HTML conversion, the decisions you need to make when writing a converter, and how to skip all of it by using an API that handles the formatting logic for you.
Why Plain Text to HTML Is Harder Than It Looks
You might think wrapping everything in a <pre> tag is sufficient. It preserves whitespace and line breaks, but it renders in a monospace font and does not respond well to different screen sizes. For most use cases, you want proper paragraphs, linked URLs, and readable line widths.
A complete TXT-to-HTML conversion needs to handle:
- Paragraph detection — consecutive lines with no blank line between them belong to the same paragraph. A blank line signals a paragraph break.
- Line break handling — should single newlines produce
<br>tags, or should lines be soft-wrapped into a single paragraph? - URL auto-linking — plain text often contains URLs. Converting them to
<a href>elements makes them clickable. - Special character escaping — raw angle brackets, ampersands, and quotes in the text must be escaped to valid HTML entities to prevent rendering issues and XSS.
- Indentation preservation — indented blocks (code samples, quoted text) should be preserved inside
<pre>or<blockquote>elements. - Email and phone recognition — optionally converting email addresses to
mailto:links and phone numbers totel:links.
Writing logic that handles all these cases correctly is tedious work. The DocForge /api/txt-to-html endpoint handles all of it.
Basic Conversion with the API
The simplest call converts a plain text string to HTML paragraphs:
curl -X POST https://docforge-api.vercel.app/api/txt-to-html \ -H "Content-Type: application/json" \ -d '{ "text": "Welcome to DocForge API.\n\nThis is the second paragraph. It has multiple sentences.\n\nVisit https://docforge-api.vercel.app for the full documentation." }'
The response provides formatted HTML with auto-linked URLs:
{
"html": "<p>Welcome to DocForge API.</p>\n<p>This is the second paragraph. It has multiple sentences.</p>\n<p>Visit <a href=\"https://docforge-api.vercel.app\">https://docforge-api.vercel.app</a> for the full documentation.</p>",
"meta": {
"paragraphCount": 3,
"wordCount": 24,
"urlCount": 1
}
}
Converting Text Files in Bulk
For processing a directory of .txt files — a common task when migrating legacy content to the web — a simple shell loop covers most cases:
#!/bin/bash # Converts all .txt files in ./content/ to HTML in ./dist/ mkdir -p dist for file in content/*.txt; do name=$(basename "$file" .txt) content=$(cat "$file") curl -s -X POST https://docforge-api.vercel.app/api/txt-to-html \ -H "Content-Type: application/json" \ -H "X-API-Key: $DOCFORGE_API_KEY" \ -d "{\"text\": $(echo "$content" | jq -Rs .)}" \ | jq -r .html > "dist/${name}.html" echo "Converted: $file" done
Handling User-Submitted Text Content
When users submit free-form text through a form — a support ticket, a comment, a bio field — you need to render it safely as HTML. The naive approach of using a template literal to embed the text in a <p> tag creates an XSS vulnerability if the text contains angle brackets or script tags.
The DocForge API escapes all special characters before wrapping content in HTML elements, making it safe to inject the output directly into a page:
async function renderUserBio(rawText) { const response = await fetch( 'https://docforge-api.vercel.app/api/txt-to-html', { method: 'POST', headers: { 'Content-Type': 'application/json', 'X-API-Key': process.env.DOCFORGE_API_KEY }, body: JSON.stringify({ text: rawText, options: { linkUrls: true, linkEmails: true } }) } ); const { html } = await response.json(); // html is sanitized — safe to render directly return html; } // Even malicious input is safely escaped const html = await renderUserBio( 'Hi! I work at <script>alert(1)</script>Acme Corp.' ); // Output: <p>Hi! I work at <script>alert(1)</script>Acme Corp.</p>
Formatting Options
The options parameter lets you customize how the conversion behaves. Common options include:
linkUrls(boolean, default true) — auto-convert bare URLs to anchor tagslinkEmails(boolean, default false) — wrap email addresses inmailto:linkspreserveIndent(boolean, default true) — wrap indented blocks in<pre>tagshardBreaks(boolean, default false) — emit a<br>for every single newline instead of combining lines into paragraphs
curl -X POST https://docforge-api.vercel.app/api/txt-to-html \ -H "Content-Type: application/json" \ -d '{ "text": "Installation steps:\n\n npm install docforge\n npm start\n\nThe server is now running.", "options": { "preserveIndent": true, "linkUrls": false } }'
Use Case: Rendering Legacy Documentation
Many older systems store documentation as plain text files — README.txt, CHANGELOG.txt, INSTALL.txt. Migrating these to a web-friendly format is a common task when modernizing a codebase or launching a public documentation site.
For a complete pipeline that integrates this conversion step into a CI/CD workflow and publishes the results automatically, see our guide on automating documentation generation with APIs.
If you are working with structured formats like Markdown or JSON rather than plain text, you might also find these guides useful:
- How to Convert Markdown to HTML with a Simple API Call — covers the
/api/md-to-htmlendpoint - JSON to HTML Rendering: The Developer's Complete Guide — covers the
/api/json-to-htmlendpoint
Use Case: Email Body Rendering
When you display the plain-text body of an email in a web interface, simply wrapping it in a <pre> block looks broken on mobile and does not respect your site's typography. The TXT-to-HTML endpoint converts email-style text — with signature separators, quoted reply blocks, and paragraph breaks — into clean, readable HTML.
import requests def render_email_body(plain_text: str) -> str: response = requests.post( "https://docforge-api.vercel.app/api/txt-to-html", headers={"X-API-Key": API_KEY}, json={ "text": plain_text, "options": { "linkUrls": True, "linkEmails": True, "hardBreaks": False } } ) return response.json()["html"]
What About the Response Metadata?
Like all DocForge endpoints, /api/txt-to-html returns a meta object alongside the HTML. For plain text conversion, this includes:
paragraphCount— how many paragraph blocks were detectedwordCount— total word count of the source texturlCount— number of URLs found and optionally linked
These fields are useful for building reading-time estimates, pagination logic, or analytics dashboards that track content volume over time. See the full API documentation for the complete response schema.
Summary
Plain text to HTML conversion involves more decisions than it first appears — paragraph detection, URL linking, indentation handling, and character escaping all need careful treatment. The DocForge /api/txt-to-html endpoint handles all of these with sensible defaults and a straightforward options object to customize behaviour for your use case.
The free tier provides 500 requests per day with no API key required. For production workloads, the Pro plan at $9/month covers most applications with 50,000 requests per day.
Try Plain Text to HTML Free
500 requests/day, no credit card required. Convert your text files to clean HTML in seconds.
Try It Live