Building an Emoji Search Tool: Fuzzy Matching, Keyword Indexing, and Performance#

Recently I needed to add emoji search to a project. Seemed simple at first, but there’s more to it than meets the eye. Here’s what I learned.

The Problem#

User types “happy” - we need to match 😀, 😊, 🥰. User types “love” - match ❤️, 😍, 💕. Core challenge: multi-language keyword matching that’s fast and accurate.

Data Structure#

First thought might be an object:

// Not recommended
const emojiMap = {
  '😀': { name: 'grinning face', keywords: ['happy', 'smile'] },
  '😂': { name: 'face with tears of joy', keywords: ['laugh', 'lol'] }
}

But this structure makes searching awkward. Better to use an array:

interface EmojiItem {
  emoji: string
  name: string
  keywords: string[]
}

export const emojiData: EmojiItem[] = [
  { emoji: '😀', name: 'grinning face', keywords: ['笑脸', '开心', '笑', 'happy', 'smile', 'joy'] },
  { emoji: '😂', name: 'face with tears of joy', keywords: ['笑哭', '大笑', '哭笑', 'lol', 'laugh'] },
  { emoji: '❤️', name: 'red heart', keywords: ['爱', '红心', 'love', 'heart'] }
]

Why array?

  1. Straightforward iteration during search
  2. Both name and keywords participate in matching
  3. Multi-language keywords in one place

Fuzzy Matching Implementation#

Core logic:

const filteredEmojis = useMemo(() => {
  if (!searchQuery.trim()) return emojiData

  const query = searchQuery.toLowerCase()
  return emojiData.filter(item =>
    item.name.toLowerCase().includes(query) ||
    item.keywords.some(kw => kw.toLowerCase().includes(query))
  )
}, [searchQuery])

Three key decisions:

1. Case-Insensitive Matching#

User might type LOVE, Love, or love. All should match ❤️:

const query = searchQuery.toLowerCase()
item.name.toLowerCase().includes(query)

Match both name and keywords:

item.name.toLowerCase().includes(query) ||
item.keywords.some(kw => kw.toLowerCase().includes(query))

User types “happy” - matches both the name field and keywords array.

3. Partial Matching#

Using includes instead of === enables partial matches. Typing “hap” matches “happy”.

Performance: useMemo#

Emoji datasets can have thousands of entries. Filtering on every keystroke would lag. Use useMemo to cache results:

const filteredEmojis = useMemo(() => {
  // Filtering logic
}, [searchQuery])

Only recalculates when searchQuery changes.

For very large datasets (5000+ emojis), consider advanced optimizations:

Debounced Input#

const [debouncedQuery, setDebouncedQuery] = useState('')

useEffect(() => {
  const timer = setTimeout(() => {
    setDebouncedQuery(searchQuery)
  }, 150)
  return () => clearTimeout(timer)
}, [searchQuery])

Inverted Index#

For exact match scenarios, pre-build an index:

// Pre-built at compile time
const keywordIndex: Record<string, string[]> = {
  'happy': ['😀', '😊', '🥰'],
  'love': ['❤️', '😍', '💕'],
  'smile': ['😀', '😊', '😄']
}

// O(1) lookup
const search = (query: string) => {
  return keywordIndex[query.toLowerCase()] || []
}

This drops search complexity from O(n*m) to O(1). Perfect for exact match use cases.

Copy to Clipboard#

Click emoji to copy:

const handleCopy = useCallback(async (emoji: string) => {
  await navigator.clipboard.writeText(emoji)
  setCopiedEmoji(emoji)
  setTimeout(() => setCopiedEmoji(null), 2000)
}, [])

Key points:

  1. navigator.clipboard.writeText returns a Promise - await it
  2. Visual feedback after copy (show checkmark for 2 seconds)
  3. Wrap in useCallback to prevent unnecessary re-renders

Users might search in Chinese or English. Store both in keywords:

{ emoji: '😀', name: 'grinning face', keywords: ['笑脸', '开心', '笑', 'happy', 'smile', 'joy'] }

User types “开心” or “happy” - both match. Data-level localization beats runtime translation for reliability.

Edge Cases#

Return all emojis when input is empty:

if (!searchQuery.trim()) return emojiData

Whitespace Handling#

Users accidentally type spaces. Trim them:

searchQuery.trim()

No Results#

Show a message instead of blank screen:

{filteredEmojis.length === 0 && (
  <div className="text-center py-8 text-gray-500">
    No matching emojis found
  </div>
)}

The Result#

Built an online emoji search tool: Emoji Search

Features:

  • Multi-language keyword search
  • Real-time filtering, responsive
  • Click to copy with visual feedback
  • Thousands of emojis supported

The implementation isn’t complex, but getting the UX right takes attention to detail. Hope this helps.


Related: String Escape Tool | Base64 Encoder