Engineering at MindLink

Rebuilding a performant emoji picker in React with hooks

May 23, 2019

Before I start, we didn’t really want to have to do this. There are a couple of really good projects out there that offer an emoji picker, they just didn’t quite work how we needed them to.

TL;DR

It is surprisingly easy to build out a complex component with React and hooks. However, you have to be careful about accessibility and profile the performance routinely!

In a couple of days we managed to put together a performant and feature-rich emoji picker UX that supports down to IE 10 using React, hooks, react-window and an emoji data source!

Background

As you perhaps already know one of MindLink’s core products is a single page application for accessing chat systems.

We support legacy chat systems from a time where emojis were not a mainstream expectation from users. We bring up to date emoji support to those chat systems via our server-side abstraction layer, which allows us to build modern features on top of existing Enterprise chat systems.

To bring emoji support to our clients we originally we went with emoji-mart by missive. It’s sleek and stylish and has a lot of customisation options. However, we encountered some issues with performance:

  • On IE10/11 the showing of the picker is slooww as it struggles to render all 2000+ emojis into the DOM
  • Even in modern browsers there’s a noticeable delay
  • The use of a large sprite sheet results in browsers having to reset their graphics context so switching back to a tab with the picker open results in a brief white screen

Additionally, we really wanted to customise it in other ways:

  • Inject the search term as a prop to make it work with our message input
  • To forward on keyboard events to move the selected emojis/trigger selection

What we didn’t need:

  • Customisable source (we opted for Twitter, we don’t need the overhead of supporting other emoji icons)
  • Custom emojis (at least not yet)

Moving to a virtualized list

We knew from performance profiling that the biggest issue is the rendering of the emoji list. 2000+ emojis on screen at once, not a good idea. We need a better way - a virtualised list.

Simply put, a virtualised list only renders what is visible into the DOM on demand and tracks scroll position to trigger loading different parts of the list. This means the complete list is virtual and we render only a window.

A well-established solution in the React world is react-window. It is a great project that handles most of the pain of managing a virtualised list or grid. There is also the possibility to get some vanilla JS to do this using an IntersectionObserver to trigger rendering “pages”.

We are also fortunate that right now it’s pretty easy to make lightweight components using React hooks.

Combining both react-window and hooks we sought to recreate the minimal feature set we wanted from emoji-mart:

  • Category icon buttons allow you to jump to categories and also highlight what category you’re currently viewing
  • Inline category headers separating groups of emojis
  • A larger preview of a highlighted emoji showing the shortcode, description and ASCII art equivalents
  • A skin variation picker to select a skin tone
  • Search to filter the emojis

A look at the finished picker

A first draft of this we threw together in about half a day - leveraging the emoji-mart data source so that we didn’t have to worry about compiling that. That is the power of leveraging hooks and react-window.

We took that first draft, liked it and built it out over another couple of days to get it to where we wanted it for production.

Emoji picker with skin tone

Emoji picker with preview

The results are impressive considering the short time frame. The picker loads considerable faster and supports all the same use cases we had. We now have the full benefits of owning that:

  • We can style it totally how we like
  • We can add extra features specific to our use cases
  • We can keep it as up to date as we need it (hello Unicode 12)

We want to take this open-source, but before we do we need to figure out a first-class data source and stop piggy-backing off of emoji-mart so we can control the Unicode support.

I’ll take a deep-dive into the implementation in a few follow-up posts.


Luke Terry

Written by Luke Terry.

Senior Engineer at MindLink. Enjoys technology, playing games and making things work, blogs at www.indescrible.co.uk.