Skip to main content
Back to BlogText Guides

How to Remove Whitespace from Text (and Why a Space Is Not Always a Space)

Remove extra spaces, tabs, empty lines, and line breaks from text — and learn why there are six kinds of whitespace, why the non-breaking space (U+00A0) survives .trim(), why code mode preserves indentation, and why the pipeline order decides what survives.

The Toolbox TeamAugust 14, 20267 min read

The problem: "space" is not one character

You pasted text from a Word document, a Google Doc, or a web page, and something's off. The spaces look normal but your parser splits on the wrong boundary. Your .trim() left a phantom space at the start. Your line count is triple what it should be because of blank lines. The reason is that "space" is not one character — it's at least six, and the one you can't see is the one that's breaking your code. The tool exposes ten toggles because whitespace is a category, not a character, and the wrong toggle either does nothing or eats the spaces you wanted to keep.

Fastest path

Open the Whitespace Remover, paste the text, pick a preset, read the output.

Input:   "  This    text  has   extra   spaces.\n\n\n  And  blank  lines.  "
Preset:  Clean Text
Output:  "This text has extra spaces.\nAnd blank lines."

The "Clean Text" preset collapses runs of spaces to one, removes tabs, removes empty lines, converts non-breaking spaces to regular spaces, and trims each line. That covers 90% of cleanup jobs. The other 10% — code, minification, line-ending normalization — is what the rest of this guide is about.

The substance: six kinds of whitespace, ten toggles, one pipeline

The six kinds of whitespace

Character Code What it is
Space U+0020 The normal one
Tab U+0009 One character, displays as N columns
Line feed (LF) U+000A Unix newline
Carriage return (CR) U+000D Old Mac newline; Windows uses CR+LF
Non-breaking space U+00A0 Looks like a space, isn't matched by \s the same way
Zero-width space U+200B Invisible, breaks words in some parsers

The tool handles the first five directly. Zero-width spaces (U+200B) aren't in the toggles — they're rarer and usually come from Arabic/Thai/Chinese text processing — but the "Remove ALL spaces" toggle won't catch them either because it targets [ \t]. If you're debugging a phantom break in non-Latin text, check for U+200B with the visual mode.

The non-breaking space trap

U+00A0 is the one that bites. It looks identical to a regular space in every editor, but:

  • JavaScript's .trim() strips it in modern engines, but older regex like /^ / does not match it.
  • Python's .strip() does not strip U+00A0 by default (it strips \s, and Python's \s does include it — but str.split(' ') with a literal space does not split on it).
  • HTML renders it as a space that prevents line wrapping at that point — which is why it's there in the first place (copy from a web page, and the original author used  ).

The tool's "Remove non-breaking spaces" toggle converts U+00A0 → U+0020 before any other space operation, so the collapse and trim steps then treat them as regular spaces. Turn it on unless you specifically need to preserve nbsp semantics.

Code mode: the one toggle that saves your indentation

Code mode is the critical toggle for developers. In normal mode, "Remove leading spaces" strips the indentation from every line — fine for prose, catastrophic for Python or YAML, where indentation is the syntax. Code mode does one thing: strips trailing whitespace from each line (line.replace(/[ \t]+$/, '')) and leaves leading whitespace untouched. It still applies line-ending normalization and newline collapse, but it will not touch the indent.

This is the difference between a clean commit and a commit that breaks the build. If you're cleaning up a source file, turn code mode on, leave "Remove leading spaces" off, and let it strip the trailing whitespace that linters complain about.

The indent conversion

Tabs ↔ spaces, with a configurable tab width (default 4). The conversion runs before other space operations, and the spaces-to-tabs regex (^( {4})+ with the m flag) only matches leading whitespace — spaces in the middle of a line are left alone. This is correct: you want if (x) to become \tif (x), but you don't want foo bar baz to become foo\tbar\tbaz.

The tab width matters for the round-trip. If your project uses 2-space indents (Prettier, Ruby), set tab width to 2 before converting tabs to spaces, or the indent will be twice as wide as intended.

The line-ending question: LF, CRLF, CR

The tool normalizes all input to LF internally, then converts to your chosen output:

  • LF (U+000A) — Unix, macOS, modern Linux, most of the web.
  • CRLF (U+000D + U+000A) — Windows. Git's core.autocrlf exists because of this.
  • CR (U+000D) — Classic Mac OS (pre-OSX). You'll see it only in very old files.

The "Do not change" option keeps LF (the internal normal form) — it doesn't preserve the original mix, because the input was already normalized in step 1. If you need to detect what the input was, the stats panel shows the LF/CRLF/CR breakdown of the original.

The pipeline order (why it matters)

The tool processes in a fixed sequence, and the order is not arbitrary:

  1. Normalize line endings to LF (so the rest of the pipeline works on one newline type)
  2. Convert non-breaking spaces → regular spaces (so they're caught by the space collapse in step 7)
  3. Indent conversion (before tab removal, so tabs→spaces survives and spaces→tabs isn't undone)
  4. Code mode: strip trailing per line, skip to step 8
  5. Tab removal (only if not in code mode)
  6. Per-line trim / leading / trailing removal
  7. Space collapse or remove-all
  8. Collapse 3+ newlines to 2
  9. Remove empty lines
  10. Remove all line breaks (flatten to one line)
  11. Line-ending output conversion
  12. Final .trim()

The order explains why "Remove non-breaking spaces" + "Collapse extra spaces" works: nbsp → space happens in step 2, the collapse happens in step 7, and by then the former nbsp is a regular space that the collapse catches. If nbsp conversion ran after the collapse, the nbsp would survive. Order is the difference between a toggle that works and a toggle that does nothing.

Gotchas

  • .trim() is not enough. It strips leading/trailing whitespace from the whole string, but not from each line, and it doesn't collapse internal runs. The tool's "Trim each line" + "Collapse extra spaces" does what .trim() can't.
  • Non-breaking spaces survive a plain space collapse. \u00A0 is not matched by /[ \t]/ — it's a different code point. Toggle "Remove non-breaking spaces" on, or they'll sit in your text looking like spaces and breaking splits.
  • "Remove ALL spaces" removes spaces and tabs, not all whitespace. It targets [ \t]. Newlines are handled by the separate "Remove ALL line breaks" toggle. If you want zero whitespace of any kind, turn both on.
  • Code mode skips the leading-space removal. If you have "Remove leading spaces" checked but code mode on, the leading spaces stay. This is correct — code mode is a guard rail, not a toggle you override.
  • The spaces-to-tabs conversion only hits leading whitespace. A space between words in the middle of a line will never become a tab. This is by design; mid-line tabs are almost never what you want.
  • "Collapse 3+ newlines to 2" preserves one blank line. Three or more consecutive newlines become two (one blank line between paragraphs). If you want no blank lines, use "Remove empty lines" instead.
  • The final .trim() strips the whole string, not each line. Leading and trailing whitespace on the entire output is removed. Internal line-level leading/trailing is handled by the per-line toggles, not the final trim.
  • Zero-width spaces (U+200B) and other Unicode whitespace (U+2000–U+200A, U+202F, U+205F, U+3000) are not in the toggles. The tool handles the common ASCII set plus nbsp. If you're cleaning text with unusual Unicode spaces (CJK ideographic space U+3000 is the most common), you'll need a regex pass outside the tool.

Summary

  • Whitespace is six characters, not one: space (U+0020), tab (U+0009), LF (U+000A), CR (U+000D), non-breaking space (U+00A0), and zero-width space (U+200B). The tool's ten toggles exist because each behaves differently.
  • The non-breaking space trap: U+00A0 looks like a space but isn't matched by a plain /[ \t]/ regex or a literal .split(' '). The "Remove non-breaking spaces" toggle converts it to U+0020 before the rest of the pipeline runs.
  • Code mode preserves indentation. It strips trailing whitespace per line and leaves leading whitespace untouched — the one toggle that cleans code without destroying Python/YAML structure. Use it for source files; use the regular toggles for prose.
  • The pipeline order decides what survives. Nbsp conversion runs before space collapse; indent conversion runs before tab removal; newline collapse runs before empty-line removal. A toggle that "does nothing" usually runs after the operation that would have fed it.
  • Line endings: LF (Unix/Mac), CRLF (Windows), CR (old Mac). The tool normalizes to LF internally, then converts to your output choice. Git's core.autocrlf exists because of this split.
  • Clean text at the Whitespace Remover; pair with Remove Duplicate Lines for dedup after cleanup, Text Case Changer for casing, and Text Compare to verify the before/after diff.