#Dice Roller#React

Dice Roller

Create an animated dice roller component in React with realistic rolling animations, random number generation, multiple dice support, and roll history tracking.

By Pratik RaiMedium
Dice Roller

Build a dice roller component that displays multiple dice with accurate dot patterns. The key challenges are generating random dice values, mapping numbers to visual dot patterns, and using CSS Grid to position dots correctly on each die face.

Overview

A dice roller allows users to specify how many dice to roll (1-12) and displays each die with the correct number of dots arranged in the standard pattern. Each die face shows dots in a 3×3 grid layout, matching real-world dice patterns.

Core Challenge: Dot Pattern Mapping

The most critical part is mapping each dice value (1-6) to its corresponding dot positions. Standard dice follow specific patterns that must be accurately represented.

Standard Dice Patterns

Each die face uses a 3×3 grid where dots can be placed. Here are the patterns:

1 dot (center):

┌───┬───┬───┐
│   │   │   │
├───┼───┼───┤
│   │ ● │   │
├───┼───┼───┤
│   │   │   │
└───┴───┴───┘

2 dots (diagonal):

┌───┬───┬───┐
│ ● │   │   │
├───┼───┼───┤
│   │   │   │
├───┼───┼───┤
│   │   │ ● │
└───┴───┴───┘

3 dots (diagonal):

┌───┬───┬───┐
│ ● │   │   │
├───┼───┼───┤
│   │ ● │   │
├───┼───┼───┤
│   │   │ ● │
└───┴───┴───┘

4 dots (corners):

┌───┬───┬───┐
│ ● │   │ ● │
├───┼───┼───┤
│   │   │   │
├───┼───┼───┤
│ ● │   │ ● │
└───┴───┴───┘

5 dots (corners + center):

┌───┬───┬───┐
│ ● │   │ ● │
├───┼───┼───┤
│   │ ● │   │
├───┼───┼───┤
│ ● │   │ ● │
└───┴───┴───┘

6 dots (two columns):

┌───┬───┬───┐
│ ● │   │ ● │
├───┼───┼───┤
│ ● │   │ ● │
├───┼───┼───┤
│ ● │   │ ● │
└───┴───┴───┘

Implementation: Pattern Mapping Function

We use a lookup object to map each value to its dot positions:

TSXcomponent.tsx
1const getDotPositions = (value: number): string[] => { 2 const positions: Record<number, string[]> = { 3 1: ['center'], 4 2: ['top-left', 'bottom-right'], 5 3: ['top-left', 'center', 'bottom-right'], 6 4: ['top-left', 'top-right', 'bottom-left', 'bottom-right'], 7 5: ['top-left', 'top-right', 'center', 'bottom-left', 'bottom-right'], 8 6: ['top-left', 'top-right', 'middle-left', 'middle-right', 'bottom-left', 'bottom-right'] 9 }; 10 return positions[value] || []; 11};

Key Points:

  • Each position name corresponds to a CSS Grid cell
  • The function returns an array of class names
  • Invalid values return an empty array (graceful degradation)

Random Number Generation

Generating random dice values uses Math.random() and Math.floor():

TSXcomponent.tsx
1function rollDice(count: number) { 2 return Array.from({ length: count }, () => Math.floor(Math.random() * 6) + 1); 3}

How it works:

  • Math.random() generates a number between 0 (inclusive) and 1 (exclusive)
  • Multiply by 6 to get a range of 0 to 5.999...
  • Math.floor() rounds down to integers 0-5
  • Add 1 to shift the range to 1-6

Example:

  • Math.random() = 0.730.73 × 6 = 4.38Math.floor(4.38) = 44 + 1 = 5
  • Math.random() = 0.150.15 × 6 = 0.9Math.floor(0.9) = 00 + 1 = 1

Component Architecture

The implementation follows a parent-child component pattern:

1. DiceRoller (Parent Component)

Manages the application state and user interaction:

TSXcomponent.tsx
1const DiceRoller = () => { 2 const [numberOfDice, setNumberOfDice] = useState<string>(''); 3 const [results, setResults] = useState<number[]>([]); 4 5 function handleRollDice() { 6 const count = Number(numberOfDice); 7 if (isNaN(count) || count < 1 || count > 12) { 8 return; 9 } 10 const rolled = rollDice(count); 11 setResults(rolled); 12 } 13 14 return ( 15 <div className="dice-roller-container"> 16 <input 17 type="number" 18 value={numberOfDice} 19 onChange={(e) => setNumberOfDice(e.target.value)} 20 /> 21 <button onClick={handleRollDice}>Roll Dice</button> 22 {results.length > 0 && <DiceRollerComponent results={results} />} 23 </div> 24 ); 25};

Responsibilities:

  • Manages input state (number of dice to roll)
  • Validates input (1-12 range)
  • Generates random dice values
  • Passes results to child component

State Management:

  • numberOfDice: String to allow empty input (better UX)
  • results: Array of dice values (1-6) to display

2. DiceRollerComponent (Child Component)

Renders the visual dice faces:

TSXcomponent.tsx
1const DiceRollerComponent = ({ results }: { results: number[] }) => { 2 return ( 3 <div className="dice-roller"> 4 {results.map((result, index) => { 5 const dotPositions = getDotPositions(result); 6 return ( 7 <div key={index} className="dice-face"> 8 {dotPositions.map((position, dotIndex) => ( 9 <span key={dotIndex} className={`dot ${position}`}></span> 10 ))} 11 </div> 12 ); 13 })} 14 </div> 15 ); 16};

Responsibilities:

  • Maps each dice value to dot positions
  • Renders dice faces with correct dot patterns
  • Uses CSS Grid for dot positioning

CSS Grid Layout for Dice Faces

Each die face uses a 3×3 CSS Grid to position dots accurately:

CSSstyles.css
1.dice-face { 2 width: 80px; 3 height: 80px; 4 background-color: white; 5 border-radius: 8px; 6 display: grid; 7 grid-template-columns: repeat(3, 1fr); 8 grid-template-rows: repeat(3, 1fr); 9 padding: 8px; 10}

Grid Structure:

┌─────┬─────┬─────┐
│  1  │  2  │  3  │  Row 1
├─────┼─────┼─────┤
│  4  │  5  │  6  │  Row 2
├─────┼─────┼─────┤
│  7  │  8  │  9  │  Row 3
└─────┴─────┴─────┘

Dot Positioning Classes

Each dot position maps to a specific grid cell:

CSSstyles.css
1.dot.center { 2 grid-column: 2; 3 grid-row: 2; 4} 5 6.dot.top-left { 7 grid-column: 1; 8 grid-row: 1; 9} 10 11.dot.top-right { 12 grid-column: 3; 13 grid-row: 1; 14} 15 16.dot.middle-left { 17 grid-column: 1; 18 grid-row: 2; 19} 20 21.dot.middle-right { 22 grid-column: 3; 23 grid-row: 2; 24} 25 26.dot.bottom-left { 27 grid-column: 1; 28 grid-row: 3; 29} 30 31.dot.bottom-right { 32 grid-column: 3; 33 grid-row: 3; 34}

Why CSS Grid?

  • Precise positioning: Each dot goes exactly where it should
  • Responsive: Grid cells automatically size to container
  • Clean markup: No absolute positioning or complex calculations
  • Maintainable: Easy to adjust spacing and sizing

Input Validation

The component validates user input before rolling:

TSXcomponent.tsx
1function handleRollDice() { 2 const count = Number(numberOfDice); 3 if (isNaN(count) || count < 1 || count > 12) { 4 return; // Invalid input, do nothing 5 } 6 const rolled = rollDice(count); 7 setResults(rolled); 8}

Validation Checks:

  1. isNaN(count): Ensures input is a valid number
  2. count < 1: Prevents rolling zero or negative dice
  3. count > 12: Limits to reasonable maximum (prevents performance issues)

Why String State for Input? Using useState<string>('') instead of useState<number>(0) allows:

  • Empty input field initially (better UX)
  • Users can clear the field
  • No "0" showing when component mounts
  • Natural input behavior

Rendering Multiple Dice

The component uses a responsive grid to display multiple dice:

CSSstyles.css
1.dice-roller { 2 display: grid; 3 grid-template-columns: repeat(auto-fill, minmax(80px, 1fr)); 4 gap: 20px; 5 max-width: 600px; 6 width: 100%; 7}

Features:

  • auto-fill: Automatically creates columns based on available space
  • minmax(80px, 1fr): Each die is at least 80px wide, can grow to fill space
  • Responsive: Adapts to different screen sizes
  • Centered: Max-width centers the grid on larger screens

Key Takeaways

  1. Pattern Mapping: Use a lookup object to map dice values to dot positions
  2. Random Generation: Math.floor(Math.random() * 6) + 1 generates values 1-6
  3. CSS Grid: 3×3 grid provides precise dot positioning
  4. Input Validation: Always validate user input before processing
  5. Component Separation: Parent manages state, child handles rendering
  6. Responsive Design: Use CSS Grid's auto-fill for flexible layouts

The beauty of this approach is its simplicity and accuracy. The pattern mapping ensures each die looks correct, and CSS Grid handles all the positioning automatically. Whether rolling 1 die or 12, the same logic works perfectly.

Goal: Implement a dice roller component that can be used to roll dice.

Frequently asked questions

What is a dice roller testing in a React interview?
Whether you know what does *not* belong in state. The face values are state; the total is a `reduce` over them, and the die count comes from the dropdown. Storing either separately creates a second source of truth that can disagree with the first, which is where every "why is the total wrong" bug comes from.
Why build the new roll from the count rather than the previous values?
Because it makes the dropdown work with no extra syncing. `Array.from({ length: count }, rollDie)` produces exactly the right number of dice each time, so no effect is needed to keep the array length in step with the select.
Is an index a valid key here?
Yes, and this is one of the few places it genuinely is. Dice have no identity beyond their position and the whole array is replaced on every roll, so there is no reordering for React to get wrong. Being able to say *why* it is safe here matters more than reciting the general rule.
What follow-ups come after it works?
A rolling animation, which raises whether that state belongs in React or in CSS; keeping a bounded history without mutating state; and announcing the new total to assistive technology, since a number changing silently tells a screen reader user nothing.

Related Challenges

Continue learning with these related challenges

View All
React

Image Carousel

Create an interactive image carousel in React with smooth slide transitions, navigation arrows, dot indicators, autoplay, and touch/swipe support for mobile devices.

React · JavaScriptPratik Rai ·

React

Dynamic Tic Tac Toe

Build a dynamic Tic Tac Toe game in React with customizable grid sizes, win detection algorithms, player turn management, and game reset functionality. Great for interviews.

React · JavaScriptPratik Rai ·

React

Whack A Mole

Build a fun Whack-a-Mole game in React with random mole spawning, click detection, score tracking, countdown timer, and increasing difficulty levels. Perfect for React practice.

React · JavaScriptPratik Rai ·