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

# Plugin hooks

> Observe, block, or annotate add-to-cart from a storefront script

Plugins are for logic *around* the cart: eligibility gating, analytics, loyalty references, custom line-item properties. They live in a script you load on the storefront — **not** in the bundle’s JS asset.

## Registering

```js theme={null}
window.nameless.use({
  addToCart: {
    before(ctx) {
      /* … */
    },
    after(ctx) {
      /* … */
    },
  },
});
```

`use()` returns an unsubscribe function. You can call it before the runtime has installed: a pre-install shim queues plugins and replays them at install time, so script order doesn’t matter. `window.nameless.ready` resolves once bundle data is loaded, if you need to wait for state rather than just register.

## `addToCart.before`

Runs after selections validate, before the cart request. It can **block** the add or **attach line properties**.

| Context field | Type                                         |
| ------------- | -------------------------------------------- |
| `bundleId`    | `string`                                     |
| `snapshot`    | the bundle snapshot (read-only)              |
| `lines`       | `Array<{ selectorId, variantId, quantity }>` |

Return one of:

```ts theme={null}
undefined                                                   // no opinion
{ ok: true, lineProperties?: { [selectorId]: { [k]: string } } }
{ ok: false, code: string, message: string }                // blocks the add
```

A block surfaces to the shopper as dispatch code `PLUGIN_BLOCKED` with `"{code}: {message}"`, and remaining `before` hooks are skipped.

`lineProperties` are keyed by **selector id** and land on the matching Shopify cart line as line-item `properties`. Multiple hooks merge; later hooks win per key.

<Warning>
  Do not write the `__FB_ATC_UID` property. Knit stamps it on every line and the Shopify discount function reads it to group bundle lines and detect stale bundles. Overwriting it silently kills the bundle discount at checkout.
</Warning>

## `addToCart.after`

Fire-and-forget, runs after the cart responds. Same context plus `result`:

```ts theme={null}
result: { ok: true; lines: Line[] } | { ok: false; code: string; message: string };
```

Return nothing, or `{ warnings: string[] }` to surface messages in the host’s warning log. You cannot change the outcome from here.

## Execution semantics

| Behavior          | Detail                                                                                 |
| ----------------- | -------------------------------------------------------------------------------------- |
| Order             | Sequential, in registration order — never parallel                                     |
| Timeout           | 1500ms per hook. Exceeding it logs `HOOK_TIMEOUT` and moves on                         |
| Thrown errors     | Logged as `HOOK_THREW` and skipped — they never break the shopper’s add-to-cart        |
| Bad return values | Anything that isn’t a plain object or `void` logs `HOOK_INVALID_RESULT` and is ignored |
| Blocking          | Only an explicit `{ ok: false }` halts the pipeline                                    |
| Mid-run changes   | Hooks registered during a run don’t join that run                                      |

Because slow hooks are cut off at 1500ms, keep network calls out of `before`, or accept that a slow response is treated as “no opinion”.

## Worked example

```js theme={null}
window.nameless.use({
  addToCart: {
    async before(ctx) {
      if (!(await isEligible(ctx.bundleId))) {
        return { ok: false, code: "NOT_ELIGIBLE", message: "Bundle not available for you." };
      }
      var props = {};
      ctx.lines.forEach(function (line) {
        props[line.selectorId] = { _loyalty_ref: currentMemberId() };
      });
      return { ok: true, lineProperties: props };
    },
    after(ctx) {
      if (ctx.result.ok) {
        analytics.track("bundle_added", {
          bundleId: ctx.bundleId,
          items: ctx.result.lines.length,
        });
      }
    },
  },
});
```

To reshape the theme’s own add-to-cart when ATC override is on, see [Resolver stages](/resolver-stages).
