Next.js Discord

Discord Forum

Only plain objects can be passed to Client Components from Server Components. Decimal objects not

Answered
Amorph posted this in #help-forum
Open in Discord
Answered by Arinji
id: product?.id ?? "",
View full answer

104 Replies

I have a products form where i can edit or add a new product.

When I want to go a single product page to edit it, i get this warning in console:

Warning: Only plain objects can be passed to Client Components from Server Components. Decimal objects are not supported.
  {id: ..., storeId: ..., categoryId: ..., name: ..., price: Decimal, isFeatured: ..., isArchived: ..., sizeId: ..., colorId: ..., createdAt: ..., updatedAt: ..., images: ...}
Product page is:
import ProductsForm from "@/components/products-form";
import {
  getCategories,
  getColors,
  getProductWithOnlyImagesRelation,
  getSizes,
} from "@/server/data-access-layer";

export default async function ProductPage({
  params,
}: {
  params: { productId: string; storeId: string };
}) {
  const product = await getProductWithOnlyImagesRelation(params.productId);

  const formattedProduct = {
    ...product,
    price: product?.price.toNumber(),
  };

  const categories = await getCategories(params.storeId);

  const sizes = await getSizes(params.storeId);

  const colors = await getColors(params.storeId);
  return (
    <div className="flex-col">
      <div className="flex-1 space-y-4 p-8 pt-6">
        <ProductsForm
          categories={categories}
          colors={colors}
          sizes={sizes}
          initialData={formattedProduct}
        />
      </div>
    </div>
  );
}
Is there a better way than formatting the product and attaching it in the initialData prop instead of the normal product?
If I use formattedProduct instead, I get this from initial data prop:
Type '{ price: number | undefined; images?: { id: string; productId: string; url: string; createdAt: Date; updatedAt: Date; }[] | undefined; id?: string | undefined; storeId?: string | undefined; ... 7 more ...; updatedAt?: Date | undefined; }' is not assignable to type '{ id: string; storeId: string; categoryId: string; name: string; price: Decimal; isFeatured: boolean; isArchived: boolean; sizeId: string; colorId: string; createdAt: Date; updatedAt: Date; } & { ...; }'.
  Type '{ price: number | undefined; images?: { id: string; productId: string; url: string; createdAt: Date; updatedAt: Date; }[] | undefined; id?: string | undefined; storeId?: string | undefined; ... 7 more ...; updatedAt?: Date | undefined; }' is not assignable to type '{ id: string; storeId: string; categoryId: string; name: string; price: Decimal; isFeatured: boolean; isArchived: boolean; sizeId: string; colorId: string; createdAt: Date; updatedAt: Date; }'.
    Types of property 'id' are incompatible.
      Type 'string | undefined' is not assignable to type 'string'.
        Type 'undefined' is not assignable to type 'string'.ts(2322)
types.ts(169, 3): The expected type comes from property 'initialData' which is declared here on type 'IntrinsicAttributes & ProductFormProps'
bump
bump
bump
bump
bump
bump
wow props for following the rules ngl
cant you just use Numbers instead of.. Decimal?
Or if you need to use Decimal, the issue is that Decimals cant be serialized in js atm
so pass it as a string to the client component
decimalValue.toString()
then you can convert back like this
const decimal = new Decimal(decimalAsString);
@Amorph
@Arinji Or if you need to use Decimal, the issue is that Decimals cant be serialized in js atm
yes i know, hence why i formatted my product to number
import ProductsForm from "@/components/products-form";
import {
  getCategories,
  getColors,
  getProductWithOnlyImagesRelation,
  getSizes,
} from "@/server/data-access-layer";

export default async function ProductPage({
  params,
}: {
  params: { productId: string; storeId: string };
}) {
  const product = await getProductWithOnlyImagesRelation(params.productId);

  const formattedProduct = {
    ...product,
    price: product?.price.toNumber(),
  };

  const categories = await getCategories(params.storeId);

  const sizes = await getSizes(params.storeId);

  const colors = await getColors(params.storeId);
  return (
    <div className="flex-col">
      <div className="flex-1 space-y-4 p-8 pt-6">
        <ProductsForm
          categories={categories}
          colors={colors}
          sizes={sizes}
          initialData={formattedProduct}
        />
      </div>
    </div>
  );
}
oh
to number or can also do string as u said since those can be passed to client components
but
Warning: Only plain objects can be passed to Client Components from Server Components. Decimal objects are not supported.

You dont see this anymore then right?
yes i have problem with the types now
if i hover over initialData prop
Type '{ price: number | undefined; images?: { id: string; productId: string; url: string; createdAt: Date; updatedAt: Date; }[] | undefined; id?: string | undefined; storeId?: string | undefined; ... 7 more ...; updatedAt?: Date | undefined; }' is not assignable to type '{ id: string; storeId: string; categoryId: string; name: string; price: Decimal; isFeatured: boolean; isArchived: boolean; sizeId: string; colorId: string; createdAt: Date; updatedAt: Date; } & { ...; }'.
Type '{ price: number | undefined; images?: { id: string; productId: string; url: string; createdAt: Date; updatedAt: Date; }[] | undefined; id?: string | undefined; storeId?: string | undefined; ... 7 more ...; updatedAt?: Date | undefined; }' is not assignable to type '{ id: string; storeId: string; categoryId: string; name: string; price: Decimal; isFeatured: boolean; isArchived: boolean; sizeId: string; colorId: string; createdAt: Date; updatedAt: Date; }'.
Types of property 'id' are incompatible.
Type 'string | undefined' is not assignable to type 'string'.
Type 'undefined' is not assignable to type 'string'.ts(2322)
types.ts(169, 3): The expected type comes from property 'initialData' which is declared here on type 'IntrinsicAttributes & ProductFormProps'
and if i hover over initial data, it will show like this:
show me <ProductsForm>
(property) initialData: ({
id: string;
storeId: string;
categoryId: string;
name: string;
price: Decimal;
isFeatured: boolean;
isArchived: boolean;
sizeId: string;
colorId: string;
createdAt: Date;
updatedAt: Date;
} & {
...;
}) | null
this is Products form
types are:
export type ProductFormProps = {
  initialData: (Product & { images: Image[] }) | null;
  categories: Category[];
  colors: Color[];
  sizes: Size[];
};
and my product model
model Product {
  id String @id @default (uuid())
  storeId String
  store Store @relation("StoreToProduct", fields: [storeId], references: [id])
  categoryId String
  category Category @relation("CategoryToProduct", fields: [categoryId], references: [id])
  name String
  price Decimal
  isFeatured Boolean @default(false)
  isArchived Boolean @default(false)
  sizeId String
  size Size @relation(fields: [sizeId],references: [id])
  colorId String
  color Color @relation(fields: [colorId], references: [id])
  images Image[]
  orderItems OrderItem[]
  createdAt DateTime @default(now())
  updatedAt DateTime @updatedAt

  @@index([storeId])
  @@index([categoryId])
  @@index([sizeId])
  @@index([colorId])
}
chill
lol
in case ur next questions would be how props look lol
its an easy fix, so basically
const product = await getProductWithOnlyImagesRelation(params.productId);
hover over product
and tell me what you see for the type of id
string
or u mean type of price?
just string or string | undefined
just string
const product: ({
images: {
id: string;
productId: string;
url: string;
createdAt: Date;
updatedAt: Date;
}[];
} & {
id: string;
storeId: string;
categoryId: string;
name: string;
price: Decimal;
isFeatured: boolean;
isArchived: boolean;
sizeId: string;
colorId: string;
createdAt: Date;
updatedAt: Date;
}) | null
thats how it looks
createdAt: Date; updatedAt: Date; }'.
Types of property 'id' are incompatible.
Type 'string | undefined' is not assignable to type 'string'.
Type 'undefined' is not assignable to type 'string'.ts(2322)
one of your ids are undefined
real quick, paste this
  const formattedProduct = {
    ...product,
    id: product?.id ?? "",
    price: product?.price.toNumber(),
  };
now it continues to the next one
Types of property 'storeId' are incompatible.
Type 'string | undefined' is not assignable to type 'string'.
Type 'undefined' is not assignable to type 'string'.ts(2322)
but see the issue now?
cause like
product
can be null
got that right @Amorph
yes
now null isnt an object, it dosent have properties. hence why null.id is undefined
you need to add a like checker for it.
if there is initial data then it will use initial data
but it can be null
mhm
yes.
like i have price/id, but do for all
yes.
id: product?.id ?? "",
Answer
if the id exists, its gonna use it.. if its undefined.. it will use ""
ok, let me do this and get back to u
sure
this is why we love typescript ❤️
@Arinji ok now i got this
Types of property 'price' are incompatible.
Type 'number' is not assignable to type 'Decimal'.ts(2322)
const formattedProduct = {
...product,
id: product?.id ?? "",
storeId: product?.storeId ?? "",
categoryId: product?.categoryId ?? "",
name: product?.name ?? "",
price: product?.price.toNumber() ?? 0,
};
price: Decimal;
price: product?.price.toNumber(),
see the issue? :D
if im understanding right, im passing a number, but my product form props are using the ProductFormProps types
and initial data type is made of Product model which has price: decimal
so im not sure to go around this, since i want to keep decimal in my model
make initial data accept a Number
then convert it back to Decimal
once you are in the client component
initialData: (Product & { images: Image[] }) | null;
@Arinji u mean this one right?
and instead of "Product" ill just write the fields myself
and have the price be number not decimal
yea
@Arinji when it comes to type Date
what would the empty value be here?
as in, for strings its ""
but what is it for a Date type?
new Date(0) to set it to unix epoch or new Date() ?
Set Dates to strings by .getTime()
It will return back in ms
As a string
@Arinji like this? createdAt: product?.createdAt.getTime().toString() ?? "",
because they will still be incompatible
actually found much easier way:D instead of accounting for all the variables, i just conditionally format my product only if i already have some data in it
const formattedProduct = product
    ? {
        ...product,
        price: product?.price.toNumber() ?? 0,
      }
    : null;
thanks for all ur help nonetheless
Sure, make your types as needed