100% Local · Free · No Sign-up

Convert Plain Text (.txt) to Clean Markdown

A line scanner promotes the plain-text conventions that are unambiguous — setext underlines, bullet glyphs, 1) numbering, indented code blocks and bare URLs — and leaves your wording untouched. CRLF endings and a stray UTF-8 BOM are normalised on the way in, entirely inside your browser.
Convert Plain Text (.txt) to Clean MarkdownDrag & drop · .txt, .text

Processing happens 100% in your browser — nothing is uploaded.

What stays as-is

Headings are only recognised from ===/--- underlines, so a bare title line stays a paragraph. ASCII-art tables are not rebuilt as GFM tables, hard-wrapped paragraphs are not reflowed, and Markdown special characters in your text are not escaped — all three require guessing at intent, and guessing wrong is worse than leaving the line alone.
100% Local Processing — Your files never leave your device

How to convert TXT to Markdown

  1. Input: drop a .txt or .text file onto the dropzone, or paste the text straight into the editor.
  2. Process: the scanner normalises line endings and promotes setext headings, list markers, indented code and bare URLs locally.
  3. Copy: copy the Markdown or download it as a .md file.

Why Convert TXT to Markdown with MD Convert?

  • Non-destructive by design

    No paragraph reflowing and no "short line in title case is probably a heading" guesswork. Rules that are right eighty percent of the time force you to audit every line, which costs more than adding a few # characters by hand.

  • Real structure where it already exists

    Lines underlined with === or --- become H1/H2, *, +, •, ‣ and · bullets become -, 1) becomes 1., and bare http(s) URLs become <autolinks>. Existing Markdown and fenced code passes through verbatim.

  • Indented blocks become fenced code

    Runs of tab- or four-space-indented lines are dedented and wrapped in triple backticks — but only after a blank line and only with two or more lines, so list continuation lines are never mistaken for code.

  • Encoding hygiene, done locally

    CRLF and lone CR endings are normalised to LF, a leading UTF-8 byte-order mark is stripped so a first-line heading still parses, and runs of blank lines collapse. Nothing is uploaded; the text is read and transformed in your tab.

TXT to Markdown: Before and After

Setext underlines become ATX headings, asterisk bullets become hyphens, `1)` becomes `1.`, the indented block becomes a fenced code block with the indent removed, and the bare URL becomes an autolink. Not one word of prose is rewritten.

Input · text
Release Notes
=============

Summary
-------

Parser shipped. See https://md-convert.org/txt-to-markdown for details.

* faster tokenizer
* fewer allocations

1) upgrade the package
2) re-run the build

    const x = compile(src);
    console.log(x.stats);
Output · Markdown
# Release Notes

## Summary

Parser shipped. See <https://md-convert.org/txt-to-markdown> for details.

- faster tokenizer
- fewer allocations

1. upgrade the package
2. re-run the build

```
const x = compile(src);
console.log(x.stats);
```

Understanding the TXT Format: RFC 2046 §4.1 (text/plain) + Unicode UTF-8

Format
Plain Text
Media type
text/plain; charset=utf-8
Parser used
Custom line scanner (no dependencies)

Plain text has no formatting model at all — that is its definition. RFC 2046 specifies `text/plain` as content with no structural markup, where the only inherent semantics are the characters themselves and the line breaks between them. Everything a reader perceives as structure in a .txt file (a heading underlined with equals signs, a bulleted list, an indented code sample) is convention, not syntax.

That makes txt-to-Markdown a fundamentally different job from every other converter on this site. There is no source structure to translate; there is only human convention to recognise. And because CommonMark was designed as a formalisation of exactly those email-era plain-text conventions, most well-written text files are already valid Markdown — a paragraph is a paragraph, a blank line is a paragraph break, an indented block is already a code block.

So the work is narrow and specific: promote the conventions that are unambiguous, leave everything else exactly as written, and normalise the mechanical details that break rendering — CRLF line endings from Windows, a stray UTF-8 byte-order mark at the start of the file, and runs of blank lines that turn into unwanted gaps.

What gets promoted, and why the list is short

Six rules run over the file, in order. A line underlined with `===` becomes an H1 and a line underlined with `---` becomes an H2 — the setext convention, which CommonMark still supports and which is the only heading signal in plain text that carries no ambiguity. Bullets written as `*`, `+`, `•`, `‣` or `·` are normalised to `-`, the canonical GFM marker. Ordered items written `1)` become `1.`, which is the form every parser accepts. Runs of indented lines become fenced code blocks with the common indent stripped. Bare `http(s)` URLs become `<autolinks>`. Runs of three or more blank lines collapse to one.

What is deliberately absent is more interesting. There is no "this line is short and in title case, so it is probably a heading" heuristic, and no paragraph reflowing. Those rules are right perhaps eighty percent of the time, and a converter that is right eighty percent of the time on structure forces you to audit every line of the output — which costs more than typing a few `#` characters would have. The design assumption is that you would rather add three headings by hand than hunt for three headings that should not be there.

Existing Markdown passes through untouched. If your text file already contains `## headings`, tables or reference links, they are already valid and no rule fires on them. Content inside a fenced code block is copied verbatim, so a file containing example Markdown or shell output does not get its contents rewritten.

Indented blocks, code fences and the list-continuation trap

An indented block in plain text is almost always code, output or a transcript, and CommonMark already treats a four-space indent as a code block. Converting it to an explicit fence is still worth doing: fences are visible to the next person editing the file, they survive being pasted into an editor that re-indents, and they can carry a language tag later. The converter captures runs of lines indented by four spaces or a tab, strips the common indent, and wraps them in triple backticks.

The trap this rule has to avoid is list continuation. In Markdown, an indented line directly beneath a bullet belongs to that bullet — turning it into a code block would silently change the document's meaning. Two conditions prevent it: the indented run must be preceded by a blank line, and it must contain at least two indented lines. A continuation line hugging its bullet fails the first test, and a single stray indented line — usually a wrapped sentence or a hanging indent — fails the second and is left alone.

Tabs are expanded to four spaces when the block is dedented, because a mixture of tabs and spaces inside a fence renders unpredictably across viewers. Blank lines inside a captured block are preserved, so a multi-paragraph log stays intact rather than being split into two fences.

Encoding, line endings and why they matter

Windows writes `\r\n`, classic Mac tooling wrote `\r`, and Unix writes `\n`. A stray carriage return is invisible in most editors and causes real damage in Markdown: trailing `\r` characters can defeat blank-line detection, so paragraphs run together, and they show up as noise in git diffs. Every line ending is normalised to `\n` up front.

A UTF-8 byte-order mark is the other invisible hazard. A BOM at the start of a file becomes the first character of the first line, which means a heading like `# Title` is no longer recognised as a heading because the `#` is not at the start of the line any more. Notepad still writes BOMs; the converter strips a leading one.

The file is decoded as UTF-8 by the browser's own File API, so any Unicode content — accented characters, CJK text, emoji, mathematical symbols — passes through unchanged, and the output is UTF-8 Markdown. A legacy file saved in a single-byte codepage such as Windows-1252 may show replacement characters where its high-range bytes were; re-save it as UTF-8 in any editor before converting. There is no server-side locale involved to guess wrong, because there is no server: the text is read into your tab, transformed in memory, and never sent anywhere.

Escaping, edge cases and when to intervene by hand

One category of surprise is worth knowing about in advance: plain text can contain characters that Markdown treats as syntax. An underscore inside a variable name like `MAX_BUFFER_SIZE`, an asterisk used for emphasis-by-convention, a `#` starting a comment line, a `|` in an ASCII table. The converter does not escape these, and that is a considered choice — escaping them would fill your text with backslashes, and most of the time they render harmlessly. If a particular file is full of snippets, wrap them in a code fence before or after converting; inside a fence nothing is interpreted.

ASCII art tables are the clearest case where no automated conversion helps. A grid drawn with `+---+---+` and pipes is a picture of a table, not a table, and inferring column boundaries from character positions is guesswork that fails on any misaligned row. Put such a block in a fenced code block to keep it readable, or rebuild it as a GFM table if the data matters. Likewise, hard-wrapped paragraphs — text broken at column 72 — stay hard-wrapped, because unwrapping them requires deciding which line breaks were meaningful, and getting that wrong destroys addresses, poetry and code alike.

Everything runs locally, so iterating is free: convert, look at the output, tweak the source, convert again. Nothing is stored, nothing is uploaded, and there is no rate limit — which makes this workable for a batch of notes you are cleaning up one file at a time, and safe for text you would never paste into an online tool in the first place.

Known limitations of TXT to Markdown conversion

Being explicit about what a converter cannot do saves you a wasted upload. These are the boundaries of what is recoverable from TXT programmatically:

  • Headings are only detected from setext underlines (`===` / `---`). A heading written as a bare line of text stays a paragraph — promote it with `#` yourself.
  • ASCII-art tables are not converted to GFM tables. Column boundaries cannot be inferred reliably; wrap them in a code fence instead.
  • Hard-wrapped paragraphs are not reflowed, because deciding which line breaks were intentional is not safe to automate.
  • Markdown special characters in your text (`_`, `*`, `#`, `|`, backticks) are not escaped, so text that uses them literally may render as formatting.
  • Ordered lists keep their original numbers rather than being renumbered, so a mis-numbered source list stays mis-numbered.
  • Files saved in a legacy single-byte codepage may decode with replacement characters. Re-save as UTF-8 first.

Who Converts TXT to Markdown?

  • Obsidian / Notion writer

    Imports years of loose .txt notes into a vault and wants real lists and links without re-typing each file.

  • Developer

    Turns a scratch file, changelog or terminal transcript into a README section with fenced code blocks already in place.

  • Technical writer

    Migrates legacy release notes and INSTALL files into a docs-as-code site without altering the approved wording.

  • Researcher

    Normalises exported notes and logs into consistent Markdown for a searchable, greppable archive.

How You Can Verify the Privacy Claim

Browser-only

Zero server upload

Conversion runs inside your browser tab. Open DevTools, switch to the Network panel, and convert a file: for every format except URL to Markdown you will see no request carrying your document — because there is no endpoint to send it to.

Web Worker

Off the main thread

Heavy parsing is dispatched to a Web Worker, so a 500-page PDF or a large spreadsheet never freezes the interface. Everything is plain JavaScript — no native plugin, no WebAssembly toolchain, nothing to install.

No account

Nothing to sign up for

No login, no quota, no paywall, and no tracking tied to your files. Analytics are cookieless and aggregate only. Read the privacy policy for the full data-flow breakdown, including the one proxied exception.

The parsers doing the work

No proprietary black box: each format is handled by a widely audited open-source library, running client-side at the version pinned in our lockfile.

TXT to Markdown — FAQ

Why are my headings not detected?

Only setext-style headings are promoted: a line of text with === or --- directly underneath it becomes an H1 or H2. A bare line that looks like a title carries no signal a parser can trust — inferring from length, casing or trailing punctuation is wrong often enough that you would end up checking every heading in the output. Add the # prefixes for those, or add underlines to the source and re-convert.

How are indented blocks and code samples handled?

A run of lines indented with a tab or four or more spaces is dedented and wrapped in a ``` fence. Two guards prevent false positives: the run must be preceded by a blank line, and it must contain at least two indented lines. That is what keeps an indented list continuation line — which belongs to its bullet — from being turned into a code block.

Will my text be reflowed or reworded?

No. Wording, punctuation and line breaks inside paragraphs are preserved exactly. Hard-wrapped text stays hard-wrapped, because deciding which of those breaks were meaningful is not safe to automate — get it wrong and you destroy addresses, verse and code listings. The only whitespace change is collapsing runs of three or more blank lines to one.

Is this good enough for an Obsidian or RAG import?

Yes, and for both reasons the conservatism helps. Obsidian reads GFM, so the normalised - bullets, ATX headings and autolinks render natively and wikilinks you already typed are left untouched. For retrieval pipelines, the promoted headings and fenced blocks give a chunker real boundaries to split on instead of a wall of undifferentiated text, while the prose stays byte-identical to the source.

What about non-UTF-8 files and Windows line endings?

CRLF and classic Mac CR endings are normalised to LF, and a leading UTF-8 BOM is stripped — an invisible BOM otherwise pushes the # off the start of line one and breaks the first heading. The text is decoded as UTF-8 by the browser, so accented, CJK and emoji content passes through unchanged. A legacy file saved in a single-byte codepage such as Windows-1252 may show replacement characters; re-save it as UTF-8 first.

In-Depth TXT Guides