lazy-load emoji-mart picker on web

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Samuel Newman
2026-07-08 13:39:07 +03:00
parent ea3d6827c7
commit b49f2c589d
2 changed files with 46 additions and 13 deletions
+42 -11
View File
@@ -1,5 +1,13 @@
import {createContext, useContext, useEffect, useMemo, useRef} from 'react'
import EmojiPicker from '@emoji-mart/react'
import {
createContext,
lazy,
Suspense,
useContext,
useEffect,
useMemo,
useRef,
} from 'react'
import {View} from 'react-native'
import {DropdownMenu} from 'radix-ui'
import {useA11y} from '#/state/a11y'
@@ -16,6 +24,24 @@ import {
export * from './types'
/**
* The emoji-mart picker is code-split into its own chunk (`emoji-mart`) so its
* weight stays out of the main bundle and is only fetched when the picker
* actually mounts (i.e. when the dropdown opens). {@link useWebPreloadEmoji}
* warms the same chunk ahead of time.
*/
const EmojiPickerLazy = lazy(
() => import(/* webpackChunkName: "emoji-mart" */ '@emoji-mart/react'),
)
/**
* Fixed-size placeholder shown while the emoji-mart chunk loads, sized to match
* emoji-mart's default picker so the dropdown doesn't visually jump when the
* real picker mounts.
*/
const PICKER_WIDTH = 352
const PICKER_HEIGHT = 435
const EmojiPickerContext = createContext<{
onEmojiSelect: (emoji: Emoji) => void
nextFocusRef: RootProps['nextFocusRef']
@@ -124,16 +150,21 @@ export function Picker({keepOpenWhenShiftHeld = true}: PickerProps) {
<div
onWheel={evt => evt.stopPropagation()}
style={flatten([!reduceMotionEnabled && a.zoom_fade_in])}>
<EmojiPicker
autoFocus
onEmojiSelect={(emoji: Emoji) => {
onEmojiSelect(emoji)
<Suspense
fallback={
<View style={{width: PICKER_WIDTH, height: PICKER_HEIGHT}} />
}>
<EmojiPickerLazy
autoFocus
onEmojiSelect={(emoji: Emoji) => {
onEmojiSelect(emoji)
if (!keepOpenWhenShiftHeld || !isShiftDown.current) {
control.close()
}
}}
/>
if (!keepOpenWhenShiftHeld || !isShiftDown.current) {
control.close()
}
}}
/>
</Suspense>
</div>
</DropdownMenu.Content>
</DropdownMenu.Portal>
+4 -2
View File
@@ -1,5 +1,4 @@
import {useCallback} from 'react'
import {init} from 'emoji-mart'
/**
* Only load the emoji picker data once per page load.
@@ -21,7 +20,10 @@ export function useWebPreloadEmoji({immediate}: {immediate?: boolean} = {}) {
if (loadRequested) return
loadRequested = true
try {
const data = (await import('@emoji-mart/data')).default
const [{init}, {default: data}] = await Promise.all([
import(/* webpackChunkName: "emoji-mart" */ 'emoji-mart'),
import(/* webpackChunkName: "emoji-mart-data" */ '@emoji-mart/data'),
])
init({data})
} catch (e) {}
}, [])