Skip to content

Optimistic cart lines can't match resolved lines: add-to-cart event detail omits selectedOptions #3853

Description

@beppek

Package: @shopify/hydrogen@0.0.0-preview-8a708a8-20260708155454 (framework-agnostic preview)

Summary

When a product form submits an add-to-cart, buildAddToCartDetail (src/core/product/product-form.ts, shipped as dist/core/product/product-form.mjs) builds the event detail the cart store uses to construct the optimistic line:

function buildAddToCartDetail(context) {
	const { selectedVariant } = context.observable.state;
	if (!selectedVariant) return void 0;
	return { products: [{
		id: selectedVariant.id,
		title: selectedVariant.title,
		product: selectedVariant.product ? { title: selectedVariant.product.title } : void 0,
		image: selectedVariant.image,
		price: selectedVariant.price
	}] };
}

selectedVariant.selectedOptions is available at this point (the product store already tracks it), but it isn't included, so the optimistic line's merchandise has no selectedOptions.

Why it matters

A cart line component that renders options as Size: Large from merchandise.selectedOptions renders correctly for resolved lines but has nothing to render during the optimistic window. The only stand-in is merchandise.title (the variant title, e.g. Large / Square), which loses the option names. The result is a visible label flicker when the mutation resolves: Large / Square becomes Size: Large · Format: Square. The workaround is to render values only everywhere, dropping option names from resolved lines too.

There's also no userland escape hatch: useCartForm().formProps() calls store.handleFormSubmit(e.nativeEvent) without exposing the eventDetail parameter, and useProductForm builds the detail internally, so an app can't augment the payload itself.

Suggested fix

Include selectedOptions: selectedVariant.selectedOptions in the buildAddToCartDetail product payload (the cart store already spreads the whole product detail into optimistic merchandise, so no consumer-side change would be needed). Alternatively or additionally, let formProps/handleFormSubmit accept a custom event detail so apps can enrich the optimistic line themselves.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions