Truncate in the middle
Fit a 62-character path into 36 and keep both ends: /Users/alex/Docum...engines/parse.js still shows the root folder and the file name. The dots count toward the limit, and you can choose how much of the start to keep. Free in your browser.
- Free
- Runs in your browser
- No sign-up
- Your data stays on your device
Run — free
Worked example
- Length
- 36
- Text
- /Users/alex/Docum...engines/parse.js
- Original length
- 62
- Result length
- 36
- Ellipsis
- ...
Middle truncation shortens a string to a target display length by cutting out the center and inserting an ellipsis, so the beginning and the ending stay readable. That is the pattern file managers, terminals, breadcrumbs, log viewers, and table cells use when a path or filename is too long for the column: you still see the root directory and the actual file, or the start of a UUID and its last digits. Enter the text and the integer length you want the result to respect; optionally choose how many leading characters to keep and which ellipsis marker to insert. The solver returns the shortened string, the kept prefix and suffix, whether a cut happened, the original and result lengths, and a short message.
What truncate in the middle means versus a prefix cut
Middle truncation is a hard display constraint with a different shape from ordinary prefix truncation. A prefix cut keeps only the start of the source and appends an ellipsis at the end; that is useful for sentences and titles, but it hides the part people usually need on a path or filename—the leaf name, the extension, the last folder, or the trailing digits of an identifier.
Truncating in the middle removes a contiguous run from the interior and inserts an ellipsis between a kept prefix and a kept suffix, so both anchors remain visible. The maximum length you send is the maximum length of the entire result including the ellipsis when a cut is required. If the source already fits, the tool returns it unchanged, sets truncated to false, puts the full string in start, and leaves end empty so callers can skip UI affordances that imply more content is hidden.
If it does not fit, the solver computes a content budget equal to length minus the ellipsis length. When you omit head, that budget is split as evenly as possible, with an odd leftover character given to the start. When you set head, that many leading characters are kept (clamped to the budget) and the remainder of the budget goes to the end, which is how you force a long filename to keep its extension while still showing a short directory hint.
Lengths below one are never accepted: a zero-width preview is not a useful contract for tables, breadcrumbs, or logs, and negative limits are always client bugs. Empty text with a valid length is allowed and returned as empty without an ellipsis, which keeps pipelines that pass optional path fields from failing only because a name is blank.
Practical tips for paths, filenames, and related searches
Use this capability when the search intent is truncate in the middle, shorten a filename with an ellipsis, or keep the start and end of a path visible online. Prefer a balanced split for generic identifiers and UUIDs; prefer a small head when the valuable token sits at the end, such as a report name with an extension; prefer a large head when the valuable token sits at the start, such as a host or drive letter you must not hide.
Custom ellipsis such as a single Unicode horizontal ellipsis character, a spaced middle dot, or a bracketed more label works when its length still fits inside the budget. This landing is not an AI summarizer, not a prefix-only truncator for sentences, not a paragraph or word counter, and not a path normalizer that resolves dots or home directories.
Prefer a prefix truncate tool when the product already decided to keep only the beginning of a sentence; prefer an LLM shorten tool when you need meaning-preserving compression; prefer this capability when a human has to recognize both ends of a path, filename, URL, or identifier inside a fixed character grid.
When teaching reviewers, show the same long path under a balanced split and under a head preference so they see that only the cut position and the start and end fields change while the length and ellipsis rules stay the same.
What you can do with it
File-manager columns and breadcrumbs
Clamp long absolute paths so a table or breadcrumb still shows the root and the leaf filename instead of hiding the extension behind a trailing ellipsis.
Log viewers and CI artifact names
Fit build artifact paths and uploaded report names into a fixed-width log line while keeping the last folder and the file extension readable.
FAQ
How does truncate in the middle decide what to keep?
It subtracts the ellipsis length from your maximum, then keeps a prefix and a suffix that together fill that budget. Omit head for an even split (odd leftover goes to the start), or set head to pin how many leading characters to keep.
Is this the same as truncating text at the end?
No. End truncation hides the suffix. Middle truncation is for paths, filenames, and identifiers where both the beginning and the ending must stay visible inside a fixed length.
What happens if length is zero or the ellipsis cannot fit?
Length must be an integer of at least one, and when a cut is required the ellipsis must not be longer than that length. Failed tasks are never charged.
Can I keep more of the filename than of the directory?
Yes. Set head to a small integer so most of the remaining budget goes to the end. That is the usual pattern when the extension or leaf name is the token a person needs to recognize.
Is the truncate-in-the-middle tool 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/truncate-middle \
-H "Authorization: Bearer $KIT_KEY" \
-H "Content-Type: application/json" \
-d '{"text":"/Users/alex/Documents/projects/kit-taller/src/engines/parse.js","length":36}'const res = await fetch("https://api.kit.forhosting.com/str/truncate-middle", {
method: "POST",
headers: {
"Authorization": `Bearer ${process.env.KIT_KEY}`,
"Content-Type": "application/json"
},
body: JSON.stringify({
"text": "/Users/alex/Documents/projects/kit-taller/src/engines/parse.js",
"length": 36
})
});
const { task_id } = await res.json();import os, requests
res = requests.post(
"https://api.kit.forhosting.com/str/truncate-middle",
headers={"Authorization": f"Bearer {os.environ['KIT_KEY']}"},
json={
"text": "/Users/alex/Documents/projects/kit-taller/src/engines/parse.js",
"length": 36
},
)
task_id = res.json()["task_id"]<?php
$res = file_get_contents("https://api.kit.forhosting.com/str/truncate-middle", false, stream_context_create([
"http" => [
"method" => "POST",
"header" => "Authorization: Bearer " . getenv("KIT_KEY") . "\r\nContent-Type: application/json",
"content" => '{"text":"/Users/alex/Documents/projects/kit-taller/src/engines/parse.js","length":36}',
],
]));
$task = json_decode($res, true);body := bytes.NewBufferString(`{"text":"/Users/alex/Documents/projects/kit-taller/src/engines/parse.js","length":36}`)
req, _ := http.NewRequest("POST", "https://api.kit.forhosting.com/str/truncate-middle", 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": "/Users/alex/Documents/projects/kit-taller/src/engines/parse.js",
"length": 36
}Example response
{
"task_id": "tsk_a1b2c3d4e5f6a1b2c3d4e5f6",
"type": "str.truncate_middle",
"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
A length below one is rejected with invalid_input so form validation and CI never treat a zero or negative cap as success.
How to call the API and what the response contains
Send a JSON body with required text (aliases input, value, string, s, content, and body) and required length (aliases max_length, max, n, limit, and max_len). Length must be an integer from one through one hundred thousand inclusive; values below one, non-integers, and oversize limits are invalid_input. Optional head (aliases start, keep_start, and prefix) is a non-negative integer that prefers how many leading characters to keep; omit it for a balanced split. Optional ellipsis (aliases marker, separator, and gap) defaults to three ASCII dots and may be any string up to thirty-two characters; when truncation is required the ellipsis must not be longer than length or the request fails rather than inventing a clipped marker. Text must stay within one hundred thousand characters so a single request cannot exhaust Worker or browser memory. On success the response echoes input as the original string, text as the final string to display, length as the requested maximum, original_length and result_length as character counts, truncated as a boolean, ellipsis as the marker used, start as the kept prefix, end as the kept suffix, and message as a short English summary. Failed validation—missing text or length, a non-string text, a non-integer or sub-one length, a negative head, an oversize payload, or an ellipsis that cannot fit under the limit when a cut is needed—returns invalid_input and is never charged. Pricing is a flat $0.002 per successful request with unit item. Pair the call with your own layout code if you need to measure glyphs in a proportional font; this endpoint counts JavaScript string indices, not rendered pixels, which is the same contract most server-side path formatters already use.
Practical tips for paths, filenames, and related searches
Round-trip checks for tests are easy: feed a long absolute path with length thirty-six and expect truncated true, a non-empty start, a non-empty end, and result_length at most thirty-six; set head to twelve on the same path and expect the prefix to be exactly those twelve characters when the budget allows; send length zero or a negative integer and expect invalid_input mentioning length below 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 path formatters
Generate known-good middle truncations, head-biased cuts, and length-below-one rejections for frontend helpers and storage UIs.