Frontend Development 7 min read

Boost Emoji Picker Performance with CSS content-visibility

The article explains how using the new CSS content-visibility property can dramatically reduce layout and paint costs when rendering tens of thousands of custom emojis, offering a lightweight alternative to full virtualization while preserving accessibility and searchability.

KooFE Frontend Team
KooFE Frontend Team
KooFE Frontend Team
Boost Emoji Picker Performance with CSS content-visibility

This article, translated from "Improving rendering performance with CSS content-visibility," describes a performance issue encountered with the emoji-picker-element on a Fediverse instance containing nearly 20,000 custom emojis, where opening the picker caused a noticeable freeze.

Each custom emoji is represented by a

<button>

and an

<img>

, resulting in about 40,000 DOM elements. Because no virtualization is used, all elements are inserted into the DOM, leading to slow rendering—Lighthouse recommends staying under 1,400 elements.

Although the images use

<img loading="lazy">

to defer downloading, rendering 40k elements remains costly. The author initially considered virtualizing the list but avoided it due to complexity, perceived unnecessary need, and potential accessibility impact.

Instead, the author explored CSS

content-visibility

, a new feature that allows parts of the DOM to be hidden from layout and paint without removing them from the accessibility tree, thus preserving searchability and ARIA semantics.

By treating each emoji category (e.g., "blobs", "cats") as a unit, the author applied

content-visibility: auto

and

contain-intrinsic-size

using CSS custom properties to reserve space for off‑screen categories, preventing layout jumps during scrolling.

The CSS snippet used for each category is:

<code>.category {
  content-visibility: auto;
  contain-intrinsic-size:
    /* width */ calc(var(--num-columns) * var(--total-emoji-size))
    /* height */ calc(var(--num-rows) * var(--total-emoji-size));
}</code>

Performance tests showed a modest 15% improvement in Chrome and 5% in Firefox for initial load, though still far behind full virtualization. Further gains were achieved by replacing

&lt;img&gt;

elements with CSS background images via a ::after pseudo‑element on the

&lt;button&gt;

, reducing the DOM element count from 40k to 20k.

Using IntersectionObserver to add an

.onscreen

class when a category scrolls into view, the author achieved roughly 45% faster rendering in both Chrome and Firefox, cutting load time from ~3 seconds to ~1.3 seconds. Safari's limited support for

content-visibility:auto-state-change

required falling back to IntersectionObserver.

While this approach does not match the speed of a fully virtualized list and may not scale indefinitely, it offers a low‑cost implementation that maintains accessibility and searchability, making

content-visibility

a useful tool when ultimate performance is not the sole priority.

frontendperformancevirtualizationCSScontent-visibilityemoji-picker
KooFE Frontend Team
Written by

KooFE Frontend Team

Follow the latest frontend updates

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.