Question

Strip HTML Tags from a String with Plain JavaScript

htmljavascriptstring

Question

How can HTML tags be removed from a string using plain JavaScript, without using an external library? For example, how can you convert this HTML string into readable text?

const html = "<p>Hello <strong>world</strong>!</p>";

The desired result is:

"Hello world!"

Short Answer

You will learn the difference between removing markup and safely extracting text from HTML. You will use built-in browser APIs to convert an HTML string into plain text, understand why regular expressions are limited for HTML, and choose the right approach for browser or server-side code.

Concept

HTML is made of elements (such as <p> and <strong>) and text nodes (such as Hello and world). When people say they want to “strip HTML tags,” they usually want the text contained inside those elements.

In browser JavaScript, the most reliable plain-JavaScript approach is to let the browser parse the string as HTML and then read its text with textContent.

const template = document.createElement("template");
template.innerHTML = "<p>Hello <strong>world</strong>!</p>";

const text = template.content.textContent;
console.log(text); // "Hello world!"

This is better than trying to describe HTML with a regular expression. HTML can contain nested elements, attributes with > characters, comments, entities such as &amp;, and malformed markup. A real HTML parser already knows how to handle these cases.

This technique is useful when displaying a plain-text preview, building search indexes, exporting text, or reading user-generated rich-text content. It is not a complete security solution: extracting text is safe for a text-only result, but inserting untrusted HTML into a live page requires careful sanitization.

Mental Model

Think of an HTML string as a document in a sealed envelope.

  • The tags are formatting instructions: “this is a paragraph,” “make this word bold.”
  • The text nodes are the actual message.
  • A browser HTML parser opens the envelope, understands the instructions, and lets you copy only the message.

Using textContent means asking: “What text is inside this HTML?” rather than manually cutting out anything that looks like <...>.

Take Quiz

Syntax and Examples

Use a detached <template> element, assign the HTML string to innerHTML, then read template.content.textContent.

function htmlToText(html) {
  const template = document.createElement("template");
  template.innerHTML = html;
  return template.content.textContent || "";
}

const html = "<h1>News &amp; Updates</h1><p>Read <a href='/post'>more</a>.</p>";

console.log(htmlToText(html));
// "News & UpdatesRead more."

textContent returns all text nodes. It also decodes normal HTML character references such as &amp; into &.

Adding readable line breaks

HTML block elements do not automatically become line breaks when read with textContent. If your input has predictable tags, convert the tags that represent breaks before extracting text:

function htmlToReadableText(html) {
  const template = document.createElement("template");

  template.innerHTML = html
    .replace(/<br\s*\/?\s*>/gi, "\n")
    .replace(/<\/p\s*>/gi, "\n");

  return (template.content.textContent || "")
    .replace(/\n{2,}/g, "\n")
    .trim();
}

const html = "<p>First line<br>Still first paragraph</p><p>Second paragraph</p>";
console.log(htmlToReadableText(html));
// First line
// Still first paragraph
// Second paragraph

Only add these replacements when your output needs formatting. For simple text extraction, use textContent directly.

Step by Step Execution

Consider this example:

const html = "<p>Hello <strong>world</strong>!</p>";

const template = document.createElement("template");
template.innerHTML = html;

const plainText = template.content.textContent || "";
console.log(plainText);
  1. html holds characters that represent HTML markup.
  2. document.createElement("template") creates a detached container. Its contents are not displayed or activated as part of the page.
  3. Assigning html to template.innerHTML makes the browser parse the markup.
  4. The parsed result contains a paragraph text node (Hello ), a strong element with text (world), and another text node (!).
  5. template.content.textContent collects the text from all nested text nodes and ignores the element tags.
  6. plainText becomes "Hello world!".

For this input:

const html = "<div>Tom &amp; <em>Sam</em></div>";

The result is "Tom & Sam", because the HTML parser decodes &amp;.

Real World Use Cases

  • Search indexing: Extract text from saved rich-text articles before searching their words.
  • Previews: Show the first part of an HTML blog post in a card or notification.
  • Exports: Convert formatted notes into plain-text email, logs, or .txt files.
  • API cleanup: Read the text from an API field that contains HTML markup.
  • Accessibility summaries: Generate short text-only descriptions from formatted content.
  • Data migration: Convert old HTML-based content into plain text before moving it to another system.

For example, a preview can be created by extracting text and then limiting its length:

function createPreview(html, maxLength = 120) {
  const template = document.createElement("template");
  template.innerHTML = html;

  const text = (template.content.textContent || "")
    .replace(/\s+/g, " ")
    .trim();

  return text.length > maxLength ? `${text.slice(0, maxLength)}…` : text;
}
Take Quiz

Real Codebase Usage

In real projects, developers usually put HTML-to-text conversion in one reusable utility function. That keeps behavior consistent across previews, search, exports, and analytics.

Reusable browser utility

export function htmlToText(html) {
  if (typeof html !== "string") {
    return "";
  }

  const template = document.createElement("template");
  template.innerHTML = html;

  return (template.content.textContent || "")
    .replace(/\s+/g, " ")
    .trim();
}

This uses a guard clause: if the input is not a string, the function returns an empty string early instead of failing.

Important security boundary

// Good: use extracted text as text.
previewElement.textContent = htmlToText(userHtml);

Avoid treating tag removal as HTML sanitization:

// Risky if userHtml is untrusted.
previewElement.innerHTML = userHtml;

If an application must render untrusted HTML as HTML, it needs a deliberate sanitization policy. Extracting textContent is appropriate when the desired output is plain text, not rendered rich text.

Server-side consideration

document is a browser API. Plain Node.js does not provide a DOM by default, so the <template> approach cannot run there without a DOM implementation or a server-side HTML parsing solution. Keep browser-only code out of backend modules.

Common Mistakes

Using a regular expression as a general HTML parser

This common shortcut works only for very simple, controlled strings:

const text = html.replace(/<[^>]*>/g, "");

It can fail when > appears inside an attribute value:

const html = '<p title="1 > 0">Hello</p>';
const text = html.replace(/<[^>]*>/g, "");

console.log(text); // ' 0">Hello' — incorrect

Use the browser parser and textContent for HTML input.

Expecting textContent to preserve visual layout

const html = "<p>One</p><p>Two</p>";

textContent may produce "OneTwo", because paragraphs are elements, not literal newline characters. Add explicit line-break handling if the output needs readable paragraphs.

Using innerHTML to display the result

// Unnecessary and potentially unsafe for untrusted input.
element.innerHTML = htmlToText(userHtml);

Use textContent when displaying plain text:

element.textContent = htmlToText(userHtml);

Forgetting that document is browser-only

Code using document.createElement() will fail in a standard Node.js environment. Use a server-compatible HTML parser when processing HTML on the backend.

Assuming tag removal sanitizes HTML

Removing tags is not a replacement for sanitizing content that will later be inserted with innerHTML. Decide whether you need plain text extraction or safe HTML rendering; they are different tasks.

Comparisons

ApproachBest forAdvantagesLimitations
<template> + textContentBrowser plain-text extractionBuilt in, handles nested HTML and entitiesDoes not preserve visual line breaks by itself
DOMParser + textContentParsing a complete HTML document stringBuilt in and explicit about document parsingMore verbose for a small HTML fragment
Regular expression replacementStrictly controlled, trivial markup onlyShort codeNot reliable for general HTML; does not decode entities
innerTextText as visually rendered in the pageCan reflect layout-related line breaksDepends on rendering and is unnecessary for detached parsing

<template> versus DOMParser

Both use the browser's HTML parser:

const parser = new DOMParser();
const documentNode = parser.parseFromString("<p>Hello <b>there</b></p>", "text/html");
console.log(documentNode.body.textContent); // "Hello there"

For a small HTML fragment, <template> is concise. DOMParser is useful when you want to work with a parsed document structure, such as documentNode.body or queried elements.

textContent versus innerHTML

  • textContent reads or writes plain text.
  • innerHTML reads or writes HTML markup.
const element = document.createElement("div");
element.innerHTML = "<strong>Hi</strong>";

console.log(element.innerHTML);   // "<strong>Hi</strong>"
console.log(element.textContent); // "Hi"
Take Quiz

Cheat Sheet

function htmlToText(html) {
  const template = document.createElement("template");
  template.innerHTML = html;
  return template.content.textContent || "";
}
  • Use textContent to extract text from parsed HTML.
  • Use a detached <template> for HTML fragments in browsers.
  • &amp;, &lt;, and similar entities are decoded by the HTML parser.
  • Expect adjacent block elements to run together unless you add newline rules.
  • Use element.textContent = value to display plain text.
  • Do not rely on /<[^>]*>/g for arbitrary HTML.
  • Do not use tag stripping as a substitute for sanitizing HTML you plan to render.
  • document.createElement() requires a browser DOM; it is not available in plain Node.js.

FAQ

How do I remove HTML tags in JavaScript without a library?

Create a <template> element, assign the string to template.innerHTML, and read template.content.textContent.

Is using a regular expression to remove HTML tags safe?

It is not reliable for general HTML. It can break on valid attributes containing > and does not properly handle all HTML syntax. Use an HTML parser instead.

Does textContent decode &amp; and other HTML entities?

Yes. When the browser parses &amp;, textContent returns &.

Why are paragraphs combined without spaces or newlines?

textContent extracts text nodes; it does not reproduce CSS layout. Convert selected tags such as <br> and </p> to newline characters first if formatting matters.

Should I use innerText or textContent?

Use textContent for predictable text extraction. innerText is affected by rendering and styles, which is usually not needed when processing an HTML string.

Can this code run in Node.js?

Not by itself. Standard Node.js does not include document or browser DOM APIs. Use a server-side HTML parsing solution when processing HTML on the server.

Does extracting text make unsafe HTML safe to render?

It is safe when you keep the result as text, for example by assigning it to textContent. It does not sanitize the original HTML for later use with innerHTML.

Take Quiz

Mini Project

Description

Build a browser utility that turns HTML snippets into clean plain-text previews. This resembles the preview text shown for articles, comments, or rich-text messages in many applications.

Goal

Create a function that extracts readable text, preserves basic line breaks, normalizes whitespace, and shortens long content with an ellipsis.

Requirements

Requirements must be supplied as an array, not a Markdown string.

Take Quiz

Keep learning

Related questions

View more