JavaScript String isWellFormed() Method

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

What You’ll Learn

String.prototype.isWellFormed() returns whether a string has any lone UTF-16 surrogates (same API as MDN String.prototype.isWellFormed()). Learn well-formed vs broken pairs, emoji that pass the check, guarding encodeURI(), pairing with toWellFormed(), five examples, and try-it labs.

01

Kind

Instance method

02

Returns

true / false

03

Parameters

None

04

Checks

Lone surrogates

05

Mutates?

No

06

Baseline

Widely available

Introduction

JavaScript strings are stored as UTF-16. Characters above U+FFFF (including many emoji) need two code units called a surrogate pair: a high (leading) surrogate plus a low (trailing) one.

Sometimes data arrives with only one half of that pair — a lone surrogate. That string is ill-formed. It may still look odd in the console, but APIs like encodeURI() can throw a URIError when they see it.

💡
Beginner tip

Everyday text like "hello" and complete emoji like "😄" are well-formed. You mainly need isWellFormed() when text comes from binary buffers, cut-and-paste bugs, or other low-level sources.

This page is part of JavaScript String Methods. Related topics include toWellFormed() and codePointAt().

Understanding the isWellFormed() Method

isWellFormed() scans the string for unpaired surrogates. Engines can do this efficiently against the internal UTF-16 data — faster and clearer than a hand-written loop.

  • No parameters — just call str.isWellFormed().
  • Returns true when every surrogate is part of a valid pair (or none exist).
  • Returns false when any lone high or low surrogate is present.
  • Does not fix the string — use toWellFormed() when you need a repaired copy.

📝 Syntax

General form of String.prototype.isWellFormed:

JavaScript
str.isWellFormed()

Parameters

  • None — this method does not accept arguments.

Return value

A boolean. Special cases:

SituationReturns
Normal text (e.g. "abc", empty "")true
Valid surrogate pair (e.g. emoji "😄")true
Lone leading surrogate (e.g. "ab\uD800")false
Lone trailing surrogate (e.g. "\uDFFFab")false
Mixed text with any lone surrogate insidefalse

Common patterns

JavaScript
"hello".isWellFormed();           // true
"\uD800".isWellFormed();          // false  (lone high surrogate)
"😄".isWellFormed();              // true   (valid pair)

if (!text.isWellFormed()) {
  console.warn("Ill-formed UTF-16");
  text = text.toWellFormed();     // replace lone surrogates with U+FFFD
}

⚡ Quick Reference

GoalCode
Check well-formed UTF-16str.isWellFormed()
Guard encodeURIif (str.isWellFormed()) encodeURI(str)
Repair lone surrogatesstr.toWellFormed()
Reject bad inputif (!str.isWellFormed()) throw ...
Empty string"".isWellFormed()true
Valid emoji"😀".isWellFormed()true

🔍 At a Glance

Four facts to remember about String.isWellFormed().

Returns
boolean

true if no lone surrogates

Parameters
none

Empty call only

Emoji pairs
true

Complete pairs pass

Mutates
no

Strings stay immutable

📋 isWellFormed() vs toWellFormed()

isWellFormed()toWellFormed()
PurposeDetect lone surrogatesReplace them with U+FFFD
Result typeBooleanNew string
Original stringUnchangedUnchanged (returns a copy)
Best forValidate / branch / warnSanitize before encode or display
Typical comboif (!s.isWellFormed()) ...encodeURI(s.toWellFormed())

Examples Gallery

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

📚 Getting Started

See true vs false for common cases.

Example 1 — Basic isWellFormed() Checks

MDN-style loop: lone surrogates fail; normal text and valid pairs pass.

JavaScript
const strings = [
  "ab\uD800",           // lone leading surrogate
  "ab\uD800c",
  "\uDFFFab",           // lone trailing surrogate
  "c\uDFFFab",
  "abc",                // well-formed
  "ab\uD83D\uDE04c",    // well-formed (😄 pair)
];

for (const str of strings) {
  console.log(str.isWellFormed());
}
// false, false, false, false, true, true
Try It Yourself

How It Works

Any unpaired \uD800\uDBFF (high) or \uDC00\uDFFF (low) makes the whole string fail. "abc" and the emoji escape \uD83D\uDE04 are complete and pass.

Example 2 — Lone High vs Lone Low Surrogates

Both halves can be orphans — either one makes isWellFormed() return false.

JavaScript
const highOnly = "\uD800";   // high surrogate alone
const lowOnly = "\uDFFF";    // low surrogate alone
const pair = "\uD83D\uDE00"; // 😀

console.log(highOnly.isWellFormed()); // false
console.log(lowOnly.isWellFormed());  // false
console.log(pair.isWellFormed());     // true

console.log(highOnly.length); // 1
console.log(pair.length);     // 2
Try It Yourself

How It Works

length counts UTF-16 units, so a lone surrogate has length 1 and a complete emoji has length 2. Well-formedness is about pairing, not about length alone.

📈 Practical Patterns

Emoji that pass, encodeURI safety, and repairing text.

Example 3 — Everyday Text and Emoji Pass

Normal strings and complete emoji are well-formed — no special escaping needed.

JavaScript
console.log("".isWellFormed());           // true
console.log("CodeToFun".isWellFormed());  // true
console.log("你好".isWellFormed());         // true
console.log("😀🎉".isWellFormed());       // true

// Broken: cut the emoji in half by taking only the first unit
const broken = "😀".charAt(0);
console.log(broken.isWellFormed());       // false
console.log(broken.charCodeAt(0));        // 55357 (high surrogate)
Try It Yourself

How It Works

charAt(0) on an emoji returns only the high surrogate — a classic way to create ill-formed text by accident. Prefer for...of or codePointAt(0) when working with emoji.

Example 4 — Avoid encodeURI() Errors

MDN pattern: test before encoding so you do not catch a URIError.

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

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

if (illFormed.isWellFormed()) {
  console.log(encodeURI(illFormed));
} else {
  console.warn("Ill-formed strings encountered.");
}
Try It Yourself

How It Works

encodeURI (and related URI helpers) require well-formed UTF-16. Checking first lets you warn, reject, or repair instead of relying on try/catch.

Example 5 — Detect Then Repair with toWellFormed()

When you must keep going, replace lone surrogates with the replacement character.

JavaScript
const raw = "Hi \uD800 there";

console.log(raw.isWellFormed()); // false

const safe = raw.toWellFormed();
console.log(safe.isWellFormed()); // true
console.log(safe);                // "Hi � there"

console.log(encodeURI(safe));     // works — no URIError
Try It Yourself

How It Works

toWellFormed() returns a new string. Lone surrogates become U+FFFD (). The original raw is unchanged.

🚀 Common Use Cases

  • URI encoding — check before encodeURI / encodeURIComponent.
  • Input validation — reject or flag text that arrived with broken surrogates.
  • Binary / buffer decoding — verify strings built from raw UTF-16 units.
  • Logging & APIs — avoid sending ill-formed payloads to systems that crash on them.
  • Sanitize pipelineisWellFormed() to decide; toWellFormed() to fix.
  • Debugging emoji bugs — catch accidental charAt / slice cuts on pairs.

🧠 How isWellFormed() Checks a String

1

Read UTF-16 units

Walk the string’s internal code units from start to end.

Scan
2

Match surrogate pairs

A high unit must be followed by a low unit; a low unit must follow a high.

Pairs
3

Spot orphans

Any unpaired high or low surrogate means the string is ill-formed.

Detect
4

Return true or false

true if every unit is fine; false if any lone surrogate exists.

📝 Notes

  • Takes no parameters — always call isWellFormed() with empty parentheses.
  • Valid emoji and other supplementary characters use paired surrogates and return true.
  • Slicing or charAt in the middle of an emoji can create ill-formed strings.
  • This is not the same as Unicode normalization (normalize()) — that deals with composed vs decomposed forms.
  • Prefer the built-in method over a custom surrogate loop — engines optimize the check.
  • Baseline widely available since October 2023 — polyfill if you must support older runtimes.

Browser & Runtime Support

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

Baseline · Widely available

String.isWellFormed()

Safe for current production browsers. Pair with toWellFormed() when you need to sanitize ill-formed text.

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
isWellFormed() Excellent

Bottom line: Use String.isWellFormed() to detect lone UTF-16 surrogates before encodeURI or other strict APIs. Repair with toWellFormed() when you need a safe string.

Conclusion

String.prototype.isWellFormed() returns true when a string has no lone UTF-16 surrogates, and false otherwise. Use it to validate text, avoid URIError from encodeURI, and decide when to call toWellFormed().

Continue with codePointAt(), indexOf(), or the String methods hub.

💡 Best Practices

✅ Do

  • Call isWellFormed() before encodeURI on untrusted text
  • Use toWellFormed() when you need a repaired copy
  • Iterate emoji with for...of to avoid cutting pairs
  • Treat false as a signal to reject, warn, or sanitize
  • Prefer the built-in check over hand-rolled surrogate logic

❌ Don’t

  • Pass arguments — the method takes none
  • Assume every string from a buffer is well-formed
  • Use charAt / fixed-width slices on emoji without checking
  • Confuse this with normalize() (different problem)
  • Forget older environments may need a polyfill

Key Takeaways

Knowledge Unlocked

Five things to remember about String.isWellFormed()

Boolean check for lone UTF-16 surrogates — pair with toWellFormed() to repair.

5
Core concepts
🔀 02

Detects

lone surrogates

UTF-16
😀 03

Emoji

complete pairs pass

Unicode
🔗 04

Guard

encodeURI safely

URI
🛠 05

Repair

toWellFormed()

Sanitize

❓ Frequently Asked Questions

String.prototype.isWellFormed() returns true if the string has no lone UTF-16 surrogates, and false if it contains any unpaired high or low surrogate.
UTF-16 uses pairs of code units for characters above U+FFFF (like many emoji). A lone (orphan) surrogate is one half of that pair without its matching partner — for example "\uD800" alone.
No. Call it with empty parentheses: str.isWellFormed(). There is nothing to configure.
Before APIs that reject ill-formed strings (such as encodeURI()), when validating text from binary or network sources, or when you want to treat broken surrogate data differently from normal text.
isWellFormed() only checks and returns a boolean. toWellFormed() returns a new string where lone surrogates are replaced with the Unicode replacement character (U+FFFD).
No. Strings are immutable. isWellFormed() only reads and returns true or false.
Did you know?

Lone surrogates often appear when code treats strings as arrays of fixed-width units — for example "😀"[0] or str.slice(0, 1) on an emoji. Those operations return one UTF-16 unit, which is why isWellFormed() suddenly returns false.

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