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

```js
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});
}

```

```ts
import {type ActionArgs, json} from '@remix-run/server-runtime';
import {
  type CartQueryData,
  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}: ActionArgs) {
  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: CartQueryData;

  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});
}

```


## Props

### CartFormProps

CartActionInputProps & CartFormCommonProps
### CartActionInputProps

CartAttributesUpdateProps | CartBuyerIdentityUpdateProps | CartCreateProps | CartDiscountCodesUpdateProps | CartLinesAddProps | CartLinesUpdateProps | CartLinesRemoveProps | CartNoteUpdateProps | CartSelectedDeliveryOptionsUpdateProps | CartMetafieldsSetProps | CartMetafieldDeleteProps | CartCustomProps
### CartAttributesUpdateProps

### action

### inputs

### AttributeInput

AttributeInput
### OtherFormData

OtherFormData
### CartBuyerIdentityUpdateProps

### action

### inputs

### CartBuyerIdentityInput

CartBuyerIdentityInput
### CartCreateProps

### action

### inputs

### CartInput

CartInput
### CartDiscountCodesUpdateProps

### action

### inputs

### CartLinesAddProps

### action

### inputs

### CartLineInput

CartLineInput
### CartLinesUpdateProps

### action

### inputs

### CartLineUpdateInput

CartLineUpdateInput
### CartLinesRemoveProps

### action

### inputs

### CartNoteUpdateProps

### action

### inputs

### CartSelectedDeliveryOptionsUpdateProps

### action

### inputs

### CartSelectedDeliveryOptionInput

CartSelectedDeliveryOptionInput
### CartMetafieldsSetProps

### action

### inputs

### MetafieldWithoutOwnerId

MetafieldWithoutOwnerId
### CartMetafieldDeleteProps

### action

### inputs

### CartCustomProps

### action

### inputs

### CartFormCommonProps

### children
Children nodes of CartForm. Children can be a render prop that receives the fetcher.
### route
The route to submit the form to. Defaults to the current route.
## Examples
Creates a form for managing cart operations. Use `CartActionInput` to accept form inputs of known type.
### CartForm using HTML input tags as form inputs

### Example

```jsx
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});
}

```

```tsx
import {type ActionArgs, json} from '@remix-run/server-runtime';
import {
  type CartQueryData,
  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}: ActionArgs) {
  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: CartQueryData;

  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});
}

```


### Custom actions

### Example

```jsx
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});
}

```

```tsx
import {type ActionArgs, json} from '@remix-run/server-runtime';
import {
  type CartQueryData,
  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}: ActionArgs) {
  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: CartQueryData;

  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});
}

```


### CartForm with fetcher

### Example

```jsx
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});
}

```

```tsx
import {useFetcher} from '@remix-run/react';
import {type ActionArgs, json} from '@remix-run/server-runtime';
import {
  type CartQueryData,
  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}: ActionArgs) {
  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: CartQueryData;

  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});
}

```