HTML to Text Converter
Turn HTML into readable plain text with block structure preserved and entities decoded — entirely on this device.
Choose whether a link's address is printed, and where. URLs are printed exactly as written — relative links are not resolved, because this page never knows your base URL. decodes to a non-breaking space (U+00A0), not a plain space. 0 turns wrapping off; 20–200 characters otherwise.
This is not a browser. It reads your markup, not a rendered page: CSS is never applied, so text hidden with `display:none` still comes out, and layout, fonts and generated content are not modelled. Removed and counted: script, style, head, template, noscript, svg, iframe contents, comments, and elements marked `hidden`.
Press ? for shortcuts
Do it offline:$ textutil -convert txt -stdout page.html
How to use it
- Paste or type markup into the left pane — a whole saved page, a fragment, or the body of an email template.
- Pick how links, images, tables and non-breaking spaces should land, and set a wrap width when you need fixed columns.
- Read the plain text on the right; the chips count the blocks emitted, the references decoded and everything discarded, by kind.
- Copy the result, save it as a .txt file, or share the permalink — the state rides in the fragment, which is never sent to a server.
Paste a page's markup and read what a person would actually see: headings, paragraphs, list items and table rows, with the tags gone and character references decoded. Scripts, styles, comments, SVG and iframe subtrees are removed and counted, never silently swallowed. Links can stay as bare text, carry their address inline, or collect into a numbered tail. Conversion runs on this device.
Examples
<article>
<h1>Release notes</h1>
<p>The tokenizer now follows <a href="https://spec.whatwg.org/">the spec</a> more closely.</p>
<ul>
<li>Rows keep their order</li>
<li>Lists nest:
<ol start="3">
<li>third</li>
<li>fourth</li>
</ol>
</li>
</ul>
<p>Everything else is in <a href="https://spec.whatwg.org/">the spec</a>.</p>
</article>
Release notes
The tokenizer now follows the spec more closely.
- Rows keep their order
- Lists nest:
3. third
4. fourth
Everything else is in the spec.Seven blocks come out: heading, paragraph, four list items and a closing paragraph. Sibling items sit on consecutive lines while the paragraphs are separated by a blank one, the nested list indents two spaces, and the ordered list starts at three because its start attribute says so. The default link mode keeps the anchor text and drops the address.
<article>
<h1>Release notes</h1>
<p>The tokenizer now follows <a href="https://spec.whatwg.org/">the spec</a> more closely.</p>
<ul>
<li>Rows keep their order</li>
<li>Lists nest:
<ol start="3">
<li>third</li>
<li>fourth</li>
</ol>
</li>
</ul>
<p>Everything else is in <a href="https://spec.whatwg.org/">the spec</a>.</p>
</article>
Release notes
The tokenizer now follows the spec [1] more closely.
- Rows keep their order
- Lists nest:
3. third
4. fourth
Everything else is in the spec [1].
[1] https://spec.whatwg.org/The same document in reference mode. Both anchors point at one address, so both are marked [1] and the tail lists it once — reference numbers are assigned per address, not per anchor, and run 1..N with no gaps. The tail is an extra block, which is why the block count rises by one.
<pre>
cols aligned
stay put</pre>
<script>if (a < b) document.write("</p>");</script>
<div><em>10 km &fooby; left</div>
cols aligned
stay put
10 km &fooby; leftFour hazards at once. The preformatted block keeps its runs and its leading indentation, minus the one newline that follows the start tag. The script body is raw text, so its </p> never opens or closes anything — the element is removed and counted instead. decodes to U+00A0, which is not whitespace, so it survives collapsing; &fooby; is left exactly as written with a warning. The end tag that closes a div over an open em is reported, and the text still comes out.
Every example on this page is executed against the same engine that powers the tool, on every build.
Gotchas worth knowing
Text hidden by CSS still comes out
No stylesheet is consulted, so a paragraph switched off with display:none is printed like any other. The hidden attribute is the opposite case: it lives in the markup, so those elements go — removed and counted with the rest.
Whitespace collapses except where the markup forbids it
Runs of spaces, tabs and newlines between tags become a single space, the collapse the HTML white-space rules describe. Inside pre and textarea the bytes survive untouched, indentation included, and the wrap width never reaches them.
Addresses are printed, never resolved
A relative href such as /about appears exactly as written, because this page has no base address to resolve it against. Table cells are just as literal: colspan and rowspan are not modelled, so each cell prints once, in source order.
Common errors
Input is larger than the 4 MB limit for this tool. Convert the file on your own machine instead — on macOS: textutil -convert txt -stdout page.html- Size is the only refusal a conversion can have — every other document produces text. Nothing is truncated; the banner hands you the local command instead.
`12` is not a wrap width — use 0 to turn wrapping off, or a whole number from 20 to 200.- Options arrive from a shared permalink as well as from the controls, so an out-of-range value is refused rather than quietly rounded into something you did not ask for.
`</div>` closes an element with `<em>` still open at line 5, column 33 — `<em>` is treated as ending here — no bytes are moved.- A tag mismatch is a warning, not a failure: the position points into your original document, the inner element is treated as ending there, and the text is produced anyway.
`&fooby;` at position 115 is not a known reference — left unchanged- References are decoded against the WHATWG named-character-reference list, and anything absent from it is left verbatim. The position counts characters in the document you pasted.
`colspan` / `rowspan` are not modelled — each cell is printed once, in source order.- Plain text has no column model to spread a merged cell across, so the note appears whenever a table would need one.
FAQ
Which parts of the markup never reach the output?
Script and style bodies, the head element and its title, template and noscript contents, svg and iframe subtrees, comments, and anything carrying the hidden attribute. Each kind is counted and named in a chip, so a page that comes out short tells you why. Fallback text inside video and audio is kept.
Why is a non-breaking space kept instead of a plain space?
Because U+00A0 is a character, not whitespace: it is what stops a line breaking between a number and its unit, and collapsing it would change the text you pasted. Tools that need ordinary spaces — a diff, a word count, a CSV field — usually want the other behaviour, so there is a switch for it and a chip that says when it fired.
Will the output match what I see in a browser?
For structure, closely: block boundaries, list markers and cell order follow the HTML spec's rules for block elements, optional end tags and raw-text elements — this reads markup, it does not render a page. For presentation, no — fonts, layout, generated content from CSS and any text a script would have written are outside what markup alone can tell us, and none of them are modelled here.
Keyboard shortcuts
- ⌘/Ctrl + Enter
- Convert now instead of waiting for the debounce.
- ⌘/Ctrl + S
- Save the plain text as a .txt file.
- ⌘/Ctrl + C
- With an empty selection, copy the whole text pane.
- ⌘/Ctrl + Shift + C
- Copy the whole text pane even mid-selection.
- t
- Move to the next theme (idle while you are typing).
- ? or ⌘/Ctrl + /
- Open the list of bindings for this page.
- Esc
- Close the frontmost overlay; the pasted markup stays.