Trim whitespace from every line
Clean the edges of every line in one pass: three padded lines, one with tabs, lose 10 stray characters and read hello, world and foo bar, with the space inside foo bar kept. Blank lines stay where they were. Free in your browser.
- Free
- Runs in your browser
- No sign-up
- Your data stays on your device
Run — free
Worked example
- Text
- hello world foo bar
- Input
- hello world foo bar
- Text
- hello world foo bar
- Line count
- 3
- Lines
- hello, world, foo bar
A line-aware whitespace trimmer turns messy multi-line paste into clean edges without collapsing the words inside each line. Enter any non-empty string—logs, code snippets, TSV dumps, or prose copied from a document—and the tool normalizes line endings, splits on newlines, and runs a full trim on every line so leading spaces, trailing tabs, and mixed indent noise disappear while interior spacing stays intact. Editors, QA scripts, homework checkers, and ETL preprocessors need that same per-line rule without opening a full IDE search-and-replace.
What per-line trim means and why it differs from String.trim
Most languages offer a single trim that only removes whitespace from the start and end of an entire string. That leaves indented middle lines untouched, which is exactly the problem when you paste a block of log lines, a code sample with accidental trailing spaces, or a multi-row spreadsheet export where each row has padded cells.
This tool trims each line independently: after newlines are normalized from Windows CRLF and legacy CR into a single LF form, the text is split on LF, every segment is passed through a full Unicode-aware whitespace trim, and the segments are joined again with LF. Interior spaces between words stay put; only the edges of each line are cleaned. Blank lines between content become empty strings after trim, so structure that used blank separators remains visible as blank lines rather than vanishing.
Leading and trailing blank lines in the original paste are still present as empty lines after the split, because the algorithm does not drop lines—it only trims them. The design matches everyday editor commands such as “trim trailing whitespace” applied across a selection, while also clearing leading padding that often appears when copying from indented lists or chat UIs.
Practical tips, limits, and related searches
Use this capability when the search intent is trim whitespace from lines, strip leading trailing spaces multiline, or clean indented paste online. Keep intentional indentation in mind only if you need leading spaces for code layout—this tool removes them; if you only want trailing cleanup while preserving indent, you need a different policy or a pre-pass. HTML source and Markdown still trim each raw line of the string you send; strip tags first if you care about rendered text rather than markup.
This landing is not a full-document String.trim only, not a blank-line deleter, not a space-collapser that turns double spaces into one, not a CSV cell cleaner with delimiter awareness, and not an AI rewriter. When teaching students or onboarding reviewers, show the same sample before and after so they see that only line edges change and that word-internal spacing is left alone.
What you can do with it
Clean log and console paste for tickets
Developers paste multi-line logs with accidental leading spaces and trailing tabs; the tool returns flush lines ready for bug trackers and diffs.
Normalize homework and essay line dumps
Teachers and LMS scripts strip edge padding from each submitted line before word-count or plagiarism pipelines see the text.
FAQ
Does this remove spaces between words?
No. Only leading and trailing whitespace on each line is removed. Interior spaces and tabs inside a line are preserved.
What happens on empty input?
Provide a non-empty string that has at least one non-whitespace character.
Are blank lines deleted?
No. Blank lines stay as empty lines after trim. The tool does not drop or merge lines; it only trims each line’s edges.
How are Windows line endings handled?
CRLF and lone CR are normalized to LF before splitting so the same paste produces the same result on every platform.
Is the line whitespace trimmer free?
The browser tool is free and runs locally.
Related tools
For developers — API access
Everything on this page is available programmatically. This section is for teams who want to wire it into their own systems; everyone else can just use the tool above.
API endpoint
Prefer to automate it? One authenticated POST creates the task; the result comes back by webhook or a signed link. The same capability also runs here on the web, by email and from Telegram — and soon from our app too.
Call it from your stack
curl -X POST https://api.kit.forhosting.com/str/trim-lines \
-H "Authorization: Bearer $KIT_KEY" \
-H "Content-Type: application/json" \
-d '{"text":" hello \n\tworld\t\n foo bar "}'const res = await fetch("https://api.kit.forhosting.com/str/trim-lines", {
method: "POST",
headers: {
"Authorization": `Bearer ${process.env.KIT_KEY}`,
"Content-Type": "application/json"
},
body: JSON.stringify({
"text": " hello \n\tworld\t\n foo bar "
})
});
const { task_id } = await res.json();import os, requests
res = requests.post(
"https://api.kit.forhosting.com/str/trim-lines",
headers={"Authorization": f"Bearer {os.environ['KIT_KEY']}"},
json={
"text": " hello \n\tworld\t\n foo bar "
},
)
task_id = res.json()["task_id"]<?php
$res = file_get_contents("https://api.kit.forhosting.com/str/trim-lines", false, stream_context_create([
"http" => [
"method" => "POST",
"header" => "Authorization: Bearer " . getenv("KIT_KEY") . "\r\nContent-Type: application/json",
"content" => '{"text":" hello \\n\\tworld\\t\\n foo bar "}',
],
]));
$task = json_decode($res, true);body := bytes.NewBufferString(`{"text":" hello \n\tworld\t\n foo bar "}`)
req, _ := http.NewRequest("POST", "https://api.kit.forhosting.com/str/trim-lines", body)
req.Header.Set("Authorization", "Bearer "+os.Getenv("KIT_KEY"))
req.Header.Set("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)Example request
{
"text": " hello \n\tworld\t\n foo bar "
}Example response
{
"task_id": "tsk_a1b2c3d4e5f6a1b2c3d4e5f6",
"type": "str.trim_lines",
"status": "queued",
"_links": {
"result": "/tasks/tsk_…/result"
}
}The API is asynchronous: the call returns a task_id immediately and the result arrives by webhook. Polling is capped at 1 req/s per task.
Pricing
Published price — no tokens, no invented credits. A failed task is never charged.
Limits
max_chars | 100000 |
Errors
| HTTP | Code | Meaning |
|---|---|---|
401 | unauthorized | Missing or invalid API key. |
402 | insufficient_balance | Your balance doesn't cover the task price. |
404 | unknown_type | That task type doesn't exist. |
429 | rate_limited | Too many requests. Use the webhook instead of polling. |
Technical notes
Empty or whitespace-only input is rejected with a clear invalid_input error so automation never treats a blank paste as a successful clean-up.
What per-line trim means and why it differs from String.trim
Empty or whitespace-only whole input is a hard error: there is no meaningful cleaned document to return, and callers should treat that as invalid_input rather than a zero-line success.
How to call the API and what the response contains
Prefer sending the raw multi-line body rather than pre-collapsing newlines when you want true per-line semantics; collapsing everything to one line would only trim the outer edges of a single segment. Send a JSON body with a required string field text (aliases input, value, string, s, content, and body). The value must be a non-empty string after overall whitespace is trimmed and must not exceed one hundred thousand characters so a single request cannot exhaust Worker or browser memory on huge paste dumps. On success the response echoes input as the original string you sent, text as the fully line-trimmed result joined with LF, line_count as the number of lines after split (including empty ones), lines as an ordered array of the trimmed line strings, chars_before and chars_after as the lengths of the original and cleaned strings, chars_removed as how many characters the trim removed in total, and message as a short English summary that reports the line count and characters removed. Failed validation—missing text, a non-string value, an oversize payload, or empty content after overall trim—returns invalid_input and is never charged. Pricing is a flat $0.002 per successful request with unit item and zero marginal unit price. When integrating, assert on text or lines for the cleaned content, optionally compare chars_removed for regression checks, and treat invalid_input as a client bug for blank form submissions rather than a billing event.
Practical tips, limits, and related searches
Round-trip checks for tests are easy: feed two padded lines and expect those words flush left with no trailing spaces; feed a tab-indented third line and confirm the tab is gone while interior double spaces in a phrase remain; deliberately send empty string or only spaces to confirm invalid_input and the message that asks for a non-empty string. Prefer data.clean_csv when the payload is tabular CSV with encoding fixes; prefer str.paragraph_count when you need blank-line paragraph totals; prefer this capability when humans already separated lines and you only need clean edges on each one. Failed tasks that return invalid_input do not consume prepaid balance, which keeps form validation and CI negative tests cheap.
Integration use cases
Unit tests for text preprocessors
Generate known-good padded multi-line fixtures and empty-input rejections for editors, ETL stages, and markdown pipelines.