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

# Hide and relabel cart lines

> A Cart SDK use case using line transforms and comparators to hide app-injected lines, rename them, and control the order they render in.

Hide lines the shopper shouldn't see, rename lines that read badly, and control the order everything renders in, all without touching what's actually in the cart.

This replaces the older pattern of hiding cart elements with CSS or `style.display = 'none'`. A line transform is applied every time the cart renders, so it survives updates, re-renders, and drawer re-opens.

## Hide a line

```js theme={"theme":{"light":"snazzy-light","dark":"github-dark"}}
// Hide free lines from the drawer. The cart total is unchanged.
window.aftersell.cart.hooks.registerLineTransform((line) => {
  if (line.finalLinePrice === 0) {
    line.setHidden(true);
  }
});
```

<Warning>
  **Hiding is not removing.** A hidden line stays in the shopper's cart, stays in the total, and goes through to checkout; it just isn't drawn in the drawer. It does drop out of `getCart().items` and `itemCount`, so your own code stops seeing it too. If you want it gone for real, use [`removeItem`](/aftersell/cart/sdk-actions#removeitemkey).
</Warning>

Common things worth hiding:

```js theme={"theme":{"light":"snazzy-light","dark":"github-dark"}}
window.aftersell.cart.hooks.registerLineTransform((line) => {
  // An app-injected line, tagged with a private property.
  if (line.properties?._bundle_child) {
    line.setHidden(true);
  }

  // A specific SKU that shouldn't be shopper-managed.
  if (line.variantId === HIDDEN_VARIANT_ID) {
    line.setHidden(true);
  }

  // Gift cards issued by a loyalty app.
  if (line.isGiftCard && line.finalLinePrice === 0) {
    line.setHidden(true);
  }
});
```

## Relabel a line

```js theme={"theme":{"light":"snazzy-light","dark":"github-dark"}}
window.aftersell.cart.hooks.registerLineTransform((line) => {
  // Flag discounted lines.
  if (line.finalLinePrice < line.originalLinePrice) {
    line.setTitle(`${line.title} (On sale)`);
  }

  // Make the subscription cadence read naturally.
  if (line.sellingPlan) {
    line.setVariantTitle(`Delivered ${line.sellingPlan.name.toLowerCase()}`);
  }

  // Drop a meaningless variant label.
  if (line.variantTitle === 'Default Title') {
    line.setVariantTitle(null);
  }
});
```

The four setters available on a line:

| Setter                            | Effect                                                                                                                           |
| --------------------------------- | -------------------------------------------------------------------------------------------------------------------------------- |
| `setHidden(bool)`                 | Hide the line from the drawer.                                                                                                   |
| `setTitle(string)`                | Change the displayed title.                                                                                                      |
| `setVariantTitle(string \| null)` | Change the displayed variant label. `null` removes it.                                                                           |
| `setInternalProperties(obj)`      | Merge render-only properties, for a [Custom code block](/aftersell/cart/custom-code-blocks) to read. Never persisted to Shopify. |

## Control the order

A comparator takes the same shape `Array.prototype.sort` expects, and runs after hiding and renaming:

```js theme={"theme":{"light":"snazzy-light","dark":"github-dark"}}
// Subscriptions first, then everything else.
window.aftersell.cart.hooks.registerLineComparator((lineA, lineB) => {
  return (lineB.sellingPlan ? 1 : 0) - (lineA.sellingPlan ? 1 : 0);
});
```

```js theme={"theme":{"light":"snazzy-light","dark":"github-dark"}}
// Push free gifts and add-ons to the bottom.
window.aftersell.cart.hooks.registerLineComparator((lineA, lineB) => {
  return (lineA.finalLinePrice === 0 ? 1 : 0) - (lineB.finalLinePrice === 0 ? 1 : 0);
});
```

Return `0` for pairs you don't care about. Comparators compose as tie-breakers, so the first to return a non-zero value decides that pair and returning `0` hands the decision to the next one instead of forcing an order on it.

## Hide a whole block, not a line

A line transform works on cart lines. To show different **block** settings by country, market, or currency (for example different Rewards tiers), use [conditions in the cart editor](/aftersell/cart/blocks-overview#show-or-hide-by-market-country-or-currency). No code required, and it survives cart redesigns. Cart total and cart contents are **not** editor condition types.

Reach for the SDK when your rule is something conditions can't express (product IDs in the cart, a custom total, and so on). In that case, query the [shadow root](/aftersell/cart/sdk-overview#shadowroot) for a public `cart-external-*` class:

```js theme={"theme":{"light":"snazzy-light","dark":"github-dark"}}
window.aftersell.cart.events.on('cart_updated', (state) => {
  const root = window.aftersell.cart.shadowRoot;
  if (!root) return;

  const rewards = root.querySelector('.cart-external-rewards');
  if (!rewards) return;

  const hasExcluded = state.items.some((line) => EXCLUDED_PRODUCT_IDS.includes(line.productId));
  rewards.style.display = hasExcluded ? 'none' : '';
});
```

<Warning>
  This runs after each render, so the block can flash into view before your code hides it. Conditional rendering doesn't have that problem, so prefer it wherever it fits.
</Warning>

## Things to get right

* **Transforms are set-up calls.** Register them at the top of your Initialization script; no `ready()` needed.
* **Everyone's transforms run.** Yours composes with any registered by other apps. You can't replace theirs, and they can't drop yours.
* **A transform can't change price, quantity, or identity.** It only changes what renders. Use the [actions](/aftersell/cart/sdk-actions) for real changes.
* **A transform that throws is skipped silently.** The rest still run. Check [`aftersellCartDebugEvents`](/aftersell/cart/sdk-overview#debugging) while developing.
* **`registerLineTransform` returns an unregister function**, if you need to undo it later.

## Where to go next

* **[Hooks](/aftersell/cart/sdk-hooks)**: the full hook reference.
* **[Cart object](/aftersell/cart/sdk-cart-object)**: every field you can branch on.
* **[Custom CSS](/aftersell/cart/custom-css)**: the `cart-external-*` class convention.
