npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@emoteer/svelte

v0.1.0

Published

Headless and styled Svelte 5 components for emoji pickers, reactions, autocomplete and inputs.

Downloads

26

Readme

@emoteer/svelte

Headless and styled Svelte 5 components for emoji pickers, reactions, inline autocomplete, shortcode-to-unicode inputs, and reaction intensity sliders.

Built on Zag.js state machines and emojibase data. Truly headless (style-free markup with data-part hooks), with prebuilt CSS that works with or without Tailwind v4. Tree-shakeable, accessible, typed, SSR-safe. This is the Svelte port of @emoteer/react — same components, same styling contract.

Install

npm install @emoteer/svelte
# or
pnpm add @emoteer/svelte

Peer dependencies:

| Package | Range | | -------- | ---------- | | svelte | ^5.20.0 |

@emoteer/core and @emoteer/theme come bundled as transitive dependencies — no need to install them directly.

Requires Svelte 5 (runes mode). The components use $state/$derived/$props.id() internally.

Setup

Styles

The components ship no styling baked into the markup — they render stable data-scope / data-part hooks and accept your class. Pick the level that fits your app. Tailwind is optional.

1. Styled, without Tailwind — import the prebuilt, framework-agnostic stylesheet once (in your root layout or +layout.svelte):

import "@emoteer/svelte/styles.css";

That's it — plain CSS, no build pipeline required. It pulls in the --em-* design tokens and styles every part with them, so you theme by overriding tokens (see @emoteer/theme).

2. Styled, with Tailwind v4 — import the preset from a CSS file in your Tailwind pipeline. You get the default look plus bg-em-* / rounded-em-* token utilities to override with:

@import "tailwindcss";
@import "@emoteer/svelte/tailwind";

3. Fully headless — import nothing (or only @emoteer/theme/css for the tokens) and write your own CSS against the styling contract. The logic, accessibility and state all work with zero styles.

Styling contract (data-part)

Identical to the React package: every rendered element carries data-scope="<component>" + data-part="<part>", and exposes state via data-* / ARIA attributes.

[data-scope="emote-list"][data-part="cell"]:hover {
  background: rebeccapurple;
}
[data-scope="reaction-counter"][data-part="chip"][data-active] {
  outline: 2px solid hotpink;
}

Provider

Every data-aware component expects an EmoteProvider ancestor. Place it once, near the root:

<script>
  import { EmoteProvider } from "@emoteer/svelte";
</script>

<EmoteProvider locale="en">
  <slot />
</EmoteProvider>

It loads emoji data lazily per locale and memoises the shortcode/unicode indexes for every descendant.

Quick start

<script lang="ts">
  import {
    EmoteProvider,
    EmoteListPicker,
    ReactionButton,
    ReactionCounter,
    type Reaction,
  } from "@emoteer/svelte";

  let reactions = $state<Reaction[]>([{ emoji: "👍", count: 3, active: false }]);

  function addReaction(emoji: string) {
    /* … */
  }
  function toggle(emoji: string, active: boolean) {
    /* … */
  }
</script>

<EmoteProvider locale="en">
  <ReactionButton.Root onSelect={addReaction}>
    <ReactionButton.Item emoji="👍" />
    <ReactionButton.Item emoji="🎉" burst />
    <ReactionButton.Divider />
    <ReactionButton.Popover>
      <ReactionButton.Trigger />
      <ReactionButton.Content>
        <EmoteListPicker onSelect={(e) => addReaction(/* … */)} />
      </ReactionButton.Content>
    </ReactionButton.Popover>
  </ReactionButton.Root>

  <ReactionCounter {reactions} onToggle={toggle} />
</EmoteProvider>

Core concepts

Compound components

Every non-trivial component is a compound API: a Root that owns state + subcomponents that read it from context. Svelte 5's dotted component syntax (<EmoteList.Root>) makes this read just like the React version:

<EmoteList.Root onSelect={handleSelect}>
  <EmoteList.Search />
  <EmoteList.Tabs />
  <EmoteList.Grid />
  <EmoteList.Preview />
</EmoteList.Root>

For the common case, convenience components (EmoteListPicker) compose the default arrangement so you don't have to.

Differences from the React API

The component set, behaviour, and data-part contract are identical. Only the prop conventions follow Svelte idiom:

| React | Svelte | | --------------------------- | ----------------------------------------------- | | className | class | | onChange (DOM) | oninput / bind:value | | render-prop children | {#snippet children(value)}…{/snippet} | | useEmoteContext() hook | useEmoteContext() (call in <script>) |

Library-specific callbacks keep their names: onSelect, onToggle, onChange/onChangeEnd (slider), onOpenChange (popover).

Text inputs: bind:value

EmoteInput and EmoteTextArea expand :shortcode: to its unicode emoji as you type, preserving the caret position across conversions:

<EmoteInput bind:value={text} placeholder="Type :smile: to expand" />
<EmoteTextArea bind:value={text} rows={5} />

EmoteAutocomplete.Input is uncontrolled by design — the Root tracks the live input value internally to detect the :shortcode trigger. Read the final text via onSelect(emote, value).

SSR

Components don't touch window/document during render. EmoteProvider loads emoji data inside an $effect (client-only), so the first server render sees an empty dataset and loading-aware components render a placeholder until hydration.

Components

| Component | Notes | | -------------------------------------- | --------------------------------------------------------------------- | | EmoteProvider | Loads emoji data once; props: natives, locals, locale, cloud. | | EmoteList.* / EmoteListPicker | Virtualised picker: Search, Tabs, Grid, Preview. | | EmoteAutocomplete.* | Inline :shortcode suggestions via Floating UI. | | EmoteInput / EmoteTextArea | :shortcode: → unicode as you type (bind:value). | | ReactionButton.* | Reaction bar + optional Zag popover picker; Item, Plus, Sticker, Divider. | | ReactionCounter | Declarative grouped reactions with counts + pressed state. | | ReactionSlider.* | Intensity slider with an emoji thumb and drag marker. |

useEmoteContext()

Returns the reactive emote store provided by an ancestor <EmoteProvider>. Call it at the top of a component <script>:

<script lang="ts">
  import { useEmoteContext } from "@emoteer/svelte";
  const ctx = useEmoteContext();
  // ctx.emojis, ctx.locals, ctx.emotes, ctx.shortcodeIndex,
  // ctx.unicodeIndex, ctx.isLoading, ctx.error — all reactive.
</script>

Throws if called outside EmoteProvider.

Types

Re-exported from @emoteer/core for convenience:

import {
  isLocalEmote,
  isNativeEmoji,
  type NativeEmoji,
  type LocalEmote,
  type Emote,
  type CloudConfig,
  type Reaction,
  type Locale,
  type SupportedLocale,
} from "@emoteer/svelte";

License

MIT © vyers