Prevent text widows with a non-breaking space
The widow prevention tool improves the visual rhythm of headlines, introductions, cards, captions, and other carefully set text.
Run — free
It inserts a non-breaking space between the final two words of each paragraph, encouraging browsers and layout engines to keep that pair on the same line. Paragraph breaks, wording, punctuation, and existing spacing remain intact. The transformation is deterministic and idempotent, so the same content can pass through an automated publishing workflow more than once without accumulating additional changes.
Why a final non-breaking space improves typography
A widow is a short final line, often consisting of a single word, left at the bottom of a paragraph or beneath a headline. It can make a balanced card, hero, or editorial block look accidental, especially when responsive layouts change width across devices. This tool applies a small and widely understood typographic safeguard: it replaces the final ordinary spacing character before the last word with a Unicode non-breaking space. The final two words therefore behave as a pair when a browser or another compatible layout engine chooses line breaks. The text itself is not rewritten, summarized, or re-punctuated. Blank lines separating paragraphs stay exactly where they were, and any extra spaces before the last word are retained except for the one character that becomes non-breaking. Paragraphs without a suitable final word pair are left alone. Because an existing non-breaking space is not replaced again, repeated processing produces the same result instead of adding invisible characters on every pass.
How to prepare and inspect your text
Send the source in the text field exactly as it should otherwise appear. Separate paragraphs with one or more blank lines; those separators are preserved in the output. Within each paragraph, the transformer looks at the final line and finds its last non-whitespace token. When that token is preceded by an ordinary space or tab, the last character in that spacing run becomes a non-breaking space. Punctuation attached to the final word remains attached, so a closing period, question mark, quotation mark, or parenthesis requires no special handling. After processing, copy the returned text into the destination system and preview it at the widths that matter. A non-breaking space influences wrapping but cannot guarantee a beautiful layout when a container is extremely narrow or the final words are unusually long. It is also worth confirming that the editor, CMS, template engine, or database preserves Unicode rather than normalizing the character back to an ordinary space during later processing.
Using widow prevention in publishing workflows
The capability is useful as a final, deterministic formatting stage after copy has been approved but before content is rendered. A CMS integration can process a headline and summary when an entry is published, while a static-site generator can apply it to selected fields during a build. Email teams can protect headings before inserting them into templates, and design systems can use the result for card titles that frequently wrap at different breakpoints. Apply the transformation selectively rather than blindly to source code, preformatted text, URLs, or data where every whitespace character has semantic meaning. Store the returned text if your delivery channel accepts Unicode text, or convert the non-breaking space to the appropriate escaped representation only when a downstream format explicitly requires that representation. The API charges $0.002 per request, while the browser runner offers the same deterministic logic locally. Since no network lookup, model, random value, or clock is involved, identical input always yields identical output across repeatable automation runs.
What you can do with it
Polish responsive headlines
Keep the final two words of hero and card headlines together as layouts narrow across desktop and mobile breakpoints.
Prepare CMS copy
Add consistent widow protection to selected title, standfirst, caption, or promotional fields during a publishing workflow.
Improve email typography
Protect the closing word pair in headings and short paragraphs before content is inserted into an email template.
FAQ
What does the tool change?
It changes the final ordinary spacing character before the last word of each eligible paragraph into a Unicode non-breaking space.
Does it preserve paragraph breaks?
Yes. Blank-line paragraph separators and all wording are preserved exactly.
Can I process the same text twice?
Yes. The transformation is idempotent: text already protected with a non-breaking space is not changed again.
Will it always eliminate every short final line?
No. It keeps the final pair together, but actual wrapping still depends on font metrics, container width, and the layout engine.
How much does the API cost?
Each API request costs $0.002. The browser version runs the same logic locally.
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/prevent-widows \
-H "Authorization: Bearer $KIT_KEY" \
-H "Content-Type: application/json" \
-d '{"text":"A clear headline should never leave one word behind.\n\nThis second paragraph is protected too."}'const res = await fetch("https://api.kit.forhosting.com/str/prevent-widows", {
method: "POST",
headers: {
"Authorization": `Bearer ${process.env.KIT_KEY}`,
"Content-Type": "application/json"
},
body: JSON.stringify({
"text": "A clear headline should never leave one word behind.\n\nThis second paragraph is protected too."
})
});
const { task_id } = await res.json();import os, requests
res = requests.post(
"https://api.kit.forhosting.com/str/prevent-widows",
headers={"Authorization": f"Bearer {os.environ['KIT_KEY']}"},
json={
"text": "A clear headline should never leave one word behind.\n\nThis second paragraph is protected too."
},
)
task_id = res.json()["task_id"]<?php
$res = file_get_contents("https://api.kit.forhosting.com/str/prevent-widows", false, stream_context_create([
"http" => [
"method" => "POST",
"header" => "Authorization: Bearer " . getenv("KIT_KEY") . "\r\nContent-Type: application/json",
"content" => '{"text":"A clear headline should never leave one word behind.\\n\\nThis second paragraph is protected too."}',
],
]));
$task = json_decode($res, true);body := bytes.NewBufferString(`{"text":"A clear headline should never leave one word behind.\n\nThis second paragraph is protected too."}`)
req, _ := http.NewRequest("POST", "https://api.kit.forhosting.com/str/prevent-widows", 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": "A clear headline should never leave one word behind.\n\nThis second paragraph is protected too."
}Example response
{
"task_id": "tsk_a1b2c3d4e5f6a1b2c3d4e5f6",
"type": "str.prevent_widows",
"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 | 200000 |
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. |