@firdaus.lubis/react-slotmachine
v1.1.2
Published
React TypeScript slot machine component with shuffle animation, fully customizable — a 1:1 React port of jQuery-SlotMachine by josex2r.
Maintainers
Readme
react-slotmachine 🎰
A fully customizable React + TypeScript slot machine component — a 1:1 port of jQuery-SlotMachine by josex2r.
Built for raffles, giveaways, casino games, prize draws, or any "spin to win" UX.
No jQuery dependency. Fully tree-shakeable. Ships with CSS, TypeScript types, and an imperative API.
✨ Features
- 🎯 1:1 port of jQuery-SlotMachine v6 behavior (shuffle, stop, next, prev, speed tiers, blur effects, gradient mask).
- ⚛️ React-first: Component + hook (
useSlotMachineEngine) for headless usage. - 🎨 Fully customizable: Slot size, border radius, background, box shadow, buttons, icons, labels, gradient mask, blur effects — all tweakable via props.
- 🧩 Imperative handle via
reffor programmatic control (shuffle(),stop(),next(),prev()). - 🌀 Seamless wraparound animation — items appear to spin infinitely.
- 🏷️ TypeScript — comprehensive type exports.
- 📦 Tree-shakeable ESM + CJS builds via
tsup. - 🎬 Speed tiers matching the original: fast → normal → slow → turtle → stop.
- 🎢 Smooth easing: ease-in acceleration at spin start + ease-out deceleration before landing for a natural slot-machine feel.
📦 Installation
npm install @firdaus.lubis/react-slotmachine
# or
yarn add @firdaus.lubis/react-slotmachine
# or
pnpm add @firdaus.lubis/react-slotmachinePeer dependencies: react >= 16.8.0, react-dom >= 16.8.0.
🚀 Quick start
import { SlotMachine } from '@firdaus.lubis/react-slotmachine';
import '@firdaus.lubis/react-slotmachine/styles.css';
function App() {
return (
<SlotMachine
items={['🍎 Apple', '🍌 Banana', '🍒 Cherry', '🍇 Grape', '🍊 Orange']}
options={{ active: 0, delay: 300, direction: 'up' }}
onStop={({ item, index }) => {
console.log(`🏆 Winner: ${item} (index ${index})`);
}}
/>
);
}🧩 Props
| Prop | Type | Default | Description |
|---|---|---|---|
| items | ReactNode[] | required | Array of slot items (strings, elements, or custom nodes). |
| itemKeys | (string \| number)[] | — | Optional keys/data associated 1:1 with items. Passed through in onStop as key so you can identify the winner without relying on array indices. |
| initialItem | ReactNode | — | Special item shown at starting position (index 0). Never chosen as winner — only items are eligible. Also prevents the slot from appearing empty before the first shuffle. |
| options | SlotMachineOptions | {} | Engine options (see below). |
| onStop | (winner: { item, index, key? }) => void | — | Called when the machine stops on a winner. key is the corresponding itemKeys value (if provided). |
| onShuffle | (nextIndex: number) => void | — | Called on every shuffle tick (useful for reactive UIs). |
| className | string | — | Additional CSS class for the outer wrapper. |
| style | CSSProperties | — | Inline styles for the outer wrapper. |
| slotHeight | string | '100px' | Height of the visible slot window. |
| slotWidth | string | '100%' | Width of the slot window. |
| borderRadius | string | '12px' | Border radius of the slot window. |
| slotBackground | string | '#ffffff' | Background color of the slot area. |
| slotBoxShadow | string | '0 4px 24px rgba(0,0,0,0.12)' | Box shadow of the slot window. |
| enableGradientMask | boolean | true | Fade gradient on top/bottom edges. |
| enableBlur | boolean | true | Blur effects on tiles while spinning. |
| shuffleLabel | string | 'Shuffle!' | Text for the shuffle button. |
| stopLabel | string | 'Stop!' | Text for the stop button. |
| hideButtons | boolean | false | Hide built-in buttons (use imperative handle instead). |
| shuffleButtonClassName | string | — | Custom class for the shuffle button. |
| stopButtonClassName | string | — | Custom class for the stop button. |
| buttonsClassName | string | — | Custom class for the buttons container. |
| shuffleIcon | ReactNode | 🔀 SVG | Custom icon for shuffle button. |
| stopIcon | ReactNode | ⏹ SVG | Custom icon for stop button. |
| tileClassName | string | — | Custom class applied to each tile wrapper. |
⚙️ Engine Options (SlotMachineOptions)
| Option | Type | Default | Description |
|---|---|---|---|
| active | number | 0 | Initial visible element index (0-based). |
| delay | number | 200 | Duration (ms) of each spin tick. |
| direction | 'up' \| 'down' | 'up' | Animation scroll direction. |
| randomize | (active, total) => number | uniform random | Custom function to determine the next active index. |
| easing | boolean | true | Enable ease-in (acceleration) and ease-out (deceleration) phases. Set false for constant-speed spins. |
| easeDuration | number | 600 | Duration in ms of each easing phase. Higher = longer acceleration/deceleration. |
🎮 Imperative API (via ref)
import { useRef } from 'react';
import { SlotMachine, type SlotMachineHandle } from '@firdaus.lubis/react-slotmachine';
function App() {
const machineRef = useRef<SlotMachineHandle>(null);
return (
<>
<SlotMachine ref={machineRef} items={['A', 'B', 'C']} hideButtons />
<button onClick={() => machineRef.current?.shuffle(999999)}>Spin!</button>
<button onClick={() => machineRef.current?.stop(0)}>Stop!</button>
<button onClick={() => machineRef.current?.next()}>Next</button>
<button onClick={() => machineRef.current?.prev()}>Prev</button>
</>
);
}SlotMachineHandle
| Method / Property | Type | Description |
|---|---|---|
| shuffle(spins?) | (spins?: number) => void | Start spinning. Default 5 spins. Use Infinity for endless. |
| stop(spins?) | (spins?: number) => void | Schedule stop after N more spins (0 = immediate). |
| next() | () => void | Spin to the next element. |
| prev() | () => void | Spin to the previous element. |
| active | number (readonly) | Current active index. |
| running | boolean (readonly) | Whether the machine is spinning. |
| stopping | boolean (readonly) | Whether the machine is decelerating. |
🪝 Headless usage (useSlotMachineEngine)
For full control over rendering, use the engine hook directly:
import { useSlotMachineEngine } from '@firdaus.lubis/react-slotmachine';
function CustomSlotMachine() {
const { active, running, shuffle, stop } = useSlotMachineEngine(5, {
active: 0,
delay: 300,
});
return (
<div>
<div style={{ /* your custom rendering */ }}>
Current: {active}
</div>
<button onClick={() => shuffle(10)} disabled={running}>
{running ? 'Spinning...' : 'Shuffle'}
</button>
<button onClick={() => stop(0)}>Stop</button>
</div>
);
}🎨 Customization examples
Dark themed raffle
<SlotMachine
items={['🏆 Prize 1', '💰 Prize 2', '🎁 Prize 3']}
options={{ active: 0, delay: 250 }}
slotHeight="120px"
slotWidth="400px"
borderRadius="20px"
slotBackground="#1a1a2e"
slotBoxShadow="0 0 40px rgba(255,215,0,0.3)"
shuffleLabel="Draw!"
stopLabel="Pick Winner!"
enableGradientMask={false}
/>Controlled via custom buttons
<SlotMachine
ref={machineRef}
items={names}
hideButtons
onStop={({ item }) => alert(`Winner: ${item}`)}
/>
<button onClick={() => machineRef.current?.shuffle(Infinity)}>Start</button>
<button onClick={() => machineRef.current?.stop(3)}>Stop in 3</button>Pre-determined winner (randomize)
<SlotMachine
items={users}
options={{
randomize: () => 2, // always land on index 2
}}
/>With a starting placeholder (initialItem)
<SlotMachine
initialItem={<em>🎰 Spin to win!</em>}
items={['🏆 Gold', '🥈 Silver', '🥉 Bronze']}
options={{ active: 0, delay: 300 }}
onStop={({ item, index }) => alert(`Winner: ${item}`)}
/>🧪 Development
git clone https://github.com/@firdaus.lubis/react-slotmachine.git
cd react-slotmachine
pnpm install
pnpm dev # watch mode
pnpm build # production build📄 License
MIT © ifexindonesia
🙏 Acknowledgements
This package is a React port of the excellent jQuery-SlotMachine by Jose Luis Represa. The original library is also MIT licensed.
