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

react-raffle-picker

v0.2.2

Published

Headless React raffle picker for giveaways, winner draws, countdowns, and slot-machine UIs.

Readme

A headless, composable React component for giveaways, raffles, and slot-machine UIs. Cycles numbers or names with smooth animations and freezes on a winner. Performant on slow devices — high-frequency tick updates bypass React.

npm install react-raffle-picker

Preview

Idea

react-raffle-picker ships as a headless compound component, not a monolithic widget. The root owns the engine — cycling, phase machine, freeze logic — and exposes it through React context. Sub-components (Value, Button, Countdown, Slots) are dumb consumers that you compose anywhere in your tree.

This means:

  • No layout opinions. Wrap pieces in your own card, modal, sidebar, paragraph.
  • No style props soup. Style each piece directly via className / style.
  • One source of truth. All state lives in the root. Sub-components read context.
  • Performance preserved. Tick updates write to DOM imperatively via refs — no React re-render per tick. Context only re-renders on phase boundaries (start, settle, freeze).
import { RafflePick } from 'react-raffle-picker'

;<RafflePick min={1} max={100} interval={100} inertia onSelect={(v) => console.log(v)}>
  <RafflePick.Value animation="roll" className="my-value" />
  <RafflePick.Button startLabel="Pick" stopLabel="Stop" />
</RafflePick>

Styles

The library is headless — no global stylesheet is required for Value, Button, or Countdown. You bring your own CSS.

For the slot reel (<RafflePick.Slots>), a minimal stylesheet is required to make the column animate. Two ways to load it:

  1. Auto-injected at runtime<RafflePick.Slots> injects a <style data-rrp-slot-base> tag into document.head on first mount. No action needed in CSR apps.

  2. Static import (recommended for SSR / strict CSP / full control):

    import 'react-raffle-picker/styles.css'

    This file also includes opt-in keyframes for <RafflePick.Value animation="roll|fade|blur|reel" />. Override or replace any selector in your own CSS.

Components

<RafflePick> (root)

Provides context. Renders an optional wrapper element (as prop, default 'div').

| Prop | Type | Default | Notes | | -------------- | --------------------------- | ---------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | min, max | number | 1, 100 | Numeric range. Ignored if items provided. | | items | string[] | — | Switches to item mode (cycles through names). | | interval | number (ms, clamped ≥ 50) | 100 | Tick speed. | | random | boolean | true | Random pick vs sequential. | | inertia | boolean | false | Soft start / soft stop ramp. | | autoStart | boolean | true | Begin cycling on mount. | | initialValue | number \| string | — | Starting display before first run. Number for min/max mode, string for items mode. For <Slots>, each character seeds the corresponding reel. | | finalValue | number \| string | — | Forces settle to land on this value. Cycle still appears random; only final freeze is rigged. For <Slots>, each character is the final char of the corresponding reel. | | onSelect | (value) => void | — | Fires once per round on freeze. | | as | ElementType | 'div' | Wrapper tag. | | className | string | — | Wrapper class. | | style | CSSProperties | — | Wrapper style. | | children | ReactNode | — | Sub-components. |

<RafflePick.Value>

Renders the cycling value. Updates textContent imperatively each tick (no React re-render).

| Prop | Type | Default | | ----------- | -------------------------------------- | -------- | | animation | 'roll' \| 'fade' \| 'blur' \| 'reel' | 'roll' | | as | ElementType | 'span' | | className | string | — | | style | CSSProperties | — |

Multiple Value instances inside one root are supported — all subscribe to the same tick.

<RafflePick.Button>

Toggles start / freeze based on phase. Disabled during settling.

| Prop | Type | Notes | | ------------ | --------------- | ------------------------------------------------ | | startLabel | ReactNode | Shown in idle / frozen (click starts). | | stopLabel | ReactNode | Shown in running / starting (click stops). | | waitLabel | ReactNode | Shown in settling (button disabled). | | children | ReactNode | Fallback label when state-specific label absent. | | className | string | — | | style | CSSProperties | — |

<RafflePick.Countdown>

Schedules auto-freeze after seconds. Renders an SVG ring + numeric label by default. Optional render-prop for custom output.

| Prop | Type | Notes | | ----------- | ---------------------------------- | ---------------------------------------------- | | seconds | number (required) | Auto-freeze delay. Renders only while running. | | className | string | — | | style | CSSProperties | — | | children | (remaining: number) => ReactNode | Render-prop for custom UI. |

<RafflePick.Slots>

Independent multi-reel slot machine. Each reel ticks on its own and stops with a stagger.

initialValue (root prop) seeds reels char-by-char before the first run. finalValue rigs the freeze so each reel lands on the corresponding character — useful for predetermined winners or scripted demos.

| Prop | Type | Default | Notes | | -------------------------------------------------- | -------------------------- | -------------- | -------------------------------------------- | | length | number | 3 | Number of reels. | | chars | string | '0123456789' | Charset pool. Emoji-safe (code-point split). | | spinInterval | number (ms, ≥ 50) | 80 | Tick rate per reel. | | staggerMs | number | 220 | Delay between consecutive reel stops. | | onResult | (joined: string) => void | — | Fires when the last reel lands. | | className, slotClassName, style, slotStyle | various | — | Style hooks. |

Recipes

Inline chip in a sentence

<RafflePick min={1} max={36} as="p" autoStart={false}>
  Roulette landed on <RafflePick.Value animation="roll" className="chip" /> —{' '}
  <RafflePick.Button startLabel="spin again" stopLabel="stop" className="link-btn" />
</RafflePick>

Hero with countdown ring

<RafflePick min={1} max={999} inertia autoStart={false} className="hero">
  <RafflePick.Countdown seconds={5} className="hero__ring" />
  <RafflePick.Value animation="blur" className="hero__value" />
  <RafflePick.Button startLabel="Start Draw" stopLabel="Stop" className="hero__btn" />
</RafflePick>

Rigged draw with predetermined winner

finalValue lands the freeze on a specific value while the cycle still looks random — useful for staged demos, scripted reveals, or showing a known winner.

<RafflePick
  items={['Alice', 'Bob', 'Carol']}
  initialValue="Alice"
  finalValue="Bob"
  autoStart={false}
  onSelect={(winner) => console.log(winner)} // always 'Bob'
>
  <RafflePick.Value animation="reel" />
  <RafflePick.Button startLabel="Draw" stopLabel="Reveal" />
</RafflePick>

Slot machine with custom result handler

<RafflePick inertia autoStart={false}>
  <RafflePick.Slots
    length={5}
    chars="0123456789"
    staggerMs={260}
    onResult={(code) => console.log('winning code:', code)}
  />
  <RafflePick.Button startLabel="Spin" stopLabel="Stop" />
</RafflePick>

Roadmap

  • Winner position — emit row/index alongside value (onSelect receives { value, index }) and a <RafflePick.WinnerPosition> consumer that reflects the landed position.
  • bounce animation — vertical hop on each tick, easing back to baseline.
  • glitch animation — offset color-channel pulse for a digital noise feel during running.
  • Headless useRafflePick() hook for users who want zero rendering from the lib.
  • Render-prop variant of <Value> for fully custom DOM.

Performance notes

  • Cycle ticks (every interval ms) write textContent directly via refs — no React render.
  • Phase machine re-renders only on transitions (start, inertia step, settle, freeze).
  • CSS animation duration is bound to --rrp-tick so each cycle aligns with one tick — no cross-frame tearing.
  • will-change is scoped to running / inertia phases only, so idle / frozen text renders with crisp subpixel anti-aliasing.

License

MIT