> ## Documentation Index
> Fetch the complete documentation index at: https://swish.app/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Product detail

> Add a save button or a Notify Me button to your product detail page.

The product detail page takes two kinds of placement: a **save button** (a save/unsave toggle, usually inline alongside add-to-cart) and a **Notify Me button** (a back-in-stock signup that stands in for your theme's add-to-cart while the selected variant is sold out).

By default, Swish places a save button on product detail pages through the app embed. You can customize its position and appearance using the placement API.

## Setup

Target the product form or actions container and use an inline layout to place the button in the document flow.

```js theme={null}
swish.placements.create({
  id: "pdp-save-button",
  target: {
    selector: ".product-form",
  },
  mount: {
    mode: "after",
  },
  element: {
    name: "save-button",
    props: { iconOnly: false },
  },
  layout: {
    type: "inline",
  },
});
```

To place the button inside an existing actions row:

```js theme={null}
swish.placements.create({
  id: "pdp-save-button",
  target: {
    selector: ".product-form",
  },
  mount: {
    at: { query: ".product-form__actions" },
    mode: "append",
  },
  element: {
    name: "save-button",
  },
  layout: {
    type: "inline",
    rowSelector: ".product-form__actions",
  },
});
```

## Styling

Pass design tokens under `design` to match the button to your product detail page style.

```js theme={null}
swish.placements.create({
  id: "pdp-save-button",
  target: { selector: ".product-form" },
  mount: { mode: "after" },
  element: {
    name: "save-button",
    props: { iconOnly: false },
  },
  layout: { type: "inline" },
  design: {
    background: "transparent",
    savedBackground: "transparent",
    color: "#1a1a1a",
    savedColor: "#ef4444",
    height: 48,
    radius: 8,
  },
});
```

## Button variants

Control the button appearance using element props:

```js theme={null}
element: {
  name: "save-button",
  props: {
    iconOnly: true,
    size: "lg",
    variant: "neutral",
    appearance: "plain",
  },
},
```

| Prop | Type | Default | Description |
| - | - | - | - |
| `iconOnly` | `boolean` | `false` | Show only the heart icon without text. |
| `size` | `"xs" \| "sm" \| "md" \| "lg"` | `"md"` | Button size. |
| `variant` | `"primary" \| "secondary" \| "neutral" \| "danger"` | `"primary"` | Button color variant. |
| `appearance` | `"solid" \| "glass" \| "plain"` | `"solid"` | Button style. |

## Overlay on product images

If you prefer the button to float over the product image instead of sitting inline, use an overlay or anchored layout and the On image look. A product page placement is On page by default - a heart with its label, styled like your theme's buttons - so name [`style`](https://swish.app/docs/references/sdk/placements#style) to get the icon-only chip:

```js theme={null}
swish.placements.create({
  id: "pdp-save-button",
  target: { selector: ".product__media" },
  mount: { mode: "append" },
  element: { name: "save-button", props: { iconOnly: true } },
  style: "on-image",
  layout: {
    type: "overlay",
    anchor: "top-right",
    offset: { x: 12, y: 12 },
  },
});
```

## Notify Me button

The `notify-button` element gives a shopper something to do on a sold-out variant. Point it at your theme's buy-button row and mount it after: while the selected variant is out of stock the button appears and Swish hides the element you targeted; switching to an in-stock variant restores it untouched and the notify button disappears.

It also covers the case where nothing has been picked yet. On a product that is sold out in **every** variant the button appears before the shopper chooses anything, and the dialog it opens carries a variant picker so they can say which size they want to hear about. A product with any buyable variant left is untouched, so your add-to-cart is never hidden on something the shopper could still buy. On a combined listing that read is per child product — the one the shopper is on, which is the one your buy button is for.

```js theme={null}
swish.placements.create({
  id: "pdp-notify-button",
  target: {
    selector: ".product-form__buttons",
  },
  mount: {
    mode: "after",
  },
  element: {
    name: "notify-button",
  },
});
```

> [!NOTE]
>
> What you point at is what gets hidden, so target the buy-button row itself. An
> ancestor like `.product-form` would take your variant picker with it, and the
> shopper could not switch back to a variant they can buy. Keep `mode: "after"`
> too: availability is per-variant, so `"replace"` would strip add-to-cart from
> your in-stock variants and leave nothing to restore.

Tapping it opens the Notify Me dialog, and the signup lands in [`swish.api.subscriptions`](https://swish.app/docs/references/sdk/api-client#subscriptions).

The dialog asks for the size or colour with a dropdown, one per option. To match your product page instead, set **Global → Preferences → Notify Me dialog → Variant picker** to **Pills** or **Rows**, or `ui.notifyMe.variantSelectDisplayType` to `"pills"` or `"rows"`.

Style it with the same `props` trio as the save button (`variant`, `appearance`, `size`) and these design tokens:

| Token | Type | Description |
| - | - | - |
| `background` | `string` | Button background colour. |
| `color` | `string` | Label colour. |
| `borderRadius` | `string \| number` | Corner radius. |
| `fontSize` | `string \| number` | Label size. |
| `fontWeight` | `string \| number` | Label weight. |
| `width` | `string \| number` | Blank fills the button row. |
| `height` | `string \| number` | Blank uses the button's own size. |
| `marginTop` | `string \| number` | Space above. |
| `marginBottom` | `string \| number` | Space below. |

## Further reading

* [Placements overview](https://swish.app/docs/guides/customize/placements/overview.md) for mount options, layout types, attributes, and responsive values
* [Intents](https://swish.app/docs/guides/customize/intents.md) to configure save behavior
* [`create:subscription`](https://swish.app/docs/references/sdk/intents#create-subscription) to open the Notify Me dialog yourself
* [Product detail view](https://swish.app/docs/guides/customize/views/product-detail.md#buy-buttons) for `notifyWhenSoldOut`, the same button inside Swish's own drawer
* [Placements reference](https://swish.app/docs/references/sdk/placements.md) for the full API


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.