---
title: "Fit prompts for retailers | Snowplow Signals"
description: "Snowplow Signals tells your storefront when a cart holds one item in several sizes or a shopper keeps changing size, so it offers the fit guide first."
source: https://signals.snowplow.io/solutions/retail/sizing
---

[Retail and e-commerce](https://signals.snowplow.io/solutions/retail) / Fit prompts 

# Offer the fit guide before the order ships. 

Signals tells your storefront when a cart holds one item in several sizes, how many times this shopper has changed size, and whether their past orders came back for fit, so the prompt lands while they are still choosing.

[Start free](https://signals.snowplow.io/start) [Talk to us](https://signals.snowplow.io/start?intent=talk) 

Fewer returns

## Cut returns by settling the size question before the order is placed. 

Returns fall when the fit question gets answered in the session, while the shopper is still choosing. The returns report shows the same dress coming back in two sizes weeks later, after the parcel has gone out. Signals gives your storefront the shape of the cart this visit, how often the shopper switched size, and whether this customer's past orders of the brand came back for size. Your storefront decides which prompt to show, within what your returns policy allows.

Four ways retailers use it

01 A cart with one dress in three sizes Same dress, small, medium and large. Offer the fit guide now. 02 A shopper switching sizes on the product page Medium, then large, then medium again. Show the chart inline. 03 A customer whose last orders came back for size Two orders of this brand, both returned for fit. Suggest the size they kept. 04 Two sizes of one item at checkout Both sizes made it to checkout. Offer try-before-you-buy, or state the return window. 

[Read the docs →](https://docs.snowplow.io/docs/signals/introduction/) 

### Offer the fit guide while all three sizes are still in the cart

A shopper has added the same dress in small, medium and large. Two of them are coming back, and the store finds out when the returns land in the warehouse, after the shipping has been paid both ways. The shopper is not sure which size fits, and right now they are on the cart page with the question still open. The fit guide, or the size other shoppers kept, answers it before the order exists.

What Signals knows

`cart_skus_session` 

Every product and size variant added to the cart this visit

`size_guide_views_session` 

Times the size guide was opened this visit

What your storefront does

Shows the fit guide beside the cart line, or the size most kept by other shoppers if your merchandising rule prefers it, when one product sits in the cart in two or more sizes and the shopper has not opened the guide yet.

Define it · PythonUse it · TypeScript

cart.tsx · 19 lines Copy Copied 

```
from datetime import timedelta
from snowplow_signals import (
    Attribute, AtomicProperty, Criteria, Criterion, EntityProperty, Event, StreamAttributeGroup, domain_sessionid,
)

page_view = Event(name="page_view", vendor="com.snowplowanalytics.snowplow", version="1-0-0")
add_to_cart = Event(name="add_to_cart", vendor="com.example", version="1-0-0")

cart_skus_session = Attribute(
    name="cart_skus_session",
    type="string_list",
    events=[add_to_cart],
    aggregation="unique_list",
    property=EntityProperty(vendor="com.example", name="product", major_version=1, path="sku"),
    ttl=timedelta(hours=6),
)

size_guide_views_session = Attribute(
    name="size_guide_views_session",
    type="int32",
    events=[page_view],
    aggregation="counter",
    criteria=Criteria(all=[
        Criterion.like(AtomicProperty(name="page_url"), "%/size-guide%"),
    ]),
    ttl=timedelta(hours=6),
    default_value=0,
)

# Keyed on the visit, so the cart page asks about this session's cart.
shop_sizing_cart = StreamAttributeGroup(
    name="shop_sizing_cart",
    version=1,
    attribute_key=domain_sessionid,
    owner="you@example.com",
    attributes=[cart_skus_session, size_guide_views_session],
)
```

```
// In the code that renders the cart page, for each product in the cart
const cart = await signals.getServiceAttributes({
  name: "shop_sizing_cart",
  attribute_key: "domain_sessionid",
  identifier: sessionId,
});
// { cart_skus_session: ["dress-2041-s", "dress-2041-m", "dress-2041-l"], size_guide_views_session: 0 }

const sizes = cart.cart_skus_session
  .filter((sku) => catalog.productOf(sku) === product.id)
  .map(catalog.sizeOf);

if (sizes.length >= 2 && cart.size_guide_views_session === 0) {
  cartLine.show({
    prompt: "fit_guide",
    product: product.id,
    sizes,
  });
}
```

### Show the size chart to a shopper who keeps changing size

A shopper on the product page has picked medium, switched to large, and gone back to medium. The size chart is a link under the selector, and they have not clicked it. If they add both, the store learns about the doubt when one comes back. The doubt is visible now, in the switching, and the chart can open on the page while they are still deciding.

What Signals knows

`size_switches_session` 

Times a size was selected this visit

`sizes_tried_session` 

The sizes selected this visit

What your storefront does

Opens the size chart inline under the selector once the shopper has changed size three times, and preselects the measurements for the sizes they have been switching between.

Define it · PythonUse it · TypeScript

product.tsx · 14 lines Copy Copied 

```
from datetime import timedelta
from snowplow_signals import Attribute, Event, EventProperty, StreamAttributeGroup, domain_sessionid

size_selected = Event(name="size_selected", vendor="com.example", version="1-0-0")

size_switches_session = Attribute(
    name="size_switches_session",
    type="int32",
    events=[size_selected],
    aggregation="counter",
    ttl=timedelta(hours=6),
    default_value=0,
)

sizes_tried_session = Attribute(
    name="sizes_tried_session",
    type="string_list",
    events=[size_selected],
    aggregation="unique_list",
    property=EventProperty(vendor="com.example", name="size_selected", major_version=1, path="size"),
    ttl=timedelta(hours=6),
)

shop_sizing_switches = StreamAttributeGroup(
    name="shop_sizing_switches",
    version=1,
    attribute_key=domain_sessionid,
    owner="you@example.com",
    attributes=[size_switches_session, sizes_tried_session],
)
```

```
// In the code that renders the product page
const shopper = await signals.getServiceAttributes({
  name: "shop_sizing_switches",
  attribute_key: "domain_sessionid",
  identifier: sessionId,
});
// { size_switches_session: 3, sizes_tried_session: ["m", "l"] }

if (shopper.size_switches_session >= 3) {
  sizeChart.open({
    inline: true,
    highlight: shopper.sizes_tried_session,
  });
}
```

### Suggest the size this customer kept last time

A logged-in customer is back on a brand whose last two orders they returned, both marked as the wrong size. The returns are in the order system, and the product page shows them the same selector as everyone else. They have one order of this brand they did keep, and the size on it is the best fit guidance the store has for this customer. It can sit in the selector before they pick.

What Signals knows

`size_returns_1y` 

Orders returned for size in the last year

`skus_ordered_1y` 

Every product and size variant they ordered in the last year

`skus_returned_for_size_1y` 

The variants they sent back for size in the last year

What your storefront does

Works out which sizes of this brand the customer ordered and did not return, and preselects that size with a note that it is the one they kept, once the customer has two or more size returns in the year. The order and return events come from your order system, and the brand lookup from your catalog.

Define it · PythonUse it · TypeScript

product.tsx · 19 lines Copy Copied 

```
from datetime import timedelta
from snowplow_signals import (
    Attribute, Criteria, Criterion, EntityProperty, Event, EventProperty, StreamAttributeGroup, user_id,
)

order_placed = Event(name="order_placed", vendor="com.example", version="1-0-0")
order_returned = Event(name="order_returned", vendor="com.example", version="1-0-0")
sku = EntityProperty(vendor="com.example", name="product", major_version=1, path="sku")
returned_for_size = Criteria(all=[
    Criterion.eq(EventProperty(vendor="com.example", name="order_returned", major_version=1, path="reason"), "size"),
])

size_returns_1y = Attribute(
    name="size_returns_1y",
    type="int32",
    events=[order_returned],
    aggregation="counter",
    criteria=returned_for_size,
    ttl=timedelta(days=365),
    default_value=0,
)

skus_ordered_1y = Attribute(
    name="skus_ordered_1y",
    type="string_list",
    events=[order_placed],
    aggregation="unique_list",
    property=sku,
    ttl=timedelta(days=365),
)

skus_returned_for_size_1y = Attribute(
    name="skus_returned_for_size_1y",
    type="string_list",
    events=[order_returned],
    aggregation="unique_list",
    property=sku,
    criteria=returned_for_size,
    ttl=timedelta(days=365),
)

# Orders and returns are recorded against the account, so the key is the user id.
shop_sizing_history = StreamAttributeGroup(
    name="shop_sizing_history",
    version=1,
    attribute_key=user_id,
    owner="you@example.com",
    attributes=[size_returns_1y, skus_ordered_1y, skus_returned_for_size_1y],
)
```

```
// In the code that renders the product page, for a logged-in customer
const customer = await signals.getServiceAttributes({
  name: "shop_sizing_history",
  attribute_key: "user_id",
  identifier: userId,
});
// { size_returns_1y: 2, skus_ordered_1y: ["coat-118-m", "coat-118-s", "knit-77-s"], skus_returned_for_size_1y: ["coat-118-m", "knit-77-s"] }

const kept = customer.skus_ordered_1y
  .filter((sku) => catalog.brandOf(sku) === product.brand)
  .filter((sku) => !customer.skus_returned_for_size_1y.includes(sku))
  .map(catalog.sizeOf);

if (customer.size_returns_1y >= 2 && kept.length > 0) {
  sizeSelector.preselect({
    size: kept[kept.length - 1],
    note: "The size you kept last time",
  });
}
```

### Offer the one-size path before the shopper pays for two

Two sizes of the same jacket have reached checkout. The shopper has decided to try both at home, and the cheaper outcome for the store is for the second size to leave the order now, or to ship under terms that make the return cheap to process. The return comes in as a cost after the order ships. At checkout it is still a choice, and the policy that covers it can be offered on the page.

What Signals knows

`cart_skus_session` 

Every product and size variant added to the cart this visit

`size_guide_views_session` 

Times the size guide was opened this visit

What your storefront does

Offers try-before-you-buy on the second size where your policy allows it, or states the single-size return window beside the order summary, when two sizes of one product reach checkout. Which offer appears is your policy's call, and the storefront reads it from your returns rules.

Define it · PythonUse it · TypeScript

checkout.tsx · 16 lines Copy Copied 

```
from datetime import timedelta
from snowplow_signals import (
    Attribute, AtomicProperty, Criteria, Criterion, EntityProperty, Event, StreamAttributeGroup, domain_sessionid,
)

page_view = Event(name="page_view", vendor="com.snowplowanalytics.snowplow", version="1-0-0")
add_to_cart = Event(name="add_to_cart", vendor="com.example", version="1-0-0")

cart_skus_session = Attribute(
    name="cart_skus_session",
    type="string_list",
    events=[add_to_cart],
    aggregation="unique_list",
    property=EntityProperty(vendor="com.example", name="product", major_version=1, path="sku"),
    ttl=timedelta(hours=6),
)

size_guide_views_session = Attribute(
    name="size_guide_views_session",
    type="int32",
    events=[page_view],
    aggregation="counter",
    criteria=Criteria(all=[
        Criterion.like(AtomicProperty(name="page_url"), "%/size-guide%"),
    ]),
    ttl=timedelta(hours=6),
    default_value=0,
)

shop_sizing_checkout = StreamAttributeGroup(
    name="shop_sizing_checkout",
    version=1,
    attribute_key=domain_sessionid,
    owner="you@example.com",
    attributes=[cart_skus_session, size_guide_views_session],
)
```

```
// In the code that renders the checkout page
const cart = await signals.getServiceAttributes({
  name: "shop_sizing_checkout",
  attribute_key: "domain_sessionid",
  identifier: sessionId,
});
// { cart_skus_session: ["jacket-530-m", "jacket-530-l"], size_guide_views_session: 1 }

const doubled = order.lines.filter((line) =>
  cart.cart_skus_session.filter((sku) => catalog.productOf(sku) === line.productId).length >= 2,
);

if (doubled.length > 0) {
  const offer = returnsPolicy.allows("try_before_you_buy") ? "try_before_you_buy" : "single_size_return_window";
  checkout.show({ prompt: offer, lines: doubled });
}
```

## Try it on your own site. 

Add the SDK, define one thing you want to know about a visitor, and watch it update while you click around. The free tier runs on the real engine: no card, no sales call.

[Start free](https://signals.snowplow.io/start) [Talk to an engineer](https://signals.snowplow.io/start?intent=engineer)
