Skip to main content

CartForm
component

Creates a form for managing cart operations. Use CartActionInput to accept form inputs of known type.

Anchor to CartActionInputProps

CartActionInputProps

| | | | | | | | | | | | | | |
ReactNode | ((fetcher: FetcherWithComponents<any>) => ReactNode)
required

Children nodes of CartForm. Children can be a render prop that receives the fetcher.

string

Optional key to use for the fetcher.

string

The route to submit the form to. Defaults to the current route.

Was this section helpful?

Example

import {data} from 'react-router';
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 data(result, {status, headers});
}

Examples of various ways to use the CartForm component.

Anchor to example-cartform-using-html-input-tags-as-form-inputsCartForm using HTML input tags as form inputs

Use HTML input tags with CartForm to accept form inputs.

Was this section helpful?

Example

import {data} from 'react-router';
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 data(result, {status, headers});
}

Create custom actions to accept form inputs of unknown type. Just prepend Custom in front of your custom action name.

Was this section helpful?

Example

import {data} from 'react-router';
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 data(result, {status, headers});
}

Use CartForm with a fetcher to manually submit the form. An example usage is to submit the form on changes to the state of a checkbox.

When using fetcher to submit, make sure to have a CartForm.INPUT_NAME data key and its data should be a JSON stringify object.

Was this section helpful?

Example

import {useFetcher} from 'react-router';
import {data} from 'react-router';
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 data(result, {status, headers});
}