Building an Emoji Search Tool: Fuzzy Matching, Keyword Indexing, and Performance
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?
- Straightforward iteration during search
- Both
nameandkeywordsparticipate in matching - 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)
2. Multi-Field Search#
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:
navigator.clipboard.writeTextreturns a Promise - await it- Visual feedback after copy (show checkmark for 2 seconds)
- Wrap in
useCallbackto prevent unnecessary re-renders
Multi-Language Search#
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#
Empty Search#
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