frimousse

A lightweight, unstyled, and composable emoji picker for React with virtualized rendering and auto-updating emoji data.

Library
npm
v0.3.0
1,780stars
MIT License

Repository Health

Pre-computed score based on development activity, maintenance, community, maturity, and trend momentum.How we score it →
48/100Fair
Development Activity40
Maintenance32
Community40
Maturity40
Momentum40

Technical Analysis

AI-assessed by reading the actual repository — architecture, code quality, innovation, and documentation.How we score it →
85/100Excellent
Architecture85
Code Quality90
Innovation88
Learning Curve75

Frimousse is a React emoji picker built as a set of composable, unstyled parts — Root, Search, Viewport, List, Loading, Empty — that you assemble and style yourself with Tailwind, CSS-in-JS, or plain classes. It ships with no runtime dependencies, is tree-shakable, and virtualizes the emoji grid so only visible rows are rendered, keeping re-renders minimal even with thousands of emoji.

Rather than bundling a static emoji dataset, Frimousse fetches the latest Emojibase data on demand and caches it in local/session storage, re-validating with ETags so repeat visits skip the network round trip. It automatically detects which emoji a user’s platform can render (hiding unsupported glyphs like tofu boxes) and supports 25+ locales, skin tone selection, and keyboard navigation out of the box.

For teams already using shadcn/ui, Frimousse also ships as an installable component via the shadcn CLI, pre-wired to shadcn’s design tokens. It was originally built internally for the Liveblocks Comments default UI before being extracted into its own standalone package.

What You Get

  • Unstyled, composable EmojiPicker.* parts (Root, Search, Viewport, List, Loading, Empty, ActiveEmoji, SkinToneSelector) you assemble into your own markup
  • Automatic Emojibase data fetching with localStorage/sessionStorage caching and ETag-based revalidation, so data loads once and stays fresh
  • Built-in virtualization of the emoji grid — only visible rows render, keeping the picker fast even with the full emoji set loaded
  • Automatic hiding of emoji unsupported on the current platform, avoiding blank tofu-box glyphs
  • Skin tone selection and 25+ locale support driven by Emojibase’s messages dataset
  • A pre-built shadcn/ui component installable via npx shadcn add, pre-wired to shadcn’s design tokens

Common Use Cases

  • Adding an emoji picker to a chat, comment box, or reaction UI without pulling in a heavyweight, pre-styled picker library
  • Building a custom-styled emoji picker inside a design system by composing Frimousse’s unstyled parts with existing component primitives (e.g. a Popover)
  • Shipping a shadcn/ui-based app that needs an emoji picker matching the rest of the app’s shadcn tokens, installed directly via the shadcn CLI
  • Supporting international users with locale-aware emoji labels/search and skin-tone-specific emoji rendering

Under The Hood

Architecture Frimousse is organized as a small monorepo (root package plus a site/ documentation app, wired together with Turborepo) with a clear internal layering: src/data/ handles fetching, parsing, and searching Emojibase datasets (emoji.ts, emoji-picker.ts), src/store.ts/src/utils/store.tsx implement a small custom pub/sub store with batched, rAF-scheduled updates and React context/selector hooks, and src/components/emoji-picker.tsx composes both into the public compound-component API (EmojiPicker.Root, .Search, .Viewport, .List, etc.) using forwardRef and a dedicated EmojiPickerDataHandler that fetches data in an effect and feeds it into the store off the main thread via requestIdleCallback. This separation means data-fetching/caching, state, and rendering can each be reasoned about independently.

Tech Stack The package itself has zero runtime dependencies — only React and TypeScript as peer dependencies — and is built with tsup into dual ESM/CJS output with generated type declarations. Development tooling is Biome for linting/formatting, Vitest (in both jsdom and Playwright-driven browser modes) for tests, and publint/arethetypeswrong for package-export correctness checks. The documentation site under site/ is a separate Next.js-based workspace consuming the built package as a live playground.

Code Quality The repository documents 95%+ test coverage and backs that with an extensive __tests__ suite covering data parsing, search scoring, the custom store, storage helpers, and browser-only behaviors (emoji-support detection) via Vitest’s browser mode with real Chromium through Playwright. TypeScript is configured in strict mode with noUncheckedIndexedAccess, noUnusedLocals/Parameters, and verbatimModuleSyntax, and CI runs type-checking, Biome linting, and package-export validation on every change (.github/workflows/tests.yml), plus continuous preview releases via pkg.pr.new on every PR.

API Design The public surface is deliberately small: a single EmojiPicker namespace export composed of a handful of primitive parts, each accepting standard HTML props plus a few picker-specific ones (locale, columns, skinTone, onEmojiSelect). Consumers get sensible defaults (English locale, 9 columns, no skin tone) and can override just what they need, with full TypeScript types exported for every prop shape. The shadcn CLI installation path lowers the barrier further for a large existing audience, and the parts expose [frimousse-*] data attributes as a built-in styling hook for consumers who don’t want to write custom class logic.

Join founders buildingwith open source

Opinionated takes, migration guides, cost-saving tips, and insights from the open source ecosystem.

Subscribe on Substack
Join 750+ subscribers

Search