JavaScript String toWellFormed() Method

Beginner
⏱️ 9 min read
📚 Updated: Jul 2026
🎯 5 Examples
🚀 5 Try-it labs
Instance method

What You’ll Learn

String.prototype.toWellFormed() returns a new string with every lone UTF-16 surrogate replaced by the Unicode replacement character U+FFFD () (same API as MDN String.prototype.toWellFormed()). Learn why ill-formed text breaks encodeURI(), how valid emoji stay intact, pairing with isWellFormed(), five examples, and try-it labs.

01

Kind

Instance method

02

Returns

New string

03

Parameters

None

04

Fixes

Lone surrogates

05

Mutates?

No

06

Baseline

Since Oct 2023

Introduction

JavaScript strings are stored as UTF-16. Characters above U+FFFF (including many emoji) need two code units called a surrogate pair. When only one half arrives, you get a lone surrogate — an ill-formed string.

toWellFormed() repairs that by swapping each orphan for U+FFFD (), the standard Unicode replacement character. Engines can do this efficiently against the internal string data.

💡
Beginner tip

Use isWellFormed() to detect problems and toWellFormed() to fix them before APIs like encodeURI().

This page is part of JavaScript String Methods. Related topics include isWellFormed() and normalize().

Understanding the toWellFormed() Method

str.toWellFormed() walks the UTF-16 code units and replaces any unpaired high or low surrogate with U+FFFD. Complete pairs (real emoji) stay as they are.

  • Returns a new string — always (even if already well-formed).
  • Takes no parameters.
  • Lone surrogates become (U+FFFD).
  • Does not change Unicode normalization — use normalize() for NFC/NFD.

📝 Syntax

General form of String.prototype.toWellFormed:

JavaScript
str.toWellFormed()

Parameters

  • None — this method does not accept arguments.

Return value

A new string that is a copy of this string, with all lone surrogates replaced with U+FFFD. If the string was already well-formed, you still get a new string with the same content.

Exceptions

None for normal string values.

Common patterns

JavaScript
"ab\uD800".toWellFormed();           // "ab�"
"abc".toWellFormed();                // "abc"
"ab\uD83D\uDE04c".toWellFormed();    // "ab😄c" (valid pair kept)
encodeURI(illFormed.toWellFormed()); // safe — no URIError

⚡ Quick Reference

GoalCode
Repair lone surrogatesstr.toWellFormed()
Only checkstr.isWellFormed()
Safe encodeURIencodeURI(str.toWellFormed())
Detect then fixif (!s.isWellFormed()) s = s.toWellFormed()
Unicode forms (NFC…)str.normalize()

🔍 At a Glance

Four facts to remember about String.toWellFormed().

Returns
string

Always a new string

Replaces
U+FFFD

Lone surrogates → �

Args
none

Empty call

Mutates
no

Immutable

📋 toWellFormed() vs isWellFormed()

toWellFormed()isWellFormed()
ReturnsNew stringboolean
RoleRepairDetect
Lone surrogatesBecome false
Well-formed inputSame text (new string)true
Best withencodeURI / sanitizeGuards / branching

Examples Gallery

Examples follow MDN String.toWellFormed() patterns. Use View Output or Try It Yourself for each case.

📚 Getting Started

MDN demo: lone surrogates vs well-formed text.

Example 1 — Basic toWellFormed()

MDN Using toWellFormed() demo — lone leading/trailing surrogates become .

JavaScript
const strings = [
  // Lone leading surrogate
  "ab\uD800",
  "ab\uD800c",
  // Lone trailing surrogate
  "\uDFFFab",
  "c\uDFFFab",
  // Well-formed
  "abc",
  "ab\uD83D\uDE04c",
];

for (const str of strings) {
  console.log(str.toWellFormed());
}
// "ab�"
// "ab�c"
// "�ab"
// "c�ab"
// "abc"
// "ab😄c"
Try It Yourself

How It Works

Orphan \uD800 / \uDFFF units are replaced. The complete emoji pair \uD83D\uDE04 stays as 😄.

Example 2 — Valid Pairs Are Kept

Well-formed emoji and ASCII pass through with the same visible text.

JavaScript
const emoji = "😄";
const fixed = emoji.toWellFormed();

console.log(fixed);                    // "😄"
console.log(fixed === emoji);          // true (same content)
console.log(fixed.isWellFormed());     // true
console.log("hello".toWellFormed());   // "hello"
Try It Yourself

How It Works

You still get a new string object/value from the engine’s point of view, but the characters match when the input was already well-formed.

🔧 Practical Patterns

encodeURI safety, detect-then-fix, and sliced emoji.

Example 3 — Avoid encodeURI() Errors

MDN demo: ill-formed URLs throw; repaired strings encode cleanly.

JavaScript
const illFormed = "https://example.com/search?q=\uD800";

try {
  encodeURI(illFormed);
} catch (e) {
  console.log(e.name);
  // URIError
}

console.log(encodeURI(illFormed.toWellFormed()));
// "https://example.com/search?q=%EF%BF%BD"
Try It Yourself

How It Works

U+FFFD encodes as UTF-8 bytes EF BF BD, shown as %EF%BF%BD in the URI.

Example 4 — Detect Then Repair

Pair with isWellFormed() when you only want to fix broken input.

JavaScript
function sanitize(text) {
  if (!text.isWellFormed()) {
    return text.toWellFormed();
  }
  return text;
}

const raw = "hi\uD800";
const safe = sanitize(raw);

console.log(raw.isWellFormed());   // false
console.log(safe.isWellFormed());  // true
console.log(safe);                 // "hi�"
Try It Yourself

How It Works

Checking first is optional — calling toWellFormed() on clean text is also fine. The branch is useful when you want to log or count repairs.

Example 5 — Sliced Emoji Can Break

Taking one UTF-16 unit from an emoji creates a lone surrogate — repair it.

JavaScript
const smile = "😀";
const half = smile.slice(0, 1);

console.log(half.isWellFormed());           // false
console.log(half.toWellFormed());           // "�"
console.log(half.toWellFormed().isWellFormed()); // true
Try It Yourself

How It Works

Prefer codePointAt / for...of over index math when cutting Unicode text. When halves slip through, toWellFormed() cleans them up.

🎯 Common Use Cases

  • Safe URIsencodeURI(str.toWellFormed()) before networking.
  • Sanitize imports — text from buffers, files, or cut-and-paste bugs.
  • Detect + repair pipelines — pair with isWellFormed().
  • Not for NFC/NFD — use normalize() for composed vs decomposed forms.
  • Not everyday English — normal literals rarely need this.

🧠 How toWellFormed() Works

1

Scan UTF-16 units

Walk each code unit in the string.

Scan
2

Keep valid pairs

High + low surrogates that form a real character stay intact.

Keep
3

Replace orphans

Lone high or low surrogates become U+FFFD (�).

Fix
4

Return a new string

Original text is never mutated.

📝 Notes

  • Always returns a new string, even when nothing needed fixing.
  • Same replacement character many APIs use automatically (for example TextEncoder).
  • Slicing mid-emoji with slice / charAt is a common source of lone surrogates.
  • Not the same as normalize() — that changes Unicode forms, not surrogates.
  • Baseline widely available since October 2023 — polyfill older runtimes if required.

Browser & Runtime Support

String.prototype.toWellFormed() is Baseline Widely available — supported across modern browsers since October 2023.

Baseline · Widely available

String.toWellFormed()

Safe for current production browsers. Pair with isWellFormed() to detect, then repair before encodeURI or other strict APIs.

Modern Widely available
Google Chrome Supported · Desktop & Mobile
Full support
Mozilla Firefox Supported · Desktop & Mobile
Full support
Apple Safari Supported · macOS & iOS
Full support
Microsoft Edge Supported · Chromium
Full support
Internet Explorer No native support · Use a polyfill
Polyfill
Opera Supported · Modern versions
Full support
Samsung Internet Supported · Android
Full support
Bun Supported · JavaScript runtime
Supported
Deno Supported · JavaScript runtime
Supported
Node.js Supported · Server runtime
Supported
Android WebView Supported · Modern WebView
Full support
toWellFormed() Excellent

Bottom line: Use String.toWellFormed() to replace lone UTF-16 surrogates with U+FFFD. Check with isWellFormed() when you only need a boolean.

Conclusion

String.prototype.toWellFormed() is the built-in way to sanitize ill-formed UTF-16 — swap lone surrogates for so APIs like encodeURI stay happy. Pair it with isWellFormed() for detect-then-fix flows.

Continue with isWellFormed(), normalize(), or the String methods hub.

💡 Best Practices

✅ Do

  • Call toWellFormed() before encodeURI on untrusted text
  • Pair with isWellFormed() when you need a check first
  • Prefer code-point-aware APIs when slicing Unicode
  • Treat the result as a new string
  • Polyfill if you must support pre-2023 engines

❌ Don’t

  • Expect it to fix NFC/NFD — that is normalize()
  • Assume slice(0, 1) on emoji stays well-formed
  • Forget it always allocates a new string
  • Confuse with “delete the character”
  • Skip sanitizing binary/network text that may be cut mid-pair

Key Takeaways

Knowledge Unlocked

Five things to remember about String.toWellFormed()

Lone surrogates become U+FFFD — original unchanged.

5
Core concepts
🛠 02

Fixes

lone surrogates

Role
03

Replace

U+FFFD

Char
🔍 04

Pair

isWellFormed()

Detect
05

Mutates

no

Immutable

❓ Frequently Asked Questions

String.prototype.toWellFormed() returns a new string where every lone UTF-16 surrogate is replaced with the Unicode replacement character U+FFFD (�). Well-formed strings are copied unchanged in content.
UTF-16 uses pairs of code units for characters above U+FFFF. A lone (orphan) surrogate is one half without its matching partner — for example "\uD800" alone.
No. Call it with empty parentheses: str.toWellFormed().
isWellFormed() returns true or false. toWellFormed() returns a repaired string. Check with isWellFormed(); fix with toWellFormed().
encodeURI throws URIError on ill-formed strings. Passing str.toWellFormed() replaces lone surrogates first so encoding succeeds (replacement becomes %EF%BF%BD).
No. Strings are immutable. It always returns a new string — even when the input was already well-formed.
Did you know?

When ill-formed strings reach APIs like TextEncoder, they are often converted with the same U+FFFD replacement. toWellFormed() lets you do that step explicitly — and more efficiently than a hand-written loop.

More String Methods

Return to the hub for slice, trim, replace, and more.

String methods hub →

About the author

Mari Selvan M P
Mari Selvan M P 🔗

Developer, cloud engineer, and technical writer

  • Experience 12 years building web and cloud systems
  • Focus Full Stack Development, AWS, and Developer Education

I write practical tutorials so students and working developers can learn by doing—from databases and APIs to deployment on AWS.

8 people found this page helpful