Add strikethrough to text
Type «Hello, World!» and get H̶e̶l̶l̶o̶,̶ ̶W̶o̶r̶l̶d̶!̶ back, struck through letter by letter and ready to paste into WhatsApp, Discord, an Instagram bio or an email subject. The line is part of the text itself, so no special font is needed. 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!
- Output
- H̶e̶l̶l̶o̶,̶ ̶W̶o̶r̶l̶d̶!̶
- Length
- 13
Strikethrough text is plain text with a hidden twist: after every visible character sits U+0336, the Unicode combining long stroke overlay, so the letters render with a horizontal line through them anywhere Unicode is supported — chats, social posts, bios, documents. This tool inserts that combining character after every character of the text you provide, including spaces and punctuation, and returns the result ready to copy and paste. It works on any Unicode text, from plain ASCII to accented letters, CJK characters and emoji, because the stroke is applied per code point without touching the characters themselves. The transformation is fully deterministic and reversible by simply removing every U+0336. No font, no markup and no platform support is required on your side: the output is ordinary text that carries its own strike everywhere it goes.
How the combining strikethrough works
Unicode defines a family of combining characters that attach to the character before them instead of standing alone. U+0336, the combining long stroke overlay, is one of them: when a renderer encounters it, it draws a horizontal line across the preceding base character. By appending U+0336 after every character of your input, this tool produces a string where each visible glyph appears struck through.
The result is not an image, not HTML and not Markdown — it is still plain text, so it survives copy-paste into WhatsApp, Telegram, X, Instagram bios, Discord, email subjects and virtually any field that accepts Unicode. Because the stroke is a separate code point, screen readers and search still see the original characters, which keeps the text findable and accessible even while it looks crossed out.
Note that rendering width can vary slightly between fonts and platforms, since each renderer decides how wide to draw the overlay, but the underlying string is identical everywhere.
What characters get the stroke
Every character receives the overlay, including spaces, digits, punctuation and newlines. Striking spaces too is deliberate: it keeps the line visually continuous across words, which is what most people expect when they paste struck text into a chat. The tool iterates over Unicode code points rather than UTF-16 units, so characters outside the Basic Multilingual Plane — emoji like a grinning face or rare CJK ideographs — are never split apart and each receives exactly one overlay of its own.
Accented letters such as e with acute keep their accent and gain the stroke, because combining marks stack in order. The response also reports the character count of the input, so callers can verify at a glance that nothing was dropped and that the text they sent is the text that was struck through.
What you can do with it
Edits and corrections in chats
Show a changed price or a retracted word in messengers that have no native strikethrough formatting.
Social posts and bios
Cross out old slogans or add ironic asides in bios and captions on platforms that only accept plain text.
Before-and-after comparisons
Strike the old value next to the new one in changelogs, price tables and release notes pasted as plain text.
FAQ
What exactly is inserted after each character?
U+0336, the Unicode combining long stroke overlay. It renders as a horizontal line through the preceding character.
Does it work on any text, including emoji and accents?
Yes. The tool walks Unicode code points, so emoji, CJK characters and accented letters each get their own stroke without being split or altered.
Can I undo the strikethrough?
Yes. Remove every U+0336 code point from the output and you get the original text back, because base characters are never modified.
Why are the spaces crossed out as well?
On purpose: striking the spaces keeps the line continuous from one word to the next, which is what most people expect when they paste crossed-out text into a chat. Digits, punctuation and line breaks get the line too.
Where can I paste strikethrough text?
Almost anywhere that accepts Unicode, such as WhatsApp, Telegram, X, Instagram bios, Discord, email subjects and documents. It is ordinary text, not an image or formatting, so the line survives copy and paste. Its width can vary slightly from one font to another.
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/text/strikethrough \
-H "Authorization: Bearer $KIT_KEY" \
-H "Content-Type: application/json" \
-d '{"text":"Hello, World!"}'const res = await fetch("https://api.kit.forhosting.com/text/strikethrough", {
method: "POST",
headers: {
"Authorization": `Bearer ${process.env.KIT_KEY}`,
"Content-Type": "application/json"
},
body: JSON.stringify({
"text": "Hello, World!"
})
});
const { task_id } = await res.json();import os, requests
res = requests.post(
"https://api.kit.forhosting.com/text/strikethrough",
headers={"Authorization": f"Bearer {os.environ['KIT_KEY']}"},
json={
"text": "Hello, World!"
},
)
task_id = res.json()["task_id"]<?php
$res = file_get_contents("https://api.kit.forhosting.com/text/strikethrough", false, stream_context_create([
"http" => [
"method" => "POST",
"header" => "Authorization: Bearer " . getenv("KIT_KEY") . "\r\nContent-Type: application/json",
"content" => '{"text":"Hello, World!"}',
],
]));
$task = json_decode($res, true);body := bytes.NewBufferString(`{"text":"Hello, World!"}`)
req, _ := http.NewRequest("POST", "https://api.kit.forhosting.com/text/strikethrough", 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, World!"
}Example response
{
"task_id": "tsk_a1b2c3d4e5f6a1b2c3d4e5f6",
"type": "text.strikethrough",
"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_text_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
What characters get the stroke
The input is capped at 100000 characters; anything longer returns a typed invalid_input error, as do missing or empty text values, so integrations can rely on a stable failure contract instead of silent truncation.
Determinism, limits and the free browser tool
The transformation is a pure function: same input, same output, byte for byte, with no network calls, no randomness and no clock involved. If you need to undo a strikethrough later, remove every U+0336 code point from the string and the original text comes back unchanged, because the algorithm never alters the base characters — it only appends. The API charges $0.002 per call regardless of text length within the limit. Errors are reported as typed invalid_input responses with stable message substrings, never as opaque server failures.
Technical questions
What happens if the text is empty?
The call fails with a typed invalid_input error — text is required and must not be empty.
Is there a length limit?
Yes, 100000 characters per call. Longer inputs are rejected with invalid_input instead of being truncated.