Skip to content
Inputs

Emoji Picker

The emoji keyboard: search, categories, skin tone and a memory of what you picked last.

Walkthrough

1. Basic

The panel on its own: type to search, arrow around the grid, Enter to take one. It fills whatever width it is given.

Smileys & emotion

People & body

Animals & nature

Food & drink

Activity

Travel & places

Objects

Symbols

Flags

Pick an emoji

Picked: nothing yet

<script lang="ts">	import { EmojiPicker, Emoji } from 'omaris';​	let picked = $state('');</script>​<div class="flex w-full max-w-sm flex-col gap-3">	<EmojiPicker bind:value={picked} storageKey={null} />	<p class="text-body-sm text-muted-foreground">		Picked:		{#if picked}			<Emoji emoji={picked} size="lg" label={picked} />		{:else}			nothing yet		{/if}	</p></div>

2. In a composer

The shape it takes in a message box: EmojiButton is the panel behind a trigger — anchored on a pointer, a bottom sheet on a phone.

<script lang="ts">	import { EmojiButton, Input } from 'omaris';​	let message = $state('Ship it ');</script>​<div class="flex w-full max-w-sm items-end gap-2">	<Input class="flex-1" bind:value={message} aria-label="Message" />	<EmojiButton storageKey={null} onselect={(emoji) => (message += emoji)} /></div>

3. Custom trigger

A reaction bar. The trigger is a snippet, so it can be anything; the panel is the same one.

Reacted with 👍
<script lang="ts">	import { EmojiButton, Emoji } from 'omaris';​	const QUICK = ['👍', '❤️', '😂', '🎉'];	let reaction = $state('👍');</script>​<div class="flex items-center gap-1.5">	{#each QUICK as emoji (emoji)}		<button			type="button"			class="grid size-9 cursor-pointer place-items-center rounded-full bg-surface-container transition-[scale,background-color] duration-150 ease-spring hover:scale-110 hover:bg-surface-container-high active:scale-95 motion-reduce:transition-none"			onclick={() => (reaction = emoji)}			aria-label="React with {emoji}"		>			<Emoji {emoji} size="lg" />		</button>	{/each}​	<EmojiButton storageKey={null} bind:value={reaction} picker={{ height: 220, tones: false }}>		{#snippet trigger({ open })}			<span				class="grid size-9 cursor-pointer place-items-center rounded-full border border-dashed border-border text-muted-foreground transition-[background-color,rotate] duration-200 ease-emphasized hover:bg-surface-container motion-reduce:transition-none"				class:rotate-45={open}			>				<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" class="size-4">					<path d="M12 5v14M5 12h14" stroke-linecap="round" />				</svg>			</span>		{/snippet}	</EmojiButton>​	<span class="ms-3 text-body-sm text-muted-foreground">		Reacted with <Emoji emoji={reaction} size="lg" label={reaction} />	</span></div>

4. Skin tone

Skin tone is chosen once, in the preview row, and applies to every emoji that takes one. Bind it when the rest of the app needs to know.

Smileys & emotion

People & body

Animals & nature

Food & drink

Activity

Travel & places

Objects

Symbols

Flags

Pick an emoji
Tone 3
<script lang="ts">	import { EmojiPicker, Emoji, type EmojiSkinTone } from 'omaris';​	let tone = $state<EmojiSkinTone>(3);</script>​<div class="flex w-full max-w-md flex-wrap items-start gap-6">	<EmojiPicker bind:skinTone={tone} storageKey={null} height={220} class="min-w-64 flex-1" />	<div class="flex flex-col gap-2">		<span class="text-body-sm text-muted-foreground">Tone {tone}</span>		<div class="flex gap-2">			<Emoji emoji="👋" {tone} size="xl" />			<Emoji emoji="👍" {tone} size="xl" />			<Emoji emoji="🙏" {tone} size="xl" />			<Emoji emoji="🧑‍💻" {tone} size="xl" />		</div>	</div></div>

5. Emoji

Emoji on its own: the emoji font first, sat on the x-height so it keeps a line's rhythm, and a label only when it carries meaning.

Deployed to production — the emoji is decoration next to text that already says so, so it has no label. A status of its own does: 🟢.

<script lang="ts">	import { Emoji, Text } from 'omaris';</script>​<div class="flex flex-col gap-4">	<div class="flex items-end gap-3">		<Emoji emoji="🎉" size="xs" />		<Emoji emoji="🎉" size="sm" />		<Emoji emoji="🎉" size="md" />		<Emoji emoji="🎉" size="lg" />		<Emoji emoji="🎉" size="xl" />		<Emoji emoji="🎉" size="2xl" />	</div>	<Text class="max-w-md">		Deployed to production <Emoji emoji="🚀" /> — the emoji is decoration next to text that already says		so, so it has no label. A status of its own does: <Emoji emoji="🟢" label="Online" />.	</Text></div>

6. Overridden

The overridden case: a set of your own, no chrome, a fixed column count, and classes on the heading and the cells.

Status

Smileys & emotion

People & body

Animals & nature

Food & drink

Activity

Travel & places

Objects

Symbols

Flags

Pick an emoji
<script lang="ts">	import { EmojiPicker, type EmojiGroup } from 'omaris';​	/** A status picker, in place of the built-in set. */	const STATUS: EmojiGroup[] = [		{			id: 'status',			label: 'Status',			icon: '🟢',			emojis: [				{ emoji: '🟢', name: 'available', keywords: ['free'], tone: false, category: 'symbols' },				{ emoji: '🟡', name: 'busy', keywords: ['later'], tone: false, category: 'symbols' },				{					emoji: '🔴',					name: 'do not disturb',					keywords: ['dnd'],					tone: false,					category: 'symbols'				},				{ emoji: '🌴', name: 'on holiday', keywords: ['away'], tone: false, category: 'nature' },				{					emoji: '🏠',					name: 'working from home',					keywords: ['wfh'],					tone: false,					category: 'travel'				},				{ emoji: '🎧', name: 'heads down', keywords: ['focus'], tone: false, category: 'objects' }			]		}	];</script>​<div class="flex w-full flex-wrap items-start gap-6">	<div class="w-56 max-w-full rounded-shape-lg border border-border bg-surface-container-low p-2">		<EmojiPicker			bare			groups={STATUS}			categories={false}			preview={false}			tones={false}			columns={3}			height={140}			searchPlaceholder="Search status"			storageKey={null}			class="w-full"		/>	</div>	<EmojiPicker		storageKey={null}		columns={10}		height="12rem"		class="w-full max-w-sm"		classes={{ heading: 'text-primary', cell: 'rounded-full' }}	/></div>