skip to Main Content

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


  1. Sounds like products might be nullish. Optional chaining could help here:

    - const products = inventory.data.map((product) => {
    + const products = inventory?.data?.map((product) => {
    
    Login or Signup to reply.
  2. What’s likely happening is that your products.find is not actually matching any products to your params.id. That results in product 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:

    1. Perhaps the most NextJS way of handing this, if product is not found, then return notFound: true instead of props, resulting in the 404 page
        if (!product) {
            return {
                notFound: true
            };
        }
    
    1. Return a value other than undefined if product is not found:
        return {
            props: {
                product: product ?? null,
            },
            revalidate: 60 * 60,
        };
    

    or, don’t add the product prop at all unless it’s defined

        return {
            props: {
                ...(product && { product }),
            },
            revalidate: 60 * 60,
        };
    

    Only use #2 if your client is set up to handle the case where product is missing.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search