Emacs 27 gained support for Cairo for font rendering and this is supposed to let me render emojis but I can't find out how to make it work. I just about managed to set up some fontsets but the emoji which are made out of multiple characters don't work. Is it possible to make them work?

2 Answers 2


You need to use a font with support for that. This is what I do:

(if (>= emacs-major-version 27)
    (set-fontset-font t '(#x1f000 . #x1faff)
              (font-spec :family "Noto Color Emoji")))
  • Does this work for country flags or skin tone modified emoji? Commented Dec 11, 2020 at 22:52
  • No, but it's simple. Ah, now I see that the question explicitly says "which are made out of multiple characters". I missed that, thinking it was a question just about getting emojis to work at all.
    – pst
    Commented Dec 11, 2020 at 23:27
  • 1
    I just wanted plain emoticons. And this worked. +1! But I think your range is too wide; following en.wikipedia.org/wiki/Unicode_block I changed it to (set-fontset-font t '(#x1f600 . #x1f64f) Most of the other blocks dont seem to work. Ive not done any special setup of Noto Color Emoji
    – Rusi
    Commented Feb 17, 2022 at 7:03

Step 1 is to make sure that you have Cairo and Harfbuzz enabled. You can run this elisp and check the results to test this:

(featurep 'cairo) ; should evaluate to t
(frame-parameter (selected-frame) 'font-backend) ; should be a list starting with ftcrhb

I'm not sure about Windows or MacOS support. I think for Windows, you possibly need harfbuzz as the font backend and for MacOS you don't get any choice and I think things may work automatically.

Step 2 is to ensure you have a font which supports emoji, and that font is used for emoji by your fontset. I did this with an Emacs lisp function:

(defun init-my-font ()
    :family "<your default font>"
    :size 10.3
    :weight 'normal
    :width 'normal
    :slant 'normal))
  (set-face-attribute 'default nil :height 103)
  ;; emoji font
   t 'symbol
    :family "Noto Color Emoji"
    :size 10.3
    :weight 'normal
    :width 'normal
    :slant 'normal))
  ;; fallback font
   t nil
    :family "DejaVu Sans Mono"
    :size 10.3
    :weight 'normal
    :width 'normal
    :slant 'normal)))

If you use Emacs without a daemon, I think it is sufficient to call this function in your init file by writing (init-my-font). But I use a daemon and it seems to require that you wait until you have a gui frame before setting up the fonts. I have this hook for that:

 (let (done)
   (lambda ()
     (unless done
       ;; still set done to true even if we hit a bug (otherwise we
       ;; can never open a frame to see the problem)
       (setq done t)

Note that this snippet requires lexical scoping in your init file.

This should cause some emoji to be displayed but not the โ€œligaturesโ€ made of multiple emoji, for example country flags, skin tone modifiers, or family emoji. To support them, we need to specify what the sequences start with, a regex for the rest of the sequence, and an instruction for how Emacs should figure out which glyphs to draw. For the instruction we use font-shape-gstring, which tells Emacs to ask the font how to combine things (but the problem is knowing how Emacs decides which font from the fontset to use. I'm not really sure but I think it picks the font for the first character.) This config catches some things that won't form ligatures however that isn't a problemโ€”they just get shaped into multiple glyphs as they normally would.

(require 'cl)
;; setting up composition functions for emoji modifiers
(dolist (items `(((?๐Ÿ‡ฆ . ?๐Ÿ‡ฟ) [".[๐Ÿ‡ฆ-๐Ÿ‡ฟ]+" 0 font-shape-gstring])
                 ((?๐Ÿณ . ?๐Ÿด) [".[๏ธโ€๐ŸŒˆโšงโ˜ ๓ € -๓ ฟ]*" 0 font-shape-gstring])
                 (?โƒฃ ["[#*0-9]๏ธโƒฃ" 2 font-shape-gstring])
                 ;; TODO: I can't make keycap sequences work because I
                 ;; think they're trying to shape with the wrong font.
                 ,@(mapcar (lambda (range) (list range [".โ€?[๐Ÿป-๐Ÿฟ]?[โ€๏ธโ™‚โ™€]*๏ธ?" 0 font-shape-gstring]))
                           (concatenate 'list "โ˜๐ŸŽ…๐Ÿ‡๐Ÿ‘‚๐Ÿ‘ƒ๐Ÿ‘ฆ๐Ÿ‘ง๐Ÿ‘ผ๐Ÿ’๐Ÿ’‘๐Ÿ’ช๐Ÿ•ด๐Ÿ•ต๐Ÿ•บ๐Ÿ–๐Ÿ–•๐Ÿ––๐Ÿ™‡๐Ÿšฃ๐Ÿ›€๐Ÿ›Œ๐Ÿค๐Ÿคž๐ŸคŸ๐Ÿคฆ๐Ÿคฝ๐Ÿคพ๐Ÿฅท๐Ÿฆป๐Ÿ‘ฏโค"
                                        '((?โ›น . ?โœ) (?๐Ÿ‚ . ?๐Ÿ„) (?๐ŸŠ . ?๐ŸŒ) (?๐Ÿ‘† . ?๐Ÿ‘)
                                          (?๐Ÿ‘ซ . ?๐Ÿ‘ฎ) (?๐Ÿ‘ฐ . ?๐Ÿ‘ธ) (?๐Ÿ’ . ?๐Ÿ’‡) (?๐Ÿ™… . ?๐Ÿ™‡) (?๐Ÿ™‹ . ?๐Ÿ™)
                                          (?๐Ÿšด . ?๐Ÿšถ) (?๐Ÿค˜ . ?๐Ÿคœ) (?๐Ÿคฐ . ?๐Ÿคน) (?๐Ÿคผ . ?๐Ÿคพ) (?๐Ÿฆต . ?๐Ÿฆน)
                                          (?๐Ÿง . ?๐Ÿง) (?๐Ÿง’ . ?๐ŸงŸ))) )
                 (?๐Ÿง‘ [".โ€?[๐Ÿป-๐Ÿฟ]?[โ€โš•โš–โœˆโค๏ธ๐ŸŒพ๐Ÿณ๐Ÿผ๐ŸŽ„๐ŸŽ“๐ŸŽค๐ŸŽจ๐Ÿซ๐Ÿญ๐Ÿ‘ฆ-๐Ÿ‘ฉ๐Ÿ’‹๐Ÿ’ป๐Ÿ’ผ๐Ÿ”ง๐Ÿ”ฌ๐Ÿš€๐Ÿš’๐Ÿค๐Ÿฆฏ๐Ÿฆฐ-๐Ÿฆณ๐Ÿฆผ๐Ÿฆฝ๐Ÿง‘]*" 0 font-shape-gstring])
                 ((?๐Ÿ‘จ . ?๐Ÿ‘ฉ) [".โ€?[๐Ÿป-๐Ÿฟ]?[โ€โš•โš–โœˆโค๏ธ๐ŸŒพ๐Ÿณ๐Ÿผ๐ŸŽ„๐ŸŽ“๐ŸŽค๐ŸŽจ๐Ÿซ๐Ÿญ๐Ÿ‘ฆ-๐Ÿ‘ฉ๐Ÿ’‹๐Ÿ’ป๐Ÿ’ผ๐Ÿ”ง๐Ÿ”ฌ๐Ÿš€๐Ÿš’๐Ÿค๐Ÿฆฏ๐Ÿฆฐ-๐Ÿฆณ๐Ÿฆผ๐Ÿฆฝ๐Ÿง‘]*" 0 font-shape-gstring])
                 ,@(mapcar (lambda (str) (list (elt str 0) (vector str 0 'font-shape-gstring)))
                           '("๐Ÿ˜ถโ€๐ŸŒซ๏ธ" "๐Ÿˆโ€โฌ›" "๐Ÿ•โ€๐Ÿฆบ" "๐Ÿปโ€โ„๏ธ" "๐Ÿ‘๏ธโ€๐Ÿ—จ๏ธ" "๐Ÿ˜ฎโ€๐Ÿ’จ" "๐Ÿ˜ตโ€๐Ÿ’ซ"))))
   (car items)
   (list (cadr items))))

Possibly, the above snippet will not survive StackExchangeโ€™s formatting, so here it is in base64 of UTF-8:


You can decode it with the command base64 -d.

  • Thanks for the Cairo and HarfBuzz check code.
    – legends2k
    Commented Apr 10, 2022 at 7:34
  • 1
    Running the snippet "setting up composition functions for emoji modifiers" gives Symbol's function definition is void: concatenate in Emacs29 at least. Fixed it by adding a (require 'cl) before the whole sexp.
    – lbo
    Commented Jan 9, 2023 at 12:40
  • Thanks, I updated the code. Commented Jan 9, 2023 at 14:32
  • 1
    Note that emacs-28 now has an emoji script, so you can set the font for symbols and emoji to be different. And it has built-in support for the various multi-character emoji such as country flags
    – rpluim
    Commented Jan 9, 2023 at 15:38

Your Answer

By clicking โ€œPost Your Answerโ€, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.