I encountered the following error "Error: Error serializing .product returned from getStaticProps in "/products/[id]".
Reason: undefined cannot be serialized as JSON. Please use null or omit this value."
This happened when I tried creating individual product pages for my e-commerce website.
My code in "/products/[id]"
import Image from "next/image";
import { stripe } from "src/utils/stripe";
import { CheckIcon, MinusSmallIcon, PlusSmallIcon } from "@heroicons/react/24/solid"
import { formatCurrencyString, useShoppingCart } from "use-shopping-cart";
import { useState } from "react";
import { toast } from "react-hot-toast";
export default function ProductPage({ product }) {
const [count, setCount] = useState(1);
const { addItem } = useShoppingCart()
function onAddToCart(event) {
event.preventDefault();
const id = toast.loading("Adding 1 item...");
addItem(product);
toast.success(product.name, { id });
}
return (
<div className="container lg:max-w-screen-lg mx-auto py-12 px-6">
<div className="flex flex-col md:flex-row justify-between items-center space-y-8 md:space-y-0 md:space-x-12">
<div className="relatie w-72 h-72 sm:w-96 sm:h-96">
<Image src={product.image} alt={product.name} fill style={{objectFit: "contain" }} sizes="100%" priority />
</div>
<div className="w-full flex-1 max-w-md border border-opacity-50 rounded-md shadow-lg p-6 bg-whitie">
<h2 className="text-3x1 font-semibold">{product.name}</h2>
<p className="pt-2 flex items-center space-x-2">
<CheckIcon className="text-lime-500 w-5 h-5" />
<span className="font-semibold">In stock</span>
</p>
<div className="mt-4 border-t pt-4">
<p className="text-gray-500">Price: </p>
<p className="text-xl front-semibold">{formatCurrencyString({ value: product.price, currency: product.currency,})} </p>
</div>
<div className="mt-4 border-t pt-4">
<p className="text-gray-500">Quantity</p>
<div className="mt-1 flex items-center space-x-3">
<button disabled={count <= 1} onClick={() => setCount(count - 1)} className="p-1 rounded-md hover:bg-rose-100 hover:text-rose-500">
<MinusSmallIcon className="w-6 h-6 flex-shrink-0"/>
</button>
<p className="font-semibold text-xl">{count}</p>
<button onAbort={() => setCount(count + 1)} className="p-1 rounded-md hover:bg-green-100 hover:text-green-500">
<PlusSmallIcon className="w-6 h-6 flex-shrink-0"/>
</button>
</div>
</div>
<button className="w-full mt-4 border border-lime-500 py-2 px-2 bg-lime-500 hover:bg-lime-600 hover:border-lime-600 focus:ring-4 focus:ring-opacity-50 focus:ring-lime-500 text-white disabled:opacity-50 disabled:cursor-not-allowed rounded-md">
Add to cart
</button>
</div>
</div>
</div>
);
}
export async function getStaticPaths() {
const inventory = await stripe.products.list();
const paths = inventory.data.map((product) => ({
params: { id: product.id },
}));
return {
paths,
fallback: "blocking"
};
}
export async function getStaticProps({ params }) {
const inventory = await stripe.products.list({
expand: ["data.default_price"],
});
const products = inventory.data.map((product) => {
const price = product.default_price;
return {
currency: price.currency,
id: product.id,
name: product.name,
price: price.unit_amount,
image: product.images[0],
};
});
const product = products.find((product) => product.id === params.id);
return {
props: {
product,
},
revalidate: 60 * 60,
};
}
I have looked at a few solutions suggesting to JSON.parse(JSON.stringify("string")). That did not work.
I Also got this back from blackbox but the function is already asynchronous, The product
variable is still undefined when the getStaticProps
function is called. This is because the stripe.products.list()
function is asynchronous, and the getStaticProps
function is synchronous.
2
Answers
Sounds like
products
might be nullish. Optional chaining could help here:What’s likely happening is that your
products.find
is not actually matching any products to your params.id. That results inproduct
being undefined, and then NextJS doesn’t like you having an undefined value in props.A couple ways around this, depending on what you need:
notFound: true
instead of props, resulting in the 404 pageor, don’t add the product prop at all unless it’s defined
Only use #2 if your client is set up to handle the case where
product
is missing.