A dead simple React Twemoji component implemented with hooks.
Last active
March 24, 2025 01:14
-
-
Save chibicode/fe195d792270910226c928b69a468206 to your computer and use it in GitHub Desktop.
A dead simple React.js Twemoji component.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| .emoji { | |
| height: 1em; | |
| vertical-align: -0.125em; | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| import React, { useState, useEffect, memo } from 'react' | |
| import twemoji from 'twemoji' | |
| const Twemoji = ({ emoji }) => { | |
| const [emojiHtml, setEmojiHtml] = useState(emoji) | |
| useEffect(() => { | |
| setEmojiHtml( | |
| twemoji.parse(emojiHtml, { | |
| folder: 'svg', | |
| ext: '.svg' | |
| }) | |
| ) | |
| }, [emoji]) | |
| return <span dangerouslySetInnerHTML={{ __html: emojiHtml }} /> | |
| } | |
| export default memo(Twemoji) |
Solid, thanks!
Since Twemoji doesn't play nicely with any type of custom Image component, like the drop-in Image replacement in Next.js, I ended up simplifying things even further:
// Twemoji.js
import React, { memo } from "react"
import Image from "next/image"
const isRequired = () => {
throw new Error("You need to specify an emoji for the Twemoji component")
}
const Twemoji = ({ emoji = isRequired() }) => {
const img = emoji.codePointAt(0).toString(16)
console.log(img)
return (
<Image
src={`https://twemoji.maxcdn.com/v/latest/svg/${img}.svg`}
height="72"
width="72"
alt={emoji}
/>
)
}
export default memo(Twemoji)
You'll need to add domains: ["twemoji.maxcdn.com"] to the appropriate place in the next.config.js file to make this work.
You could debate specifying alt="" instead of alt={emoji} for this type of decorative use of an emoji, but I think it's fine.
This is also a more programmer-friendly version than trying to comply with ESLint's emoji rule for a11y.
Thanks for the help @chibicode !
@DoctorDerek Brilliant! Thanks.
@DoctorDerek You save me couple of hours! Thanks.
Fix : 30-fe0f-20e3.svg => 30-20e3.svg .
import React from 'react'
import NextImage from 'next/image'
import twemoji from 'twemoji'
const U200D = String.fromCharCode(0x200d)
const UFE0Fg = /\uFE0F/g
function Twemoji({
emoji,
ext = 'svg',
width = 72,
height = 72,
}: {
emoji: string
ext?: 'svg' | 'png'
width?: number | string
height?: number | string
}) {
const HEXCodePoint = twemoji.convert.toCodePoint(
emoji.indexOf(U200D) < 0 ? emoji.replace(UFE0Fg, '') : emoji
)
return (
<NextImage
src={`https://twemoji.maxcdn.com/v/latest/${ext === 'png' ? '72x72' : 'svg'}/${HEXCodePoint}.${ext}`}
width={width}
height={height}
alt={emoji}
draggable={false}
/>
)
}
export default React.memo(Twemoji)@NekoChanTaiwan works great, thanks for sharing!
UPDATE
import React from 'react';
import NextImage from 'next/image';
import twemoji from 'twemoji';
const U200D = String.fromCharCode(0x200d);
const UFE0Fg = /\uFE0F/g;
function Twemoji({
emoji,
ext = 'svg',
width = 72,
height = 72,
}: {
emoji: string
ext?: 'svg' | 'png'
width?: number
height?: number
}) {
const HEXCodePoint = twemoji.convert.toCodePoint(
emoji.indexOf(U200D) < 0 ? emoji.replace(UFE0Fg, '') : emoji,
);
return (
<NextImage
src={`https://twemoji.maxcdn.com/v/latest/${ext === 'png' ? '72x72' : 'svg'}/${HEXCodePoint}.${ext}`}
width={width}
height={height}
alt={emoji}
loading="lazy"
draggable={false}
/>
);
}
export default React.memo(Twemoji);
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Thanks for the code example!