How to shuffle an array in JavaScript
Shuffling arrays is crucial for randomizing data presentation, creating quiz questions, implementing card games, and providing varied user experiences in JavaScript applications. With over 25 years of experience in software development and as the creator of CoreUI, I have implemented array shuffling in components like image galleries, testimonial carousels, and dashboard widgets where randomized content keeps interfaces fresh and engaging. From my extensive expertise, the most mathematically sound and efficient solution is implementing the Fisher-Yates shuffle algorithm, which ensures truly uniform random distribution. This approach is unbiased, performant, and provides the gold standard for array randomization in computer science.
Use the Fisher-Yates shuffle algorithm to randomly reorder array elements.
const array = [1, 2, 3, 4, 5]
for (let i = array.length - 1; i > 0; i--) {
const j = Math.floor(Math.random() * (i + 1))
;[array[i], array[j]] = [array[j], array[i]]
}
// Result: randomly shuffled array
The Fisher-Yates algorithm works by iterating backwards through the array, swapping each element with a randomly selected element from the remaining unshuffled portion. Starting from the last element, Math.random() * (i + 1) generates a random index from 0 to the current position, and the destructuring assignment [array[i], array[j]] = [array[j], array[i]] swaps the elements. This ensures each possible permutation has an equal probability of occurring, creating a truly uniform shuffle.
Reusable Shuffle Function
Wrapping the Fisher-Yates algorithm in a reusable function makes it easy to shuffle any array without mutating the original.
function shuffle(array) {
const shuffled = [...array]
for (let i = shuffled.length - 1; i > 0; i--) {
const j = Math.floor(Math.random() * (i + 1))
;[shuffled[i], shuffled[j]] = [shuffled[j], shuffled[i]]
}
return shuffled
}
const numbers = [1, 2, 3, 4, 5]
const randomized = shuffle(numbers)
console.log(randomized) // e.g. [3, 1, 5, 2, 4]
console.log(numbers) // [1, 2, 3, 4, 5] (unchanged)
The spread operator [...array] creates a shallow copy before shuffling, so the original array remains intact. This is the preferred approach in modern JavaScript where immutability is valued, especially in React state management where mutating arrays directly prevents re-renders.
Why sort(() => Math.random() - 0.5) Is Biased
A common but flawed approach is using sort() with a random comparator. This does not produce a uniform distribution and should be avoided.
// DO NOT USE — biased shuffle
const numbers = [1, 2, 3, 4, 5]
const bad = [...numbers].sort(() => Math.random() - 0.5)
The sort() method expects a consistent comparator — one that always returns the same result for the same pair of elements. A random comparator violates this contract, causing different sorting algorithms to produce different biases. In practice, elements near the beginning of the array tend to stay near the beginning. For a proper understanding of how sort() works and when to use it, see how to sort an array in JavaScript.
// Bias demonstration: shuffle 1,000,000 times and count first-position frequency
const counts = { 1: 0, 2: 0, 3: 0, 4: 0, 5: 0 }
for (let t = 0; t < 1_000_000; t++) {
const arr = [1, 2, 3, 4, 5]
arr.sort(() => Math.random() - 0.5)
counts[arr[0]]++
}
console.log(counts)
// Biased: some numbers appear in position 0 significantly more often
// With Fisher-Yates, each would appear ~200,000 times (20%)
Shuffling an Array of Objects
The Fisher-Yates algorithm works with any data type, including arrays of objects. This is common when randomizing content in components like the CoreUI Carousel.
const testimonials = [
{ author: 'Alice', text: 'Great product!' },
{ author: 'Bob', text: 'Easy to use.' },
{ author: 'Charlie', text: 'Highly recommend.' },
{ author: 'Diana', text: 'Excellent support.' }
]
const randomTestimonials = shuffle(testimonials)
console.log(randomTestimonials[0].author) // random author each time
Because shuffle() creates a shallow copy, the individual objects are shared between the original and shuffled arrays. This is efficient for display purposes — you are reordering references, not duplicating data.
Picking Random Elements from an Array
A partial Fisher-Yates shuffle is more efficient than a full shuffle when you only need a few random elements from a large array.
function pickRandom(array, count) {
const copy = [...array]
const result = []
for (let i = 0; i < Math.min(count, copy.length); i++) {
const j = i + Math.floor(Math.random() * (copy.length - i))
;[copy[i], copy[j]] = [copy[j], copy[i]]
result.push(copy[i])
}
return result
}
const colors = ['red', 'green', 'blue', 'yellow', 'purple', 'orange']
console.log(pickRandom(colors, 3)) // e.g. ['purple', 'red', 'orange']
This stops after count swaps instead of shuffling the entire array, giving O(k) performance where k is the number of elements you need. This is useful for displaying a random subset of items in a grid built with the CoreUI Card component.
Shuffling in React State
When shuffling arrays in React, always create a new array reference so React detects the change and re-renders.
import { useState, useCallback } from 'react'
function shuffle(array) {
const shuffled = [...array]
for (let i = shuffled.length - 1; i > 0; i--) {
const j = Math.floor(Math.random() * (i + 1))
;[shuffled[i], shuffled[j]] = [shuffled[j], shuffled[i]]
}
return shuffled
}
function Gallery({ images }) {
const [items, setItems] = useState(images)
const handleShuffle = useCallback(() => {
setItems((prev) => shuffle(prev))
}, [])
return (
<div>
<button onClick={handleShuffle}>Shuffle</button>
{items.map((img) => (
<img key={img.id} src={img.src} alt={img.alt} />
))}
</div>
)
}
The shuffle() function already returns a new array via the spread operator, so React correctly detects the state change. Wrapping the handler in useCallback prevents unnecessary re-renders of child components.
Cryptographically Secure Shuffle
For security-sensitive use cases like lottery draws or randomized access tokens, replace Math.random() with crypto.getRandomValues() to avoid predictable sequences.
function secureShuffle(array) {
const shuffled = [...array]
const randomValues = new Uint32Array(shuffled.length)
crypto.getRandomValues(randomValues)
for (let i = shuffled.length - 1; i > 0; i--) {
const j = randomValues[i] % (i + 1)
;[shuffled[i], shuffled[j]] = [shuffled[j], shuffled[i]]
}
return shuffled
}
const deck = Array.from({ length: 52 }, (_, i) => i + 1)
const shuffledDeck = secureShuffle(deck)
Math.random() uses a pseudo-random number generator that can be predicted if the seed is known. crypto.getRandomValues() uses the operating system’s entropy source, making the output unpredictable. For most UI shuffling, Math.random() is sufficient — reserve the cryptographic version for cases where fairness or security is critical. For more on array manipulation, see how to reverse an array in JavaScript.
Best Practice Note:
Always use Fisher-Yates for shuffling — never sort() with a random comparator. Wrap the algorithm in a reusable function that returns a new array to avoid mutation. For most use cases, Math.random() provides sufficient randomness. The algorithm runs in O(n) time and O(n) space (for the copy), making it efficient for arrays of any practical size.



