Markdown to Text: Strip Formatting to Plain Text

September 11, 2026 · 11 min read

Markdown to Text: Strip Formatting to Plain Text Cleanly

Converting markdown to text means removing the symbols (#, **, -, []()) while keeping the words in a readable order. You can do it in seconds with pandoc, a Python one-liner, an npm package, or by pasting into a converter. This tutorial gives you the decision table, three tested code paths, and the fix for messy AI-generated answers.

What Should Each Element Become in Plain Text?

There's no standard for stripping Markdown, which is why every tool gives a slightly different result. Before you pick a tool, decide what you want each element to turn into. This is the table we use, and it matches what most readers expect when they paste into an email or a form.

Markdown elementPlain text resultWhy
# HeadingThe heading text on its own lineReaders still need the structure
**bold**, *italic*, ~~strike~~The word with no markersEmphasis has no plain-text form
- item or 1. itemBullet or number kept, or dropped for excerptsDepends on whether the list must stay scannable
[text](url)text alone, or text (url)Keep the URL when the reader needs to click
![alt](image.png)The alt textThe image itself can't travel
TableTab-separated rows or aligned columnsPipes are noise without a renderer
Fenced code blockThe code with the fences removedThe content matters, the fence doesn't
\*escaped\**escaped* with the backslash removedThe backslash only exists for the parser

The last row trips people up. CommonMark says any ASCII punctuation character can be backslash-escaped, so a good stripper removes the backslash and keeps the character. A regex-based stripper usually removes both, which corrupts text like 2 \* 3. See the backslash escapes section of the spec for the full list of escapable characters.

Hard line breaks are the other quiet loss. Two trailing spaces or a backslash at the end of a line mean "new line here" in Markdown. Most strippers keep the newline but drop the backslash, which is what you want. Our line break guide explains the two forms if you're unsure which one your source uses.

How Can I Convert Markdown to Text Without Installing Anything?

Paste your Markdown into the editor below and read the preview. The rendered view is already the "plain" version of your document in the sense that matters for copying: select the preview, copy, and paste into a plain-text field. Every browser drops formatting when the destination is a textarea, so headings, bold, and links arrive as bare words.

If the destination accepts rich text (Gmail, Google Docs, Notion, Slack), the same copy keeps the headings and bold as real formatting instead of asterisks. That's the second use case for this conversion, and it runs through HTML. Our Markdown to HTML guide covers that route in detail, and the Markdown to HTML tool gives you the raw HTML if you need it.

Summary

Here is what changed in version 2.1:

  1. Install the update
  2. Restart the app
Feature Status
Export Done

Escaped *asterisks* stay literal.

42 words268 characters15 lines
Markdown

Try selecting the preview on the right and pasting it into a plain .txt file. You'll get the heading, the sentence with the markers gone, the list items, and the table cells separated by tabs in most browsers.

Convert Markdown to Text with pandoc

Pandoc's plain writer is the most predictable command-line option, and it's the one we prefer for batch jobs. Install pandoc, then run:

pandoc input.md -t plain --wrap=none -o output.txt

-t plain selects the plain text writer. --wrap=none stops pandoc from re-wrapping lines at 72 characters, which is the default and surprises people who paste the output into a form. The pandoc manual lists auto, none, and preserve as the three wrap modes.

We ran the sample from the editor above through pandoc 3.8.2.1. Here's what came out:

Summary

Here is what changed in version 2.1:

- Faster export (about 3x)
- A new changelog page

1.  Install the update
2.  Restart the app

  Feature   Status
  --------- --------
  Export    Done

Escaped *asterisks* stay literal.

Three things to notice. Links lose their URL by default. Adding --reference-links makes the plain writer append each URL as a reference definition at the end of the file ([changelog page]: https://example.com/changelog), which is enough for a record but not for inline text (url); use the Node option below for that. The table becomes an aligned column layout with a dashed underline, which reads well but isn't tab-separated. And the escaped asterisks come back as literal asterisks, exactly as the decision table says they should.

Pandoc treats the input as Pandoc Markdown by default. If your source uses GitHub tables and task lists, pass -f gfm so the reader recognises them.

Strip Markdown in Python or Node

Both ecosystems have a parse-first option and a quick regex option. Parse first when the text matters, and reach for the regex option only for preview snippets.

Python: markdown plus BeautifulSoup

The classic Stack Overflow answer converts Markdown to HTML with the markdown package and then extracts the text with BeautifulSoup. It works because the HTML step resolves every edge case (nested emphasis, escapes, reference links) that a regex would get wrong.

import re
import markdown
from bs4 import BeautifulSoup

html = markdown.markdown(source, extensions=["tables", "fenced_code"])
text = BeautifulSoup(html, "html.parser").get_text()
text = re.sub(r"\n{3,}", "\n\n", text).strip()

We tested this with Markdown 3.10.3 and Beautiful Soup 4.15.0. The get_text() call returns all the text in the document as one string, and the regex collapses the runs of blank lines that HTML block elements leave behind. Without the tables extension, a GitHub-style table comes out as one paragraph of pipes, so add it whenever the source came from GitHub or an AI assistant.

One limitation: get_text() drops list markers. Each item lands on its own line, but the dashes and numbers are gone. If you need them, walk the li elements yourself and prefix each one.

Node: remove-markdown or strip-markdown

Two npm packages cover the JavaScript side, and they take opposite approaches.

remove-markdown is regex-based, has zero dependencies, and is the quick pick for excerpts. Version 0.7.0 was published on 31 August 2026. Its options map almost one to one onto the decision table:

import removeMd from "remove-markdown";

const text = removeMd(source, {
  stripListLeaders: true,   // default: drop "- " and "1. "
  useImgAltText: true,      // default: images become alt text
  replaceLinksWithURL: false, // true keeps the URL instead of the text
  separateLinksAndTexts: " ", // "text url" when you want both
});

Running it on the sample above gave clean headings, list items, and links, with two rough edges. The table came through with its pipes intact, and \*asterisks\* lost the asterisks rather than the backslashes. That's the regex trade-off. It's fast and dependency-free, but it doesn't parse.

strip-markdown is a remark plugin, so it parses the document into a syntax tree first and then discards the formatting nodes. Its README says it removes code, HTML, horizontal rules, tables, and YAML entirely, keeps alt text for images, and renders everything else as plain paragraphs. The last release, 6.0.0, dates from October 2023, but it depends on the actively maintained remark stack.

import { remark } from "remark";
import strip from "strip-markdown";

const file = await remark().use(strip).process(source);
console.log(String(file));

Pick strip-markdown when correctness matters and remove-markdown when you need a 3 KB function for preview snippets. Note that strip-markdown deletes tables and code blocks with their content, so it's the wrong choice if those hold the information you're keeping.

Cleaning Up AI-Generated Markdown

Most people searching for a Markdown to text converter in 2026 are cleaning up an answer from ChatGPT, Claude, or Gemini. Those assistants reply in Markdown because it's compact and the chat interface renders it. Paste the same answer into an email, a support ticket, or a job application form and you get a wall of ### and **. Our post on why AI tools use Markdown explains where that habit comes from.

You have two options, and the right one depends on the destination.

For a plain-text field (a form, an SMS, a code comment, a subject line): strip. Use any of the methods above, or paste into the editor and copy the preview into the field.

For a rich-text destination (Gmail, Outlook, Google Docs, Word): convert instead of strip. Render the Markdown, copy the preview, and paste. The headings become real headings and the bullets become real bullets. If the destination is Word, the Markdown to DOCX tool gives you a file with proper styles, and the Markdown to Word guide covers the details.

In our experience the strip option is chosen too often. A bold phrase that survives as a bold phrase in Gmail reads better than the same phrase flattened to nothing.

Is a Markdown File Just a Text File?

Yes. A .md file contains nothing but characters. The CommonMark spec describes a document as any sequence of Unicode code points, and it doesn't even specify an encoding, though UTF-8 is what every editor and platform assumes. There's no binary header, no metadata, no hidden formatting.

That has three practical consequences. Renaming notes.md to notes.txt changes nothing except which program opens it by default. Any text editor (Notepad, TextEdit, VS Code, vim) opens a Markdown file and shows you the raw syntax. And "converting Markdown to text" is really a choice about whether to remove the syntax characters, because the file was already text.

If you're new to the format, What Is Markdown? walks through what those syntax characters mean and why they were designed to be readable even when they aren't rendered.

Common Markdown to Text Mistakes

Stripping with a naive regex. A pattern like s/\*//g removes every asterisk, including the ones in 2 * 3 and inside code blocks. Parse first (pandoc, Python markdown, remark) or use a library that handles the edge cases.

Forgetting the table extension. Python's markdown package doesn't know GFM tables by default. Without extensions=["tables"] your table becomes a single line of pipe characters. Pandoc has the same issue in reverse: use -f gfm for GitHub-style input.

Letting pandoc re-wrap your lines. The default --wrap=auto inserts line breaks every 72 characters. Pasted into a chat app or a form, those breaks look like deliberate paragraph splits. Always pass --wrap=none for text that will be pasted somewhere.

Markdown to Text FAQ

Converting markdown to text comes down to one decision per element. Keep the words, drop the markers, and choose what happens to links, images, and tables. Pandoc's plain writer, the Python markdown plus BeautifulSoup pair, and the strip-markdown package all parse before they strip, which is what keeps escaped characters intact. For a one-off answer from an AI assistant, paste it into the editor and copy the preview.