JavaScript String Escape Sequences: A Complete Guide to \n, \t, \u, and Raw Strings
Understand every JavaScript string escape sequence — \n, \t, \r, \uXXXX, \u{...}, \x, and String.raw — with real code examples, Unicode edge cases, and JSON pitfalls developers hit in production.
JavaScript String Escape Sequences: A Complete Guide to \n, \t, \u, and Raw Strings
JavaScript strings look straightforward until you paste a file path into a JSON body, try to embed an emoji in a regex, or print a tab-aligned table to the console. That's when escape sequences stop being trivia and start causing real bugs. This guide covers every escape sequence you'll encounter in practice — with real input/output pairs, not just syntax diagrams.
The Six Escape Sequences You'll Use Every Week
The most common escapes fit on a sticky note:
| Sequence | Name | Char code | |---|---|---| | \n | newline | U+000A | | \r | carriage return | U+000D | | \t | horizontal tab | U+0009 | | \\ | literal backslash | U+005C | | \' | single quote | U+0027 | | \" | double quote | U+0022 |
Paste this into a Node.js REPL and see what each one does:
// Input
const sample = "Name:\tAlice\nRole:\tEngineer\\backend";
console.log(sample);
// Output
Name: Alice
Role: Engineer\backend
The \t injects a real tab character, \n breaks to a new line, and \\ produces a single literal backslash. That last one is the one that catches people — a Windows path like C:\new\tasks written as a bare string literal silently converts \n and \t into a newline and a tab, mangling the path entirely. The fix is either \\n, \\t, or a raw string (see below).
Unicode Escapes: \uXXXX vs \u{...}
JavaScript originally supported only the 4-hex-digit form \uXXXX, which covers the Basic Multilingual Plane — 65,536 code points. The ECMAScript 2015 specification added the brace form \u{1F600}, which covers all 1,114,112 Unicode code points, including every emoji, every historic script, and every supplementary CJK character.
// 4-digit form — BMP only
"é" // → é (U+00E9 LATIN SMALL LETTER E WITH ACUTE)
"中" // → 中 (U+4E2D CJK UNIFIED IDEOGRAPH)
// Brace form — full code point range (ES2015+)
"\u{1F600}" // → 😀 (U+1F600 GRINNING FACE)
"\u{1F1FA}\u{1F1F8}" // → 🇺🇸 (regional indicator letters U+S)
The brace form is the correct choice for any character above U+FFFF. Using the 4-digit form for a supplementary character requires a surrogate pair — two \uXXXX sequences together — which is error-prone and hard to read:
// Surrogate pair (pre-ES2015 style) — fragile, avoid
"😀" // → 😀
// Brace form (ES2015+) — clear and correct
"\u{1F600}" // → 😀
To inspect what code point a character actually sits at, use the Unicode Code Point Explorer — paste any character and it shows the U+XXXX notation, UTF-8 byte sequence, and surrounding Unicode block in one view.
Hex Escapes and Control Characters: \x and \0
Beyond Unicode, JavaScript supports:
\xNN— two-hex-digit escape, covers U+0000–U+00FF (Latin-1)\0— null character (U+0000), but only when not followed by a digit\b— backspace (U+0008), not to be confused with\bin regex (word boundary)\f— form feed (U+000C), a relic from printer days\v— vertical tab (U+000B)
I ran into \b confusion myself while debugging a regex — the string "\b" is a literal backspace character, but inside a new RegExp("\\b") or /\b/ it's a word boundary. They share the same syntax but mean completely different things depending on context.
// String context
"\b".charCodeAt(0) // → 8 (backspace)
// Regex context
/\b/.test("hello") // → true (matches at word boundary, not backspace)
String.raw: Raw Strings Without Any Escaping
Template literal tag String.raw returns the string with no escape processing — backslashes stay as literal backslashes:
// Normal template literal
`C:\new\tasks` // → C:
// ew asks (newline + tab injected)
// Raw string
String.raw`C:\new\tasks` // → C:\new\tasks (exactly as written)
This is the correct pattern for Windows paths, regex source strings you're building dynamically, and any context where you need the backslash to survive into the output.
String.raw is a tagged template literal — the tag function receives the raw array of string parts, where each part is the unprocessed source text. The ECMAScript spec (ECMA-262, 2015) defines this as part of the template literal revision, specifically so tooling and template libraries could access the pre-escape text. Per the V8 blog, template literals in modern V8 are optimized at compile time with no runtime overhead compared to +-concatenated strings, meaning String.raw carries zero performance cost over a regular template.
The JSON.stringify Trap
This is where I've seen the most production bugs. JSON.stringify does its own escaping — it turns actual newline characters into the two-character sequence \n, and actual tab characters into \t. So a round-trip through JSON.stringify and JSON.parse is safe. But the failure mode is:
// Input: a string with a real newline
const text = "first line\nsecond line";
// What you want in JSON:
JSON.stringify(text) // → '"first line\\nsecond line"' ✓
// What breaks: building JSON manually without stringifying
const broken = `{"message": "${text}"}`;
// → {"message": "first line
// second line"} — actual newline, invalid JSON ✗
That broken second case produces invalid JSON because a raw newline inside a JSON string value is not allowed — the spec (RFC 8259 §7) requires it to be escaped as \n. The JavaScript String Escaper handles this for pasted content: switch to "JSON string" mode and it escapes all disallowed characters, including embedded newlines, carriage returns, and null bytes.
If you're also dealing with JavaScript-escaped strings that need to become proper Unicode sequences for other systems, the Unicode Escape Converter converts between literal text and \uXXXX / \u{...} / HTML numeric reference forms in both directions.
Quick Decision Guide
When you reach for an escape sequence, the choice usually reduces to:
- You want a newline / tab in output → use
\n/\tin a regular string literal - You're embedding a path, regex pattern, or LaTeX snippet → use
String.raw\…\ - You need an emoji or a non-BMP character → use
\u{1F600}(brace form) - You're building JSON by hand → don't; use
JSON.stringifyinstead - You inherited a codebase with
\uXXXX\uXXXXsurrogate pairs → convert to\u{...}brace form for readability
The escape sequences themselves haven't changed since ES5 for the basic set, and since ES2015 for the brace-form Unicode extensions. What changes is knowing which one to reach for and when the "automatic" escaping of JSON.stringify is your friend versus when you need to escape explicitly yourself.
Made by Toolora · Updated 2026-07-01