punycode.js

A robust Punycode converter for internationalized domain names, fully compliant with RFC 3492 and RFC 5891.

Library
npm
v2.3.1
1,690 stars
MIT License

Repository Health

Pre-computed score based on development activity, maintenance, community, maturity, and trend momentum. How we score it →
44 /100 Fair
Development Activity 0
Maintenance 20
Community 68
Maturity 60
Momentum 28

Technical Analysis

AI-assessed by reading the actual repository — architecture, code quality, innovation, and documentation. How we score it →
74 /100 Good
Architecture 70
Code Quality 80
Innovation 55
Learning Curve 90

Punycode.js is a JavaScript implementation of the Punycode algorithm, converting Unicode strings to and from the ASCII-only Punycode encoding used by internationalized domain names (IDN). It was built by comparing and optimizing several existing C and JavaScript Punycode implementations, resulting in an implementation that fully complies with RFC 3492 and RFC 5891, unlike several earlier ports.

The library exposes low-level encode/decode functions for raw Punycode strings as well as higher-level toASCII/toUnicode helpers that operate on entire domain names or email addresses, converting only the non-ASCII or Punycode-prefixed labels. It was bundled directly into Node.js core from v0.6.2 through v7 before being soft-deprecated and split back out into this standalone userland package.

What You Get

  • punycode.encode(string) / punycode.decode(string) for raw Unicode-to-ASCII Punycode conversion
  • punycode.toASCII(input) / punycode.toUnicode(input) that operate on full domain names or email addresses, converting only the relevant labels
  • A UCS-2 helper for safely handling astral (surrogate-pair) Unicode code points
  • Full RFC 3492 and RFC 5891 compliance, correcting bugs present in several earlier Punycode ports
  • Both a CommonJS build (punycode.js) and an ES module build (punycode.es6.js)
  • Zero runtime dependencies, working across modern Node.js and browser environments

Common Use Cases

  • Encoding an internationalized domain name (e.g. mañana.com) into its ASCII xn-- DNS-compatible form before a network request
  • Decoding a Punycode-encoded domain (xn--maana-pta.com) back to its human-readable Unicode form for display
  • Normalizing internationalized email addresses that mix Unicode local parts with Punycode domain parts
  • Replacing Node.js’s soft-deprecated built-in punycode core module with a maintained userland equivalent

Under The Hood

Architecture: The entire implementation lives in a single file, punycode.js (443 lines), organized around a small set of pure functions: low-level encode/decode implement the bootstring algorithm from RFC 3492 directly (variable-length integer encoding, adaptive bias, digit-to-basic mapping), while toASCII/toUnicode are thin wrappers that split a domain or email string on ./@ boundaries, apply encode/decode only to labels that need it (checking for the xn-- ACE prefix or non-ASCII characters), and rejoin the result. A small internal ucs2 helper (decode/encode) handles surrogate-pair-aware string-to-code-point conversion so the algorithm works correctly on astral Unicode characters, not just the BMP. Tech Stack: Pure JavaScript with zero runtime dependencies; ships both a CommonJS build (main: punycode.js) and an ES module build (module: punycode.es6.js) for dual consumption, targets Node.js >=6, and uses mocha/nyc/codecov for testing and coverage reporting during development. Code Quality: tests/tests.js (371 lines) exercises encode, decode, toASCII, toUnicode, and the ucs2 helpers against a broad matrix of fixtures including astral characters, mixed-case domains, and known-tricky RFC 3492 edge cases, giving strong confidence in correctness for an algorithm where subtle bugs are easy to introduce (the README explicitly documents that earlier ports, including Node.js’s own bundled version, were non-compliant). Development activity is low today (single maintainer, infrequent commits) but the code is algorithmically stable and rarely needs to change. API Design: The four-function surface (encode, decode, toASCII, toUnicode) maps directly onto how developers think about the problem — raw string transforms vs. domain/email-aware transforms — with no configuration objects or options to learn; the README’s copy-pasteable examples cover the entire API, and the explicit warning about Node’s core punycode module shadowing this package (requiring require('punycode/')) heads off the single most common integration gotcha directly in the docs.

Join founders buildingwith open source

Opinionated takes, migration guides, cost-saving tips, and insights from the open source ecosystem.

Subscribe on Substack
Join 750+ subscribers