trimStart()

ES2019+

Returns a new string with whitespace removed only from the beginning, leaving the end and the interior untouched and the original string unchanged. It recognizes the same whitespace set as trim(), including tabs, line breaks, and Unicode space separators. The legacy alias trimLeft() refers to the same function.

Syntax

string.trimStart()

Return Value

string

A new string with whitespace removed from the start

Examples

JavaScript
const str = '   Hello';
console.log(str.trimStart());
Output:
// 'Hello'

📌 When to Use

Use trimStart() when the start of a string is noise but the end must be preserved, or when you need to measure or manipulate indentation. Classic cases: computing how deeply a line of code is indented by comparing line.length with line.trimStart().length, dedenting a block of source by a common margin, cleaning the leading spaces that follow a delimiter after splitting ('a, b, c' split on commas leaves ' b' and ' c'), and processing prompt-like or log-like lines where trailing spacing is significant but leading padding is not. It is the precise tool where trim() would be a blunt one: if trailing whitespace can carry meaning, for example in fixed-width output, Markdown where two trailing spaces force a line break, or diff hunks, trimming only the start states your intent exactly and avoids collateral damage. trimStart() takes no arguments and removes only whitespace; stripping an arbitrary prefix such as a protocol or a leading slash is a different job, done with startsWith() plus slice() or with an anchored regex replacement. It arrived with ES2019 alongside trimEnd(), and the older nonstandard trimLeft() alias persists only for web compatibility; new code should always use the standard name.

⚠️ Common Mistakes

Expecting the original string to change: like every string method, trimStart() returns a new string. Using it as a statement without capturing the result is a silent no-op.

Reaching for the legacy alias trimLeft(): it survives as an alias for web compatibility but is nonstandard Annex-B territory; linters flag it and it reads as dated. Always write trimStart() in new code.

Using trimStart() to remove a non-whitespace prefix: it cannot strip things like 'https://' or leading zeros; it removes only whitespace characters. Prefix removal needs startsWith() with slice(), or a regex replacement anchored to the start of the string.

Destroying meaningful indentation: in code snippets, YAML, Python, and Markdown, leading whitespace is syntax. Blanket-applying trimStart() to every line of such content corrupts it; dedent by the common minimum indent instead, so relative structure survives.

Forgetting that line terminators are whitespace: trimStart() also eats leading newlines and carriage returns, not just spaces and tabs. When processing a multi-line string as a whole, a leading blank line disappears, which may or may not be what you intended.

Assuming every invisible character is removed: zero-width spaces and other Unicode format characters are not whitespace and remain at the start of the string after trimming, still able to break comparisons and startsWith() checks.

✅ Best Practices

Choose the one-sided trims deliberately: trimStart() when trailing content matters, trimEnd() when leading content matters, trim() only when both ends are genuinely noise. The method name then documents your intent.

Use the length difference between a line and its trimStart() result as a clean, allocation-light way to measure indentation depth when parsing or formatting code.

When dedenting multi-line text, compute the minimum indentation across non-empty lines first and slice that amount from every line, rather than fully trimming each line and flattening the structure.

Prefer the standard trimStart() name over trimLeft() and enable a lint rule to enforce it, keeping the codebase consistent with the specification vocabulary.

For stripping a known textual prefix, pair startsWith() with slice(prefix.length); reserve trimStart() strictly for whitespace so readers never have to guess what is being removed.

⚡ Performance Notes

trimStart() scans forward from the first character until it finds non-whitespace, then returns the remainder, so its cost tracks the amount of leading whitespace, and engines often return the original string object when there is nothing to remove. Because engines may implement the result as a sliced view into the parent string, even trimming a huge string is effectively free of copying. It is comfortably cheap enough to run per line over large files, as in dedenting or indentation analysis, where the dominant cost will be the split into lines and the final join, not the trims. The indentation-measuring idiom of subtracting the trimmed length from the original length allocates one small string per line; if you are processing millions of lines and profiling shows pressure, a manual index scan over character codes avoids the allocation, but that is a last-resort optimization, not a starting point.

🌍 Real World Example

Code Formatting and Indentation Handling

A dedenting utility of the kind found in template-literal libraries and documentation generators. dedentCode() splits a code block into lines, then measures each non-empty line's indentation by subtracting the trimStart() length from the raw length, taking the minimum across lines as the shared margin. Every line is then sliced by that amount and rejoined, so the block shifts flush-left while its internal, relative indentation survives intact. This is exactly the case where trim() or per-line trimStart() alone would be destructive, and the trimStart-based length arithmetic is the standard measuring trick.

// Remove leading whitespace from each line in code block
function dedentCode(code) {
  const lines = code.split('\n');
  // Find minimum indentation (ignoring empty lines)
  const minIndent = lines
    .filter(line => line.trim().length > 0)
    .reduce((min, line) => {
      const indent = line.length - line.trimStart().length;
      return Math.min(min, indent);
    }, Infinity);

  // Remove the minimum indentation from all lines
  return lines
    .map(line => line.slice(minIndent))
    .join('\n');
}

const indentedCode = `
    function hello() {
        console.log('Hello');
    }
`;
console.log(dedentCode(indentedCode));

// Process user input where leading spaces were accidental
function normalizeListItem(item) {
  // Remove accidental leading spaces but keep trailing spaces
  // (might be intentional formatting)
  return item.trimStart();
}

const items = ['  Apple', '   Banana', 'Cherry  '];
console.log(items.map(normalizeListItem));
// Output: ['Apple', 'Banana', 'Cherry  ']

Related Methods