Introduction
The .reduce() method transforms an array into a single value which is perfect for summing numbers, aggregating data, or grouping items. In this tutorial, you'll explore practical examples using modern React and Next.js 15 to see it in action.
Example 1: Simple Total
This is the most basic use of .reduce() . We have an array of numbers, and we use .reduce() to calulate the total sum. It's a great starting point to understand how the accumulator sum and current value num of each iteration work together.
app/(pages)/simple/page.tsx const numbers = [ 10 , 20 , 30 ] ;
export default function SimplePage () {
const totalSum = numbers . reduce ( ( sum , num ) => sum + num , 0 ) ;
return (
< div className = "flex flex-col w-full max-w-[600px] h-[60vh] justify-center items-center mx-auto gap-4" >
< h1 className = "font-semibold text-lg" > Simple </ h1 >
< p > Total sum is: { totalSum } </ p >
</ div >
)
}
Example 2: Cart Total
We simulate a shopping cart containing items with prices. The .reduce() method totals all the price values from each product object. This is a practical pattern in eCommerce and checkout pages.
app/(pages)/total-from-cart/page.tsx const cart = [
{ id : 1 , product : "T-Shirt" , price : 20 },
{ id : 2 , product : "Shoes" , price : 65 },
{ id : 3 , product : "Hat" , price : 25 },
]
export default function TotalFromCartPage () {
const totalPrice = cart . reduce ( ( sum , item ) => sum + item . price , 0 ) ;
return (
< div className = "flex flex-col w-full max-w-[600px] h-[60vh] justify-center items-center mx-auto gap-4" >
< h1 className = "font-semibold text-lg" > Total Price from Cart </ h1 >
< p > Total price is: { totalPrice } </ p >
</ div >
)
}
Example 3: VIP Members
Here we filter and count members with a specific property: vip . The .reduce() method increases the count only when a member has vip: true . It's a clean way to count matches without using .filter().length .
app/(pages)/total-matched/page-tsx const members = [
{ id : 1 , name : "Jakkrit" , vip : false },
{ id : 2 , name : "Elizabeth" , vip : true },
{ id : 3 , name : "John" , vip : true },
{ id : 4 , name : "Ashley" , vip : false },
]
export default function TotalMatchedPage () {
const totalVipMembers = members . reduce ( ( vip , member ) => member . vip ? vip + 1 : vip , 0 ) ;
return (
< div className = "flex flex-col w-full max-w-[600px] h-[60vh] gap-4 mx-auto justify-center items-center" >
< h1 className = "font-semibold text-lg" > Total Matched Found </ h1 >
< p > Total VIP members: { totalVipMembers } persons </ p >
</ div >
)
}
Example 4: Group Products by Category
In this advanced example, we group an array of product objects by their category . This showcases how .reduce() can build more complex data structures—like transforming a flat array into a categorized object.
app/(pages)/group-items const products = [
{ id : 1 , product : "T-Shirt" , category : "Clothing" },
{ id : 2 , product : "Laptop" , category : "Electronic" },
{ id : 3 , product : "Pants" , category : "Clothing" },
{ id : 4 , product : "Headphones" , category : "Electronic" },
]
export default function GroupItemsPage () {
const groupedProducts = products . reduce ( ( group , product ) => {
if ( ! group[product . category]) {
group[product . category] = [] ;
}
group[product . category] . push (product) ;
return group ;
}, {} as Record < string , typeof products > ) ;
console . log (groupedProducts) ;
return (
< div className = "flex flex-col w-full max-w-[600px] h-[60vh] mx-auto gap-4 justify-center items-center" >
< h1 className = "font-semibold text-lg" > Group Items by Category </ h1 >
< div >
{ Object . entries (groupedProducts) . map ( ([ category , items ]) => (
< div key = { category } >
< h2 className = "font-semibold mb-2" > { category } </ h2 >
< div className = "mb-4" >
{ items . map ( i => < p key = { i . id } > { i . product } </ p > ) }
</ div >
</ div >
)) }
</ div >
</ div >
)
}
Explanation
What's happening inside the .reduce() ?
const groupedProducts = products . reduce ( ( group , product ) => {
if ( ! group[product . category]) {
group[product . category] = [] ;
}
group[product . category] . push (product) ;
return group ;
}, {} ) ;
We start with an empty object {} as the initial value for the accumulator group .
On each iteration:
if (!group[product.category]) we check if the current category (product.category ) already exists as a key in group .
So if it doesn’t exist yet, we create it and assign it an empty array. Hence: group[product.category] = []; .
Then, regardless of whether it was newly created or already there, we push the current product into the appropriate category array. Hence: group[product.category].push(product);
After all iterations, groupedProducts contains all products neatly categorized.
So based on our structure inside .reduce() , the resulting groupedProducts object will look like this:
{
Clothing : [
{ id : 1 , product : 'T-Shirt' , category : 'Clothing' },
{ id : 3 , product : 'Pants' , category : 'Clothing' }
] ,
Electronic : [
{ id : 2 , product : 'Laptop' , category : 'Electronic' },
{ id : 4 , product : 'Headphones' , category : 'Electronic' }
]
}
Extra: No initial value provided to the accumulator
const members = [
{ id : 1 , name : "Jakkrit" , role : "admin" },
{ id : 2 , name : "Elizabeth" , role : "mod" },
{ id : 3 , name : "Jake" , role : "mod" },
{ id : 4 , name : "Ashley" , role : "editor" },
{ id : 5 , name : "Jessica" , role : "marketer" },
]
export default function TestPage () {
const longestNameMember = members . reduce ( ( longestMember , currentMember ) => {
if (current . name . length > longest . name . length) {
return current ;
} else {
return longest ;
}
} ) ;
console . log ( "Longest name member is: " , longestNameMember . name) ;
return (
< div >
< h1 > No initial value provided to the accumulator </ h1 >
</ div >
) ;
}
How reduce() Works Without an Initial Value?
When you call array.reduce(callbackFunction) without providing an initialValue as the second argument:
First Iteration:
The accumulator which we've named longestMember is automatically set to the first element of the array members[0] .
The currentValue which we've named currentMember is automatically set to the second element of the array members[1] .
The reduce callback function is then executed with these two values.
Subsequent Iterations:
For all subsequent iterations, the accumulator will be whatever was returned from the previous call to the callbackFunction .
Return Single Number
const sum = [ 1 , 2 , 3 ] . reduce ( ( acc , num ) => acc + num , 0 ) ;
console . log ( "sum: " , sum) ;
Return a String
const users = [
{ name : "Alice" },
{ name : "Bob" },
] ;
const allNames = users . reduce ( ( str , user ) => str + user . name + " " , "" ) ;
console . log (allNames) ;
Return an Array
const users = [
{ email : "a@email.com" },
{ email : "b@email.com" },
] ;
const emails = users . reduce ( ( arr , user ) => [ ... arr , user . email] , [] as string [] ) ;
console . log (emails) ;
terminal [ 'a@email.com' , 'b@email.com' ]
Return an Object
const wordCount = [ "apple" , "banana" , "apple" ] . reduce ( ( acc , word ) => {
acc[word] = (acc[word] || 0 ) + 1 ;
return acc ;
}, {} as Record < string , number > ) ;
console . log (wordCount) ;
Return an Object (grouped by condition)
const users = [
{ name : "Alice" , role : "admin" },
{ name : "Bob" , role : "editor" },
] ;
const byRole = users . reduce ( ( acc , user ) => {
acc[user . role] = acc[user . role] || [] ;
acc[user . role] . push (user) ;
return acc ;
}, {} as Record < string , typeof users > ) ;
console . log (byRole) ;
terminal {
admin: [ { name: 'Alice', role: 'admin' } ],
editor: [ { name: 'Bob', role: 'editor' } ]
}
Return a Boolean
const users = [
{ name : "Alice" , active : true },
{ name : "Bob" , active : true },
] ;
const allActive = users . reduce ( ( acc , u ) => acc && u . active , true ) ;
console . log (allActive) ;