Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 23 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,29 @@ Preview URL: https://skill-deploy-abc123.vercel.app
Claim URL: https://vercel.com/claim-deployment?code=...
```

### nosto

Frontend integration guide for [Nosto](https://www.nosto.com/), the e-commerce personalization platform. Covers the `nostojs` global, the `@nosto/nosto-js` npm package, and `@nosto/nosto-react`.

**Use when:**
- Implementing or debugging Nosto on a storefront
- Migrating from Page Tagging to the Session API (SPA / headless)
- Sending Nosto events (`viewProduct`, `viewCategory`, `viewCart`, `viewSearch`, `addOrder`)
- Rendering recommendation placements and wiring add-to-cart attribution
- Integrating Nosto into a React / Next.js app with `@nosto/nosto-react`
- Gating Nosto behind GDPR / cookie consent

**Topics covered:**
- Initialization (`init`, the legacy stub, custom script loaders, Shopify Markets)
- Session API chain (`defaultSession().viewX(...).setPlacements([...]).load()`)
- Order tracking and conversion attribution
- Recommendation placements, dynamic filtering, `prerender` / `postrender` callbacks
- Customer identification (with PII / `customer_reference` rules)
- Nosto Search GraphQL
- Common gotchas: mixing Page Tagging with Session API, double-tracking, empty placements, SSR limitations

**Sources:** [docs.nosto.com/techdocs](https://docs.nosto.com/techdocs), [nosto-js typedoc](https://nosto.github.io/nosto-js/modules.html).

## Installation

```bash
Expand Down
143 changes: 143 additions & 0 deletions skills/nosto/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
---
name: nosto
description: Nosto frontend JavaScript SDK and `@nosto/nosto-js` package — initialization, session events (viewProduct, viewCategory, viewCart, viewSearch, addOrder), recommendation placements, cart and customer tracking, search, and SPA/React integration. Use when implementing or debugging Nosto on a storefront, integrating product recommendations, sending Nosto events, wiring Nosto into a React/Next.js/headless app, or working with the `nostojs` global, the `@nosto/nosto-js` npm package, or `@nosto/nosto-react`. Triggers on mentions of Nosto, `nostojs`, `nosto-js`, Nosto recommendations, Nosto placements, or Nosto session API.
license: MIT
metadata:
author: community
version: "1.0.0"
---

# Nosto Frontend JS APIs

Reference for integrating Nosto's frontend JavaScript SDK on a storefront. Covers the legacy `nostojs` global, the modern `@nosto/nosto-js` npm package, the `@nosto/nosto-react` provider, and the underlying session/recommendation APIs.

## When to Apply

- Adding Nosto to a Shopify, BigCommerce, Magento, or custom storefront
- Migrating from Page Tagging to the Session API (SPA/headless)
- Sending Nosto events (`viewProduct`, `viewCategory`, `viewCart`, `viewSearch`, `addOrder`)
- Rendering recommendation placements and attributing add-to-cart events
- Wiring Nosto into React/Next.js with `@nosto/nosto-react`
- Implementing GDPR/cookie-consent gating around the Nosto loader
- Debugging empty placements, double-tracking, or broken attribution

## Two Integration Modes — Pick One Per Page

| Mode | Use it when | Key calls |
|---|---|---|
| **Page Tagging** | Server-rendered storefront with `<div class="nosto_*">` tags in the DOM | The script auto-reads tagging; `api.customer(...)`, `api.resendCartTagging()` |
| **Session API** | SPA, headless, or any app where you build state imperatively per route | `api.defaultSession().viewX(...).setPlacements([...]).load()` |

**Never mix both on the same page** — produces double-tracked sessions or empty placements. See `references/patterns-gotchas.md`.

## Quick Reference

### 1. Load the script

Modern (recommended):

```ts
import { init, nostojs } from "@nosto/nosto-js";
await init({ merchantId: "shopify-12345" });
```

Legacy embed (when modifying a theme directly):

```html
<script>
(function(){var n="nostojs";window[n]=window[n]||function(c){
(window[n].q=window[n].q||[]).push(c);};})();
</script>
<script src="https://connect.nosto.com/include/shopify-12345" async></script>
```

Full loader patterns, custom script loaders, Shopify International, and consent-gated loading: `references/initialization.md`.

### 2. Session API — every route change

```js
nostojs(api =>
api.defaultSession()
.viewProduct("product-id")
.setPlacements(["productpage-nosto-1", "productpage-nosto-2"])
.load()
.then(res => console.log(res.recommendations))
);
```

Replace `viewProduct` with the page-type method:

| Page | Method |
|---|---|
| Front page | `viewFrontPage()` |
| Category | `viewCategory("/path")` |
| Product | `viewProduct(productId)` |
| Cart | `viewCart()` |
| Search | `viewSearch(query)` |
| 404 / content / account | `viewOther()` |

Full session chain, `setCustomer`, `addOrder`, dynamic filtering, and recommendation callbacks: `references/session-events.md` and `references/recommendations.md`.

### 3. Order tracking — once on the confirmation page

```js
nostojs(api =>
api.defaultSession()
.addOrder({
external_order_ref: "145000006",
info: { order_number: "195", email: "buyer@example.com",
first_name: "X", last_name: "Y", type: "order", newsletter: true },
items: [{ product_id: "406", sku_id: "243", name: "Item",
quantity: 1, unit_price: 49.5, price_currency_code: "USD" }]
})
.setPlacements(["order-related"])
.load()
);
```

Idempotency relies on `external_order_ref`. Full schema in `references/session-events.md`.

### 4. Add-to-cart attribution

```js
nostojs(api => api.recommendedProductAddedToCart(productId, "nosto-categorypage-1"));
```

Records *attribution only*. Cart contents must also be updated (Page Tagging: `api.resendCartTagging()`; Session API: re-call `viewCart()` or include the new cart in the next session).

### 5. React / Next.js

```tsx
import { NostoProvider, NostoSession } from "@nosto/nosto-react";

<NostoProvider account="shopify-12345">
<NostoSession cart={cart} customer={user} />
<Routes />
</NostoProvider>
```

Routing patterns and SPA pitfalls: `references/patterns-gotchas.md`.

## References (load on demand)

- `references/initialization.md` — loaders, `init()` options, consent gating, debug mode
- `references/session-events.md` — full `defaultSession` chain, every `view*`, `addOrder`, `setCustomer`, cart updates
- `references/recommendations.md` — `setPlacements`, `loadRecommendations`, `createRecommendationRequest`, dynamic filtering, prerender/postrender callbacks
- `references/customer-search-experiments.md` — customer identification, GraphQL search, A/B testing surfaces
- `references/patterns-gotchas.md` — SPA integration, multi-store, GDPR, common bugs (double-tracking, missing attribution, PII in `customer_reference`)

## TypeScript Module Map

The `@nosto/nosto-js` typedoc lives at <https://nosto.github.io/nosto-js/modules.html> and exposes two top-level modules:

- `core` — `init`, `nostojs`, `isNostoLoaded`, `getSettings`, `getNostoWindow`, `addSkuToCart`, types `InitProps`, `Settings`, `BackendEnvironment`, `ScriptLoadOptions`
- `client` — the runtime API surface: `API`, `Session`, `Cart`, `Order`, `Customer`, `Product`, `JSONProduct`, `SearchQuery`, `SearchResult`, `ABTest`, `Experiment`, `RenderMode`, `InsertMode`, etc.

When writing TypeScript, import types from `@nosto/nosto-js/client` for runtime shapes and from `@nosto/nosto-js` for `init`/`nostojs`.

## Authoritative Sources

- Tech docs: <https://docs.nosto.com/techdocs>
- JS APIs: <https://docs.nosto.com/techdocs/apis/frontend/js-apis>
- Typedoc: <https://nosto.github.io/nosto-js/modules.html>
- Source: <https://github.com/Nosto/nosto-js>
16 changes: 16 additions & 0 deletions skills/nosto/metadata.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
"version": "1.0.0",
"organization": "Community",
"date": "April 2026",
"abstract": "Reference for Nosto's frontend JavaScript SDK and the @nosto/nosto-js npm package. Covers script loading, the session API (viewProduct, viewCategory, viewCart, viewSearch, addOrder), recommendation placements and attribution, customer identification, GraphQL search, GDPR/consent gating, React/Next.js integration via @nosto/nosto-react, and common gotchas (double-tracking, mixing Page Tagging with Session API, PII in customer_reference). Designed for AI agents implementing or debugging Nosto on storefronts.",
"references": [
"https://docs.nosto.com/techdocs",
"https://docs.nosto.com/techdocs/apis/frontend/js-apis",
"https://docs.nosto.com/techdocs/apis/frontend/implementation-guide-session-api",
"https://nosto.github.io/nosto-js/modules.html",
"https://nosto.github.io/nosto-js/modules/client.html",
"https://nosto.github.io/nosto-js/modules/core.html",
"https://github.com/Nosto/nosto-js",
"https://github.com/Nosto/nosto-react"
]
}
129 changes: 129 additions & 0 deletions skills/nosto/references/customer-search-experiments.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
# Nosto — Customer, Search & Experiments

## Customer identification

Two equivalent forms — pick by integration mode.

**Session API (chainable):**

```js
nostojs(api =>
api.defaultSession()
.setCustomer({
customer_reference: "b369f1235cf4f08153c560.82515936",
email: "buyer@example.com",
first_name: "Nosto",
last_name: "Test",
newsletter: true,
marketing_permission: true
})
.viewCart()
.update()
);
```

**Page Tagging (one-shot):**

```js
nostojs(api =>
api.customer({
email: "jane.doe@example.com",
first_name: "Jane",
last_name: "Doe",
marketing_permission: true,
customer_reference: "5e3d4a9c-cf58-11ea-87d0-0242ac130003"
})
);
```

### Field rules

| Field | Required | Notes |
|---|---|---|
| `customer_reference` | Yes | Stable, pseudonymous ID. UUID or hashed user ID. **Never raw email or PII.** |
| `email` | No | Goes in this field, not in `customer_reference` |
| `marketing_permission` | For triggered email | Explicit GDPR opt-in. Without it, contact is treated as opted-out of triggered messages. |
| `newsletter` | For triggered email | Equivalent on the order-level `info` object |

## Search — Nosto Search GraphQL

Search is exposed via GraphQL at `https://search.nosto.com/v1/graphql`. The client SDK ships TypeScript types but not a built-in fetcher — wire your own client (urql, Apollo, fetch):

```graphql
query Storefront($q: String!) {
search(
accountId: "shopify-12345"
query: $q
products: { size: 24 }
keywords: { size: 5 }
categories: { size: 5 }
popularSearches: { size: 5, emptyQueryMatchesAll: true }
) {
products {
hits { productId name listPrice price imageUrl url }
total
fuzzy
}
keywords {
hits { keyword _redirect _highlight { keyword } }
}
categories {
hits { name url urlPath }
total
}
popularSearches {
hits { query total }
total
}
}
}
```

```ts
const res = await fetch("https://search.nosto.com/v1/graphql", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
query: STOREFRONT_QUERY,
variables: { q: input }
})
}).then(r => r.json());

const fuzzy = res.data.search.products.fuzzy; // boolean — populate analytics autoCorrect
```

### Relevant types from `@nosto/nosto-js/client`

`SearchQuery`, `SearchResult`, `SearchProduct`, `InputSearchFilter`, `InputSearchFacetConfig`, `SearchAutocorrect`, `SearchExplain`.

### Autocomplete

Use Nosto's autocomplete component (configured in admin) rather than calling GraphQL directly for the dropdown — it handles rendering, debouncing, keyboard nav, and analytics. Drop-in via the embed script; no extra code needed beyond placing the `data-nosto-search` attribute on your input.

## Experiments / A-B testing

Most experiments are configured in the Nosto admin and surfaced through standard placement responses — no bespoke client code is needed.

When you DO need to read or override variations from the client, the relevant types in `@nosto/nosto-js/client` are:

- `ABTest` — admin-defined test definition
- `Experiment` — currently-running experiment instance
- `Campaign` — single campaign (placement) inside an experiment
- `ForcedTestDTO` — admin-forced variation for QA
- `TestPreviewsDTO` — preview data when entering preview mode

### Reading the active variation in callbacks

```js
nostojs(api => {
api.listen("prerender", event => {
event.segments.forEach(segmentId => console.log("In segment:", segmentId));
});
});
```

A/B variations show up as segment IDs on `prerender`. Conditional UI based on segment membership is the standard pattern.

### Forcing a variation (QA / preview)

Append `?nostodebug=true` to the URL and use the debug toolbar's "Test previews" panel to force a variation. There is no production-safe way to force variations from JS — that's by design (forcing would skew test results).
Loading