# Twemoji: Unicode emoji as images, and what the library actually does

> Twemoji replaces emoji characters in a page with image tags pointing at PNG or SVG assets, using the Unicode code point as the file name. The library is small and MIT licensed; the artwork is a separate licence question, and the default CDN in the README is dead.

**twitter/twemoji** — Emoji for everyone. https://twemoji.twitter.com/

- Repository: https://github.com/twitter/twemoji
- Stars: 17,794 · Forks: 1,900
- Language: HTML
- License: MIT
- Published: 2026-09-21 · Updated: 2026-09-21 · Language: en
- Canonical page: https://hysenlabs.com/projects/twitter-twemoji

## What Twemoji solves, and who ends up using it

Emoji rendering is a font problem disguised as a text problem. The same code point draws differently on iOS, Android, Windows and Linux, and on some systems it draws as a blank box. Twemoji sidesteps font availability entirely: it finds emoji characters in a document and replaces them with image tags whose src is built from the Unicode code point. The README describes it as "A simple library that provides standard Unicode emoji support across all platforms" and states that Twemoji v14.0 adheres to the Unicode 14.0 spec and supports the Emoji 14.0 spec. It also states plainly that custom emoji are not supported.

The audience is therefore narrow and specific. Anyone who needs a comment thread, a chat transcript or a user profile to look the same on every device. Anyone who wants emoji to inherit the surrounding font size rather than the platform's emoji font metrics. Anyone rendering emoji in a context where the system font is unknown or missing, such as a screenshot service or a headless browser. If your only requirement is that emoji appear at all, the platform font already does that and you do not need this library.

## How DOM parsing rewrites a page without touching innerHTML

The README recommends DOM parsing over string parsing, and the reason it gives is security: string parsing "does not sanitize the string or otherwise prevent malicious code from being executed; such sanitization is out of scope." That is an unusually direct statement of a limitation, and it should shape how you call the library.

In DOM mode the first argument to twemoji.parse is an HTMLElement. The README says generated image tags replace emoji that are inside #text nodes only, without compromising surrounding nodes or listeners, and completely avoiding the usage of innerHTML. The README acknowledges the cost: DOM operations are "inevitably costly", so this is the safest option with a slight performance penalty.

The output shape is documented with a worked example. Given a div whose textContent is 'I \u2764\uFE0F emoji!', after twemoji.parse(document.body) the README shows that the div is preserved, img.parentNode === div is true, img.alt holds the original character sequence, img.className is 'emoji', and img.draggable is false. The src in that example is https://twemoji.maxcdn.com/v/latest/72x72/2764.png, which is the old default base and no longer resolves.

The optional object passed to parse controls the rewrite: callback generates the src, attributes returns extra attributes, base sets the URL prefix, ext sets the file extension, className sets the class on each generated image, size selects an asset directory such as 72x72, and folder overrides size entirely. Setting folder to 'svg' with ext '.svg' produces URLs like https://twemoji.maxcdn.com/svg/2764.svg instead of a sized PNG. Because base defaults to the same value as twemoji.base, changing the global changes the default for every subsequent parse call.

## Installing Twemoji and parsing a first node

The README gives two routes. The CDN route puts a script tag in the head of your HTML document. The README notes that MaxCDN, the original provider, has shut down, and points at the unpkg tag as the replacement. The unpkg URL below is copied from the README and always serves the latest version.

```html
<script src="https://unpkg.com/twemoji@latest/dist/twemoji.min.js" crossorigin="anonymous"></script>
```

If you prefer to pin the version, the README gives an explicit tag with a subresource integrity hash for 14.0.3. Note that the version in this snippet comes from the README, while package.json lists 14.0.3 as the current version.

```html
<script src="https://unpkg.com/twemoji@14.0.3/dist/twemoji.min.js" integrity="sha384-eoGiwFCoIsUzdZGbvJ/7h/ICofqh5LolgoDnsgdbLptvnpK4+/swGDdkv3sb6bq+" crossorigin="anonymous"></script>
```

For a bundled build, package.json names the package twemoji and publishes dist/twemoji.npm.js as main, dist/twemoji.esm.js as module, and dist/twemoji.min.js as unpkg, with index.d.ts for TypeScript. The README's other route is to download a specific version from the gh-pages branch, where built assets for both current and older versions live.

Once the script is loaded, parsing a node is one call. The README's example creates a div, sets its textContent, appends it, and parses the body.

```js
var div = document.createElement('div');
div.textContent = 'I \u2764\uFE0F emoji!';
document.body.appendChild(div);

twemoji.parse(document.body);

var img = div.querySelector('img');
img.parentNode === div; // true
img.src;        // https://twemoji.maxcdn.com/v/latest/72x72/2764.png
img.alt;        // \u2764\uFE0F
img.className;  // emoji
img.draggable;  // false
```

What you should see is an img element inside the div, with the div still the parent and the alt text holding the original character. The src in the README points at MaxCDN, so in practice you will see a broken image until you set base to a host you control or to a working CDN. Two supporting details from the README matter here: the document must declare UTF-8 with a meta charset tag, and emoji will render at the wrong size until you add the CSS rule below to your stylesheet.

```css
img.emoji {
   height: 1em;
   width: 1em;
   margin: 0 .05em 0 .1em;
   vertical-align: -0.1em;
}
```

That rule makes each emoji take its width and height from the font-size of the surrounding text, adds a small amount of space on either side, and pulls it up slightly for optical alignment. Without it the images render at their intrinsic pixel size and break line height.

## The default CDN is gone, and the README says so in a strikethrough

The most consequential thing about adopting Twemoji today is not the API. It is that the README's own defaults point at a host that no longer exists. The CDN paragraph is struck through, with the note that MaxCDN is shut down and a link to issue 580. Yet the object documentation still lists base as "default MaxCDN", and the worked DOM example still shows a twemoji.maxcdn.com URL. The two halves of the README disagree with each other.

The practical consequence: every code sample that relies on the default base produces broken images. You have to set base explicitly, either to your own asset host or to a third-party CDN, and the README does not name a replacement beyond the unpkg script tag for the JavaScript itself. The unpkg tag ships the library, not the PNG and SVG assets. If you follow the README literally and only add the script tag, emoji will be replaced by image tags that 404.

This is a documentation debt problem rather than a code problem, but it changes the install story from one line to two decisions: where the JavaScript comes from, and where the images come from. The second decision is the one the README leaves open.

## String parsing, code point conversion, and excluding characters

The README documents a second parsing mode, string parsing, and explicitly does not recommend it. The reasoning is stated: it does not sanitize the string or prevent malicious code execution, and sanitization is out of scope for the library. If you are parsing user input, that sentence should end the discussion. Use DOM parsing, or sanitize upstream and accept that you own that problem.

Two conversion helpers are exposed. twemoji.convert.fromCodePoint takes a hex code point and returns UTF-16 surrogate pairs; the README's example passes '1f1e8' and gets back "\ud83c\udde8". twemoji.convert.toCodePoint goes the other way, turning surrogate pairs into a hyphen-joined hex string, and accepts a separator as a second argument: passing '\ud83c\udde8\ud83c\uddf3' returns "1f1e8-1f1f3", and the same input with '~' returns "1f1e8~1f1f3". Those are useful if you are generating asset paths yourself rather than letting the default callback build them.

Exclusion is handled through the callback. The README shows calling twemoji.parse with a callback that switches on the icon code point and returns false for specific ones, with 'a9' (copyright) and 'ae' (registered trademark) as the first two cases. Returning false leaves that character as text. This is the mechanism to reach for if you want most emoji replaced but symbols like the copyright sign left alone, which matters because those code points appear in ordinary prose far more often than a smiley does.

## What Twemoji is not, and where another approach fits better

Twemoji does not ship a font. Nothing in the README or package.json describes a font file, and the asset folders referenced in the examples hold PNG and SVG images. If your requirement is a font, you are looking at a different kind of project, and the search traffic around "twemoji font" and "twemoji ttf" reflects a mismatch between what people expect and what this repository publishes.

The nearest alternative in the same problem space is Noto Emoji, which Google distributes as a font. The difference in approach is structural rather than cosmetic. A font is installed on the system or loaded as a webfont and the browser renders emoji through normal text layout: no DOM rewriting, no image requests, no per-emoji markup, and text selection and copy behave normally. Twemoji produces images, which means each emoji is a separate HTTP request unless you configure a sprite or inline them, the markup grows, and accessibility depends on the alt attribute being set correctly, which the README's example shows it is. In exchange, image assets give you pixel-identical output regardless of the platform's font stack, and you control exactly which artwork appears.

That trade-off is the whole decision. If your pages are rendered in browsers with modern emoji fonts and you only care about consistency of style, a font is less machinery. If you are rendering to a canvas, taking screenshots, or need the same glyph everywhere including environments with no emoji font, images are the only reliable option, and that is where Twemoji sits.

## Maintenance, licensing, and the cost of upgrading

The repository is not archived, and the last push was on 2026-07-07. That is recent enough to say the repository is still receiving commits, but the release history tells a different story about the library itself: the three most recent releases are v14.0.0, v14.0.1 and v14.0.2, all dated 2022-03-31. package.json carries version 14.0.3 and the README's pinned CDN snippet references 14.0.3 as well, so there is a gap between the tagged releases and the version in the working tree. Anyone tracking Twemoji by watching releases rather than commits will see nothing after March 2022.

The upgrade cost is driven by the Unicode version, not by API churn. The README ties v14.0 to Unicode 14.0 and Emoji 14.0. When Unicode adds emoji, the asset set has to grow and the parser data has to follow, which is why twemoji-parser is pinned to 14.0.0 as a dependency. If you host the assets yourself, a version bump means re-syncing your asset directory and updating the base URL or the pinned script tag. The API surface documented in the README is small and stable: one parse function with a handful of options, two conversion helpers, and three global defaults.

Licensing is split, and the split is the part worth reading carefully. package.json declares the license as an array of MIT and CC-BY-4.0, and the repository root contains both LICENSE and LICENSE-GRAPHICS. The code is MIT. The artwork is under a separate licence, which is what LICENSE-GRAPHICS is for, and it carries an attribution requirement that MIT does not. If you redistribute the images, the graphics licence is the one that applies to them, not the code licence. This is not legal advice; read both files before shipping, and note that the README itself does not summarize the split.

## Conclusion

Adopt Twemoji when you need identical emoji rendering across platforms and can host the assets yourself, since the README's default MaxCDN base is dead and the unpkg snippet is the documented replacement. Do not adopt it if you need custom emoji, since the README states custom emoji are not supported, or if you want a font rather than images. Before wiring it in, verify that your chosen base URL actually serves the files, check the img.emoji CSS rule against your own stylesheet, and read LICENSE-GRAPHICS separately from LICENSE because the two cover different things.

## FAQ

### What is Twemoji?

It is a JavaScript library that provides standard Unicode emoji support across all platforms by replacing emoji characters in a document with image tags. The README states that Twemoji v14.0 adheres to the Unicode 14.0 spec and supports the Emoji 14.0 spec.

### How do I use Twemoji in a page?

Load the script from unpkg in the head of your HTML document, then call twemoji.parse with an HTMLElement as the first argument. The README recommends DOM parsing over string parsing because string parsing does not sanitize input.

### How do I install Twemoji?

The README gives two routes: a script tag pointing at unpkg, or downloading built assets from the gh-pages branch. For a bundled build, package.json publishes dist/twemoji.npm.js, dist/twemoji.esm.js and dist/twemoji.min.js.

### Is Twemoji free to use?

package.json declares the license as MIT and CC-BY-4.0, and the repository root contains both LICENSE and LICENSE-GRAPHICS. The code and the artwork are covered by different files, so read both before redistributing the images.

### Is Twemoji open source?

Yes. The repository is public, the primary language listed is HTML, and package.json declares MIT and CC-BY-4.0 in its license field.

## Sources

- [Issues](https://github.com/twitter/twemoji/issues)
- [License: MIT](https://github.com/twitter/twemoji/blob/master/LICENSE)
- [README](https://github.com/twitter/twemoji/blob/master/README.md)
- [Releases](https://github.com/twitter/twemoji/releases)
- [twitter/twemoji on GitHub](https://github.com/twitter/twemoji)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/twitter-twemoji
