DEV Community

Cover image for How To Make Emoji Input Menu React Component with Emoji Mart
Rodion Chachura
Rodion Chachura

Posted on • Originally published at radzion.com

How To Make Emoji Input Menu React Component with Emoji Mart

Watch on YouTube | 🐙 GitHub | 🎮 Demo

Let me share a reusable React component for choosing an emoji. That's how I use it in my app at increaser.org to assign an emoji to a project.

Demo

The EmojiInput component receives only two properties:

  • value - selected emoji
  • onChange - a function that is called when the value changes
import { Spinner } from "lib/ui/Spinner"
import { HStack } from "lib/ui/Stack"
import { centerContentCSS } from "lib/ui/utils/centerContentCSS"
import { Suspense, lazy } from "react"
import styled from "styled-components"
import { ExpandableInputOpener } from "../ExpandableInputOpener"
import { Menu } from "lib/ui/Menu"
import { Text } from "lib/ui/Text"
import { InputProps } from "lib/shared/props"

const EmojiPicker = lazy(() => import("./EmojiPicker"))

interface EmojiInputProps extends InputProps<string> {}

const EmojiMartFallback = styled.div`
  width: 352px;
  height: 435px;
  ${centerContentCSS};
`

export const EmojiInput = ({ value, onChange }: EmojiInputProps) => {
  return (
    <Menu
      title="Select an emoji"
      renderOpener={(props) => (
        <ExpandableInputOpener type="button" {...props}>
          <Text color="contrast" size={32}>
            {value}
          </Text>
        </ExpandableInputOpener>
      )}
      renderContent={({ onClose }) => (
        <Suspense
          fallback={
            <EmojiMartFallback>
              <HStack gap={4} alignItems="center">
                <Spinner />
                <Text>Loading emoji picker</Text>
              </HStack>
            </EmojiMartFallback>
          }
        >
          <EmojiPicker
            onSelect={(value) => {
              onChange(value)
              onClose()
            }}
          />
        </Suspense>
      )}
    />
  )
}
Enter fullscreen mode Exit fullscreen mode

To show a responsive menu that becomes a slideover on mobile we rely on the Menu component, you can learn more about it in this article. For a button that opens the menu, we use the ExpandableInputOpener component.

import styled from "styled-components"
import { defaultTransitionCSS } from "ui/animations/transitions"
import { defaultBorderRadiusCSS } from "ui/borderRadius"
import { inputDefaultHeight } from "ui/Input/helpers/getInputShapeCSS"
import { getColor } from "ui/theme/getters"
import { centerContentCSS } from "ui/utils/centerContentCSS"
import { getSameDimensionsCSS } from "ui/utils/getSameDimensionsCSS"

import { UnstyledButton } from "./UnstyledButton"

export const ExpandableInputOpener = styled(UnstyledButton)`
  ${centerContentCSS}
  ${defaultBorderRadiusCSS}
  ${defaultTransitionCSS}

  ${getSameDimensionsCSS(inputDefaultHeight)};

  background: ${getColor("backgroundGlass")};

  :hover {
    background: ${getColor("backgroundGlass2")};
  }
`
Enter fullscreen mode Exit fullscreen mode

The EmojiPicker component is a wrapper around the emoji-mart library, and we don't want to include this library in the bundle, and rather lazy load it when needed. That's why we use the lazy function and the Suspense component from React.

import data from "@emoji-mart/data"
import Picker from "@emoji-mart/react"
import { SelectableComponentProps } from "lib/shared/props"
import { useTheme } from "styled-components"

const EmojiPicker = ({ onSelect }: SelectableComponentProps<string>) => {
  const { name } = useTheme()

  return (
    <Picker
      autoFocus
      data={data}
      theme={name}
      showPreview={false}
      showSkinTones={false}
      onEmojiSelect={(emoji: any) => {
        if (!emoji?.native) return

        onSelect(emoji.native)
      }}
    />
  )
}

export default EmojiPicker
Enter fullscreen mode Exit fullscreen mode

Top comments (0)