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 &, 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 <...>.
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 & 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 & 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);
htmlholds characters that represent HTML markup.document.createElement("template")creates a detached container. Its contents are not displayed or activated as part of the page.- Assigning
htmltotemplate.innerHTMLmakes the browser parse the markup. - The parsed result contains a paragraph text node (
Hello), astrongelement with text (world), and another text node (!). template.content.textContentcollects the text from all nested text nodes and ignores the element tags.plainTextbecomes"Hello world!".
For this input:
const html = "<div>Tom & <em>Sam</em></div>";
The result is "Tom & Sam", because the HTML parser decodes &.
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
.txtfiles. - 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;
}
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
| Approach | Best for | Advantages | Limitations |
|---|---|---|---|
<template> + textContent | Browser plain-text extraction | Built in, handles nested HTML and entities | Does not preserve visual line breaks by itself |
DOMParser + textContent | Parsing a complete HTML document string | Built in and explicit about document parsing | More verbose for a small HTML fragment |
| Regular expression replacement | Strictly controlled, trivial markup only | Short code | Not reliable for general HTML; does not decode entities |
innerText | Text as visually rendered in the page | Can reflect layout-related line breaks | Depends 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
textContentreads or writes plain text.innerHTMLreads 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"
Cheat Sheet
function htmlToText(html) {
const template = document.createElement("template");
template.innerHTML = html;
return template.content.textContent || "";
}
- Use
textContentto extract text from parsed HTML. - Use a detached
<template>for HTML fragments in browsers. &,<, and similar entities are decoded by the HTML parser.- Expect adjacent block elements to run together unless you add newline rules.
- Use
element.textContent = valueto display plain text. - Do not rely on
/<[^>]*>/gfor 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 & and other HTML entities?
Yes. When the browser parses &, 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.
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.
Keep learning
Related questions
Add a Favicon to a Static HTML Page
Learn how to add a favicon to static HTML pages, use correct paths and icon formats, and troubleshoot browser caching and local-file issues.
Allow Only Numeric Input (0-9) in HTML Input Using jQuery
Learn how to allow only digits 0-9 in an HTML input using jQuery, with examples, validation tips, common mistakes, and best practices.
Apply CSS !important with jQuery .css()
Learn why jQuery .css() cannot set !important and how to use CSSStyleDeclaration.setProperty() to apply inline important styles safely.