RFC 8259 section 7 requires escapes for only three things: the quotation mark, the backslash and the control characters U+0000 through U+001F. Everything else, including every emoji and non-Latin script, may appear as-is.
| Character | Escape | Required? |
|---|---|---|
Quotation mark " | \" | Yes, it would end the string |
Backslash \ | \\ | Yes, it starts every escape |
| Line feed, carriage return, tab | \n \r \t | Yes (control characters) |
| Backspace, form feed | \b \f | Yes (control characters) |
| Other U+0000–U+001F | \u0000 … \u001f | Yes, no short form exists |
Solidus / | \/ | No, optional |
| Any other character | \uXXXX | No, optional |
Characters outside the Basic Multilingual Plane are written as a UTF-16 surrogate pair, so
🚀 (U+1F680) escapes to 🚀. JSON has no \u{1F680}
syntax. A lone surrogate such as \ud800 is syntactically valid JSON but does
not represent real Unicode text. Since ES2019, JSON.stringify writes unpaired
surrogates as escapes so the output stays valid UTF-8. When unescaping produces one, this
tool warns you.