Componentpickers1.05 KB gzipZero dependencies

EmojiPicker

Panel with category tabs (Smileys, Gestures, Hearts, Objects), search filter, and emoji grid with selection callback.

Preview

Live Preview

EmojiPicker

Recent

Installation

Tokens guide →

Add to your project via CLI (zero dependencies, code you own):

npx bigbullui add emoji-picker

Or install the full npm package:

npm install bigbullui
import { EmojiPicker } from "@/components/ui/emoji-picker";

<EmojiPicker onSelect={handleSelect} />

Usage guidance

Use EmojiPicker when a ticket stub surface needs Panel with category tabs (Smileys, Gestures, Hearts, Objects), search filter, and emoji grid with selection callback. Keep props minimal and prefer composition with sibling stubs.

Avoid EmojiPicker for long form flows or dense data grids where a dedicated layout block fits better.

Source code

emoji-picker.tsx

Zero dependencies (only React + utils). Copy and paste directly into your project:

"use client";

import * as React from "react";
import { cn } from "./lib/utils";

// Deterministic emoji arrays by category
const emojiCategories = {
  smileys: ["😀", "😁", "😂", "🤣", "😃", "😄", "😆", "😅", "😊"],
  gestures: ["👍", "👎", "👋", "🤝", "🙏", "✊", "👊", "🤘", "🤙", "💪"],
  hearts: ["❤️", "🧡", "💛", "💚", "💙", "💜", "🖤", "🤍", "💔", "❣️"],
  objects: ["💻", "📱", "💾", "🎮", "📚", "🎨", "🎵", "📸", "🎤", "📠"],
};

const EmojiPicker: React.FC<{
  onSelect: (emoji: string) => void;
  initialCategory?: string;
}> = ({ onSelect, initialCategory = "smileys" }) => {
  const [category, setCategory] = React.useState(initialCategory);
  const [search, setSearch] = React.useState("");
  const [selected, setSelected] = React.useState("");

  const filteredEmojis = emojiCategories[category as keyof typeof emojiCategories]
    .filter((emoji) => emoji.includes(search))
    .map((emoji) => emoji);

  return (
    <div
      className={cn(
        "w-full rounded-lg border border-border bg-card p-4",
        "motion-reduce:animate-none"
      )}
    >
      {/* Category tabs */}
      <div className="flex gap-1 mb-3">
        {Object.keys(emojiCategories).map((cat) => (
          <button
            key={cat}
            onClick={() => setCategory(cat)}
            className={cn(
              "rounded-md px-2 py-1 text-[10px] font-mono uppercase text-muted-foreground",
              cat === category && "bg-accent text-accent-foreground"
            )}
          >
            {cat}
          </button>
        ))}
      </div>

      {/* Search filter */}
      <div className="flex gap-2 mb-3">
        <input
          value={search}
          onChange={(e) => setSearch(e.target.value)}
          placeholder="Search emoji..."
          className="flex-1 rounded-border border-input px-3 py-2 text-sm"
        />
      </div>

      {/* Emoji grid */}
      <div className="flex flex-wrap gap-1">
        {filteredEmojis.map((emoji) => (
          <button
            key={emoji}
            onClick={() => {
              setSelected(emoji);
              onSelect(emoji);
            }}
            className={cn(
              "rounded-md px-2 py-1 text-[12px]",
              "hover:bg-accent/5 hover:text-accent-foreground transition-colors duration-150",
              "motion-reduce:animate-none"
            )}
          >
            {emoji}
          </button>
        ))}
      </div>

      {/* Recent row (placeholder - could store in localStorage) */}
      <div className="mt-3 pt-3 border-t border-border/30 text-[10px] mono uppercase text-muted-foreground">
        Recent
      </div>
    </div>
  );
};

export { EmojiPicker };

Props

PropTypeDescription
onSelect(emoji: string) => voidCallback when an emoji is selected.
initialCategorystringInitial category tab (default: smileys).