Grapheme clustering
Grapheme Clustering (Mode 2027) is a DECSET/DECRST mode that switches the terminal from legacy wcwidth-based character width calculation to Unicode grapheme cluster segmentation as defined by UAX #29.
Syntax
CSI ? 2027 h DECSET — Enable grapheme clustering
CSI ? 2027 l DECRST — Disable grapheme clustering (use legacy wcwidth)
DECSET-mode2027 = 0x1b, "[", "?", "2", "0", "2", "7", "h" ; DECRST-mode2027 = 0x1b, "[", "?", "2", "0", "2", "7", "l" ;
Description
Mode 2027 controls how the terminal determines the display width of characters and character sequences. This directly affects the rendering of emoji, combining characters, and other complex Unicode sequences.
- Set — The terminal uses Unicode grapheme cluster segmentation (UAX #29) to determine character boundaries and widths. Multi-codepoint sequences that form a single grapheme cluster occupy the expected number of cells.
- Reset — The terminal uses the legacy
wcwidth/wcswidthapproach, evaluating each codepoint individually. This is the default.
The problem with legacy width calculation
The traditional wcwidth function assigns a width (1 or 2 cells) to each individual Unicode codepoint. This fails for modern Unicode in several ways:
- Emoji sequences — A family emoji like
\U0001F468\u200D\U0001F469\u200D\U0001F467(joined with Zero-Width Joiners) should display as a single glyph occupying 2 cells, butwcwidthcounts each codepoint separately. - Combining characters — A base character followed by combining marks (e.g.,
e\u0301for "e with acute accent") should occupy 1 cell, but naive processing may allocate extra space. - Flag sequences — Regional indicator pairs (e.g.,
\U0001F1FA\U0001F1F8for a US flag) should be a single 2-cell glyph. - Variation selectors —
\u2764\uFE0F(red heart with emoji presentation selector) should be 2 cells wide, not 1.
Grapheme cluster segmentation
When Mode 2027 is enabled, the terminal applies UAX #29 rules to identify grapheme cluster boundaries, then determines the width of each cluster as a whole. This means:
- Emoji ZWJ sequences are treated as single glyphs
- Combining character sequences are properly grouped
- Regional indicator pairs form single flag glyphs
- Variation selectors modify the width of the preceding character
Application considerations
Applications that enable Mode 2027 must also use grapheme-cluster-aware width calculations internally. If the application uses wcwidth while the terminal uses grapheme clustering (or vice versa), the cursor position will drift, causing display corruption.
Mode 2027 is a relatively recent proposal. Applications should query the terminal's support for this mode before enabling it, using DECRPM (Mode 2027 query) or checking the terminal's reported capabilities.
Examples
printf '\e[?2027h' # Enable grapheme clustering
printf '\e[?2027l' # Disable grapheme clustering
# With Mode 2027 enabled, this emoji sequence occupies 2 cells:
printf '\U0001F468\u200D\U0001F469\u200D\U0001F467'
# Query whether Mode 2027 is supported (DECRPM):
printf '\e[?2027$p'
# Response: CSI ? 2027 ; Ps $ y
# Ps=1: set, Ps=2: reset, Ps=0: not recognized
Specifications
| Specification | Section |
|---|---|
| Unicode UAX #29 | — |
Terminal support
| Terminal | Support | Version | Notes |
|---|---|---|---|
| Terminal Emulators | |||
| Alacritty | ✗ | No | |
| Bobcat | ? | ? | Not explicitly configured in Bobcat. TerminalCtrl may support this, but cannot confirm from Bobcat's code. |
| contour | ✓ | v0.3.4.223 | Implemented as DECMode::Unicode at primitives.h:716 |
| foot | ✓ | 1.16.0 | Requires compile flag |
| Ghostty | ✓ | v1.0.0 | |
| iTerm2 | ✗ | No | |
| Kitty | ✓ | Yes | Always active, not toggleable via mode |
| Konsole | ✗ | No | |
| mintty | ✗ | No | Mode 2027 is used for emoji width mode, not grapheme clustering; comment at src/termout.c:2628 says '2027 is dropped' |
| mlterm | ✗ | No | |
| PuTTY | ✗ | No | |
| Rio | ✗ | No | |
| rxvt-unicode | ✗ | No | |
| st | ✗ | No | |
| terminology | ✗ | No | |
| VT100 | ✗ | No | |
| VTE | ✗ | No | Mode defined but not implemented |
| WezTerm | ✓ | Yes | Permanently enabled |
| Windows Terminal | ✓ | Yes | GCM_GraphemeClusterMode at adaptDispatch.cpp:1886-1887, reported as permanently enabled/disabled at adaptDispatch.cpp:2027-2029 |
| xterm | ✗ | No | |
| xterm.js | ✗ | No | Grapheme clustering exists internally but no mode toggle |
| Multiplexers | |||
| cy | ✗ | No | |
| GNU Screen | ✗ | No | |
| tmux | ✗ | No | |
| tuios | ✗ | No | |
| Zellij | ✗ | No | Code comment at grid.rs:1489 explicitly mentions that grapheme segmentation is broken |
See also
- Color Scheme Reporting — Mode 2031 — Another modern terminal capability mode
- SGR Mouse Encoding — Mode 1006 — Extended encoding for modern terminals