Creates a form for managing cart operations. Use `CartActionInput` to accept form inputs of known type.
import {json} from '@remix-run/server-runtime';
import {CartForm} from '@shopify/hydrogen';
import invariant from 'tiny-invariant';
export default function Cart() {
return (
<CartForm
action={CartForm.ACTIONS.LinesUpdate}
inputs={{
lines: [
{
id: 'gid://shopify/CartLine/123456789',
quantity: 3,
},
],
other: 'data',
}}
>
<button>Quantity up</button>
</CartForm>
);
}
export async function action({request, context}) {
const {cart} = context;
const formData = await request.formData();
const {action, inputs} = CartForm.getFormInput(formData);
let status = 200;
let result;
if (action === CartForm.ACTIONS.LinesUpdate) {
result = await cart.updateLines(inputs.lines);
} else {
invariant(false, `${action} cart action is not defined`);
}
const headers = cart.setCartId(result.cart.id);
return json(result, {status, headers});
}
import {type ActionFunctionArgs, json} from '@remix-run/server-runtime';
import {
type CartQueryDataReturn,
type HydrogenCart,
CartForm,
} from '@shopify/hydrogen';
import invariant from 'tiny-invariant';
export default function Cart() {
return (
<CartForm
action={CartForm.ACTIONS.LinesUpdate}
inputs={{
lines: [
{
id: 'gid://shopify/CartLine/123456789',
quantity: 3,
},
],
other: 'data',
}}
>
<button>Quantity up</button>
</CartForm>
);
}
export async function action({request, context}: ActionFunctionArgs) {
const cart = context.cart as HydrogenCart;
// cart is type HydrogenCart or HydrogenCartCustom
// Declare cart type in remix.env.d.ts for interface AppLoadContext to avoid type casting
// const {cart} = context;
const formData = await request.formData();
const {action, inputs} = CartForm.getFormInput(formData);
let status = 200;
let result: CartQueryDataReturn;
if (action === CartForm.ACTIONS.LinesUpdate) {
result = await cart.updateLines(inputs.lines);
} else {
invariant(false, `${action} cart action is not defined`);
}
const headers = cart.setCartId(result.cart.id);
return json(result, {status, headers});
}
CartActionInputProps & CartFormCommonProps
CartAttributesUpdateProps | CartBuyerIdentityUpdateProps | CartCreateProps | CartDiscountCodesUpdateProps | CartLinesAddProps | CartLinesUpdateProps | CartLinesRemoveProps | CartNoteUpdateProps | CartSelectedDeliveryOptionsUpdateProps | CartMetafieldsSetProps | CartMetafieldDeleteProps | CartCustomProps
AttributeInput
OtherFormData
CartBuyerIdentityInput
CartInput
CartLineInput & { selectedVariant?: unknown; }
CartLineInput
CartLineUpdateInput
CartSelectedDeliveryOptionInput
MetafieldWithoutOwnerId
Children nodes of CartForm. Children can be a render prop that receives the fetcher.
The route to submit the form to. Defaults to the current route.
Optional key to use for the fetcher.
Creates a form for managing cart operations. Use `CartActionInput` to accept form inputs of known type.
import {json} from '@remix-run/server-runtime';
import {CartForm} from '@shopify/hydrogen';
import invariant from 'tiny-invariant';
export default function Note() {
return (
<CartForm action={CartForm.ACTIONS.NoteUpdate}>
<input type="text" name="note" />
<button>Update Note</button>
</CartForm>
);
}
export async function action({request, context}) {
const cart = context.cart;
const formData = await request.formData();
const {action, inputs} = CartForm.getFormInput(formData);
let status = 200;
let result;
if (action === CartForm.ACTIONS.NoteUpdate) {
result = await cart.updateNote(inputs.note);
} else {
invariant(false, `${action} cart action is not defined`);
}
const headers = cart.setCartId(result.cart.id);
return json(result, {status, headers});
}
import {type ActionFunctionArgs, json} from '@remix-run/server-runtime';
import {
type CartQueryDataReturn,
type HydrogenCart,
CartForm,
} from '@shopify/hydrogen';
import invariant from 'tiny-invariant';
export default function Note() {
return (
<CartForm action={CartForm.ACTIONS.NoteUpdate}>
<input type="text" name="note" />
<button>Update Note</button>
</CartForm>
);
}
export async function action({request, context}: ActionFunctionArgs) {
const cart = context.cart as HydrogenCart;
// cart is type HydrogenCart or HydrogenCartCustom
// Declare cart type in remix.env.d.ts for interface AppLoadContext to avoid type casting
// const {cart} = context;
const formData = await request.formData();
const {action, inputs} = CartForm.getFormInput(formData);
let status = 200;
let result: CartQueryDataReturn;
if (action === CartForm.ACTIONS.NoteUpdate) {
result = await cart.updateNote(inputs.note);
} else {
invariant(false, `${action} cart action is not defined`);
}
const headers = cart.setCartId(result.cart.id);
return json(result, {status, headers});
}
import {json} from '@remix-run/server-runtime';
import {CartForm} from '@shopify/hydrogen';
import invariant from 'tiny-invariant';
export default function Cart() {
return (
<CartForm
action="CustomEditInPlace"
inputs={{
addLines: [
{
merchandiseId: 'gid://shopify/Product/123456789',
quantity: 1,
},
],
removeLines: ['gid://shopify/CartLine/123456789'],
}}
>
<button>Green color swatch</button>
</CartForm>
);
}
export async function action({request, context}) {
const {cart} = context;
const formData = await request.formData();
const {action, inputs} = CartForm.getFormInput(formData);
let status = 200;
let result;
if (action === 'CustomEditInPlace') {
result = await cart.addLines(inputs.addLines);
result = await cart.removeLines(inputs.removeLines);
} else {
invariant(false, `${action} cart action is not defined`);
}
const headers = cart.setCartId(result.cart.id);
return json(result, {status, headers});
}
import {type ActionFunctionArgs, json} from '@remix-run/server-runtime';
import {
type CartQueryDataReturn,
type HydrogenCart,
CartForm,
} from '@shopify/hydrogen';
import {type CartLineInput} from '@shopify/hydrogen-react/storefront-api-types';
import invariant from 'tiny-invariant';
export default function Cart() {
return (
<CartForm
action="CustomEditInPlace"
inputs={{
addLines: [
{
merchandiseId: 'gid://shopify/Product/123456789',
quantity: 1,
},
],
removeLines: ['gid://shopify/CartLine/123456789'],
}}
>
<button>Green color swatch</button>
</CartForm>
);
}
export async function action({request, context}: ActionFunctionArgs) {
const cart = context.cart as HydrogenCart;
// cart is type HydrogenCart or HydrogenCartCustom
// Declare cart type in remix.env.d.ts for interface AppLoadContext to avoid type casting
// const {cart} = context;
const formData = await request.formData();
const {action, inputs} = CartForm.getFormInput(formData);
let status = 200;
let result: CartQueryDataReturn;
if (action === 'CustomEditInPlace') {
result = await cart.addLines(inputs.addLines as CartLineInput[]);
result = await cart.removeLines(inputs.removeLines as string[]);
} else {
invariant(false, `${action} cart action is not defined`);
}
const headers = cart.setCartId(result.cart.id);
return json(result, {status, headers});
}
import {useFetcher} from '@remix-run/react';
import {json} from '@remix-run/server-runtime';
import {CartForm} from '@shopify/hydrogen';
import invariant from 'tiny-invariant';
export function ThisIsGift({metafield}) {
const fetcher = useFetcher();
const buildFormInput = (event) => ({
action: CartForm.ACTIONS.MetafieldsSet,
inputs: {
metafields: [
{
key: 'custom.gift',
type: 'boolean',
value: event.target.checked.toString(),
},
],
},
});
return (
<div>
<input
checked={metafield?.value === 'true'}
type="checkbox"
id="isGift"
onChange={(event) => {
fetcher.submit(
{
[CartForm.INPUT_NAME]: JSON.stringify(buildFormInput(event)),
},
{method: 'POST', action: '/cart'},
);
}}
/>
<label htmlFor="isGift">This is a gift</label>
</div>
);
}
export async function action({request, context}) {
const {cart} = context;
const formData = await request.formData();
const {action, inputs} = CartForm.getFormInput(formData);
let status = 200;
let result;
if (action === CartForm.ACTIONS.MetafieldsSet) {
result = await cart.setMetafields(inputs.metafields);
} else {
invariant(false, `${action} cart action is not defined`);
}
const headers = cart.setCartId(result.cart.id);
return json(result, {status, headers});
}
import {useFetcher} from '@remix-run/react';
import {type ActionFunctionArgs, json} from '@remix-run/server-runtime';
import {
type CartQueryDataReturn,
type HydrogenCart,
CartForm,
type CartActionInput,
} from '@shopify/hydrogen';
import invariant from 'tiny-invariant';
import type {Cart} from '@shopify/hydrogen/storefront-api-types';
export function ThisIsGift({metafield}: {metafield: Cart['metafield']}) {
const fetcher = useFetcher();
const buildFormInput: (
event: React.ChangeEvent<HTMLInputElement>,
) => CartActionInput = (event) => ({
action: CartForm.ACTIONS.MetafieldsSet,
inputs: {
metafields: [
{
key: 'custom.gift',
type: 'boolean',
value: event.target.checked.toString(),
},
],
},
});
return (
<div>
<input
checked={metafield?.value === 'true'}
type="checkbox"
id="isGift"
onChange={(event) => {
fetcher.submit(
{
[CartForm.INPUT_NAME]: JSON.stringify(buildFormInput(event)),
},
{method: 'POST', action: '/cart'},
);
}}
/>
<label htmlFor="isGift">This is a gift</label>
</div>
);
}
export async function action({request, context}: ActionFunctionArgs) {
const cart = context.cart as HydrogenCart;
// cart is type HydrogenCart or HydrogenCartCustom
// Declare cart type in remix.env.d.ts for interface AppLoadContext to avoid type casting
// const {cart} = context;
const formData = await request.formData();
const {action, inputs} = CartForm.getFormInput(formData);
let status = 200;
let result: CartQueryDataReturn;
if (action === CartForm.ACTIONS.MetafieldsSet) {
result = await cart.setMetafields(inputs.metafields);
} else {
invariant(false, `${action} cart action is not defined`);
}
const headers = cart.setCartId(result.cart.id);
return json(result, {status, headers});
}