trimEnd()
ES2019+Returns a new string with whitespace removed only from the end, preserving all leading and interior whitespace, and leaving the original string unchanged. It recognizes the same whitespace set as trim(), including trailing newlines and carriage returns. The legacy alias trimRight() refers to the same function.
Syntax
string.trimEnd()Return Value
A new string with whitespace removed from the end
Examples
const str = 'Hello ';
console.log(str.trimEnd()); 📌 When to Use
Use trimEnd() when trailing whitespace is garbage but leading whitespace is information. The archetypal case is line-oriented text where indentation is syntax: stripping the invisible trailing spaces and carriage returns from each line of a source file, log, or diff without disturbing the indentation that gives the content its structure. It is equally useful for cleaning values whose trailing newline came from the transport rather than the data, such as the output of a shell command, a line read from a stream, or a fixed-width field padded to a column boundary. Trailing whitespace is a notorious source of phantom bugs: two lines that render identically compare unequal, checksums differ, and version-control diffs light up with invisible changes, which is why editors and linters flag it and why normalizing it at processing time pays off. Splitting Windows-style CRLF text on the newline character alone leaves a carriage return stuck to every line, and trimEnd() per line is a robust cleanup. Choose it over trim() whenever the left edge could ever matter; for content where neither edge matters, trim() is fine, and for removing a specific trailing suffix such as a file extension or punctuation, use endsWith() with slice() instead, because trimEnd() only removes whitespace.
⚠️ Common Mistakes
Discarding the result: trimEnd() returns a new string and cannot modify the original in place. It must be used in an expression or assignment; as a bare statement it accomplishes nothing.
Using trim() when indentation matters: reflexively trimming both ends of each line of code, YAML, or Markdown destroys leading structure. If the goal is only to remove trailing mess, trimEnd() is the correct, lossless choice.
Forgetting the stray carriage return from CRLF files: after splitting Windows text on the newline character, every line ends with an invisible carriage return that makes equality checks and endsWith() tests fail. trimEnd() removes it; not knowing it is there is the actual bug.
Trying to strip a non-whitespace suffix: trimEnd() cannot remove trailing punctuation, extensions, or padding characters like dashes or zeros. Use endsWith() plus slice(), or a regex replacement anchored to the end of the string, for that job.
Using the legacy trimRight() alias in new code: it exists for backwards compatibility only; the standard name since ES2019 is trimEnd(), and mixing both names in one codebase is needless inconsistency.
Removing whitespace that is significant: Markdown treats two trailing spaces as a hard line break, and some fixed-width formats rely on trailing padding to keep columns aligned. Normalize trailing whitespace only when you know the format does not assign it meaning.
✅ Best Practices
Normalize line endings and trailing whitespace together when ingesting text files: split into lines, trimEnd() each line, and rejoin, which simultaneously fixes CRLF remnants and editor-introduced trailing spaces.
Prefer trimEnd() over trim() in any per-line pipeline over structured text, so the transformation is provably non-destructive to indentation.
Strip the trailing newline of subprocess or stream output with trimEnd() rather than slicing a fixed number of characters, since the output may end with no newline, one, or a CRLF pair.
Keep suffix removal and whitespace removal distinct: endsWith() with slice() for meaningful suffixes, trimEnd() for whitespace, so each line of code states exactly what it removes.
When comparing or hashing lines of text, trimEnd() both sides first; trailing-whitespace differences are invisible to humans and should almost never count as real differences.
⚡ Performance Notes
trimEnd() scans backward from the last character until it meets non-whitespace and returns the prefix, so the work is proportional to the trailing whitespace removed; when there is none, engines typically hand back the original string object with no allocation. As with the other trims, the result may be a sliced view rather than a copy, making the operation cheap even on very large strings. Running it per line across sizable files is entirely reasonable: in a split-map-join pipeline, the split and join dominate, not the trims. One subtle memory note applies to extreme cases: a trimmed view can retain its larger parent string in engines that use sliced representations, which only matters if you keep millions of trimmed lines from giant buffers alive long-term. For ordinary file cleaning, configuration parsing, and log processing, trimEnd() is effectively free and never the bottleneck.
🌍 Real World Example
File Processing and Log Cleaning
A file-cleaning utility in the style of an editor's trim-trailing-whitespace-on-save feature. cleanCodeFile() splits the file content into lines, maps trimEnd() over each line to delete the invisible trailing spaces and tabs that accumulate during editing, and joins the lines back together. Crucially, the leading indentation of every line survives untouched, which is why trimEnd() and not trim() is the right tool: the transformation is guaranteed to change nothing a reader can see, while eliminating the noise that pollutes diffs, breaks line-equality checks, and trips up whitespace-sensitive linters.
// Clean trailing whitespace from code while preserving indentation
function cleanCodeFile(content) {
return content
.split('\n')
.map(line => line.trimEnd())
.join('\n');
}
const messyCode = `
function example() {
const x = 1;
return x;
}
`;
console.log(cleanCodeFile(messyCode));
// Removes trailing spaces while keeping indentation
// Process log entries with timestamps
function parseLogEntry(line) {
// Preserve leading timestamp/level but remove trailing garbage
const cleaned = line.trimEnd();
const match = cleaned.match(/^(\[.*?\])\s*(.*)$/);
if (match) {
return { timestamp: match[1], message: match[2] };
}
return { timestamp: '', message: cleaned };
}
const logLine = '[2024-01-15 10:30:45] User logged in \t\n';
console.log(parseLogEntry(logLine));
// Output: { timestamp: '[2024-01-15 10:30:45]', message: 'User logged in' }