# React shipping method selector

_Box Kite 2.2.4 · a markdown copy of https://www.box-kite.dev/blocks/react-shipping-method-selector/_

Delivery options as selectable cards in React: a native radio group, one tab stop, the chosen card styled with :has(:checked).

## Install

One command copies the files into your project, as code you then own. It needs a `components.json`, and [the shadcn registry page](https://www.box-kite.dev/registry.md) has the one-time setup.

```shell
npx shadcn@latest add https://www.box-kite.dev/r/shipping-method.json
```

[Open in v0](https://v0.dev/chat/api/open?url=https%3A%2F%2Fwww.box-kite.dev%2Fr%2Fshipping-method.json)

[The item JSON](https://www.box-kite.dev/r/shipping-method.json)

1 file · installs @box-kite/react, store-design

## Light and dark

The same component twice, each half in a theme of its own — nothing in it is written for either. [Open it full screen](https://www.box-kite.dev/blocks/react-shipping-method-selector/demo.md) to see it in yours.

## Source

The files the command writes, exactly as the preview above runs them. Every style is a prop, so there is no stylesheet to copy with them.

**box-kite/store/shipping-method.tsx**

```jsx
'use client';
import Flex from '@box-kite/react/components/flex';
import RadioGroup from '@box-kite/react/components/radioGroup';
import { Span } from '@box-kite/react/components/semantics';
import { formatPrice, type ShippingMethod as Method, shippingMethods } from './store-data';

export interface ShippingMethodProps {
  methods?: readonly Method[];
  value?: string;
  onValueChange?: (id: string) => void;
  /** Standard shipping is free above this subtotal. */
  freeStandard?: boolean;
}

/**
 * Delivery options as cards. Each card is the `<label>` of a native radio, so the whole card is the
 * target, the set is one tab stop with the arrow keys inside it, and the chosen card is styled with
 * `:has(:checked)` — the radio itself stays visible, because it is what says this is a choice of one.
 */
export default function ShippingMethod({ methods = shippingMethods, value, onValueChange, freeStandard }: ShippingMethodProps) {
  const priceOf = (method: Method) => (freeStandard && method.id === 'standard' ? 0 : method.price);

  return (
    <RadioGroup
      label="Delivery"
      name="shipping.method"
      value={value}
      defaultValue={value === undefined ? methods[0].id : undefined}
      onValueChange={(id) => id && onValueChange?.(id)}
      gap={3}
    >
      {methods.map((method) => (
        <RadioGroup.Item
          key={method.id}
          value={method.id}
          flexShrink={0}
          labelProps={{
            display: 'flex',
            ai: 'center',
            gap: 3,
            p: 4,
            b: 1,
            borderRadius: 3,
            cursor: 'pointer',
            borderColor: 'stone-300',
            bgColor: 'transparent',
            hover: { borderColor: 'stone-500' },
            has: { ':checked': { borderColor: 'stone-900', ring: 1, ringColor: 'stone-900' } },
            theme: {
              dark: {
                borderColor: 'stone-700',
                bgColor: 'transparent',
                hover: { borderColor: 'stone-500' },
                has: { ':checked': { borderColor: 'stone-100', ringColor: 'stone-100' } },
              },
            },
          }}
          label={
            <Flex flex1 jc="space-between" ai="center" gap={4}>
              <Flex d="column" gap={0.5}>
                <Span fontWeight={500}>{method.name}</Span>
                <Span fontSize={14} color="stone-600" theme={{ dark: { color: 'stone-400' } }}>
                  {method.detail}
                </Span>
              </Flex>
              <Span fontWeight={500}>{priceOf(method) ? formatPrice(priceOf(method)) : 'Free'}</Span>
            </Flex>
          }
        />
      ))}
    </RadioGroup>
  );
}
```

It stands on `store-design`, which the command installs with it. The design the store blocks and pages share: the components restyled with Box.createComponents(), the catalogue, the drawn product art, and the header and footer. [The design is on the template page.](https://www.box-kite.dev/templates/react-checkout-page.md#design)

## Used in

- [React checkout page template](https://www.box-kite.dev/templates/react-checkout-page.md)

---

_Every page: https://www.box-kite.dev/llms.txt · every prop, measured: https://www.box-kite.dev/props.md_

