# Moving a site to site-kit 8

site-kit 8 moves commerce into its own package, [`@sonordev/commerce-kit`](https://sonor.dev/commerce-kit), which also carries the new memberships module, and removes the entries for two modules Sonor retired, Engage and the Sanity CMS. Most sites need no changes at all: if yours uses none of these, update site-kit and rebuild. A site's `^7` range never picks up 8, so nothing forces the move before you next touch the site.

## Who's affected

Only sites that do one of these:

- Import from `@sonordev/site-kit/commerce` or `@sonordev/site-kit/commerce/server` (products, services, events, checkout, calendars).
- Import a commerce type from the root, such as `import type { CommerceOffering } from '@sonordev/site-kit'`.
- Turn on the opt-in `list_offerings` agent tool by passing `offerings` to `sonorMcpServer`. See [The MCP change](#the-mcp-change).
- Import one of the entries that were [removed](#what-was-removed): `@sonordev/site-kit/engage`, `/cms`, `/website/cms` (or their `/server` entries), or `ManagedContent`, `getManagedContentData` or `getContentBlock` from `/seo`.

Not sure which you are? Search the site for the old paths, then for root imports:

```bash
grep -rnE "@sonordev/site-kit/(commerce|memberships|engage|cms|website/cms)" . \
  --include='*.ts' --include='*.tsx' --include='*.js' --include='*.jsx' \
  --include='*.mjs' --include='*.cjs' --include='*.mts' --include='*.cts' --include='*.mdx' \
  --exclude-dir=node_modules --exclude-dir=.next

grep -rnE "ManagedContent|getManagedContentData|getContentBlock" . \
  --include='*.ts' --include='*.tsx' --include='*.js' --include='*.jsx' --exclude-dir=node_modules --exclude-dir=.next

grep -rnE "from ['\"]@sonordev/site-kit['\"]" . \
  --include='*.ts' --include='*.tsx' --exclude-dir=node_modules --exclude-dir=.next
```

The root entry is types-only (plus `SITE_KIT_VERSION`), so the last search turns up type imports. Look for the commerce types listed under [What moved](#what-moved) and the Engage type names under [What was removed](#what-was-removed). If the first two searches find nothing, none of the root imports are those types, and you don't pass `offerings` to `sonorMcpServer`, you're done: raise site-kit to `^8` whenever you like.

## Steps

### 1. Install both packages

```bash
pnpm add @sonordev/site-kit@^8 @sonordev/commerce-kit
```

`npm install @sonordev/site-kit@^8 @sonordev/commerce-kit` does the same. commerce-kit lists site-kit 8 or newer as a peer dependency, so it uses your site's one copy of site-kit. If your package manager reports that commerce-kit needs `@sonordev/site-kit >=8.0.0` (npm stops with an `ERESOLVE` error, pnpm and Yarn warn), site-kit is still on 7. Like site-kit, commerce-kit needs React 19. Next 16 is an optional peer: only the memberships server helpers use it.

### 2. Run the codemod

Dry run first:

```bash
npx sonor-setup codemod --only site-kit-8
```

It prints what it would change and writes nothing. When the diff looks right, apply it:

```bash
npx sonor-setup codemod --only site-kit-8 --write
```

The codemod needs sonor-setup 7.3.1 or later, and `npx` fetches the latest. It reads source files anywhere in the project (`pages/`, `hooks/` and `scripts/` count, not just `app/` and `src/`), but not `node_modules`, build output or `public/`. It rewrites your imports (the table below), and goes by imports only: a path that appears in a comment or inside a string, such as a code sample or a redirect table, is left as it is. When your code imports commerce, it also adds `@sonordev/commerce-kit` to package.json and raises site-kit's range to `^8.0.0`, and if `pnpm-workspace.yaml` lists site-kit under `minimumReleaseAgeExclude` it adds commerce-kit there too. Anything it can't rewrite safely it flags, with the fix, so you can finish those by hand: a `require()` of an old path (commerce-kit is ESM only, so that call needs an `import`), an import of a [removed](#what-was-removed) entry, or a `sonorMcpServer` whose `offerings` has no reader (see [The MCP change](#the-mcp-change)). Run it in the app's folder: in a monorepo, once per app that imports commerce, because it stops at a workspace root.

`--check` is the gate: it exits 1 while a file needs rewriting and while a manual follow-up remains, so exit 0 means there's nothing left for you to do.

If it changed package.json, install before you build:

```bash
pnpm install
```

No codemod handy? The swap is mechanical: replace the package name in each import and add commerce-kit to package.json. The subpath stays the same.

### 3. Build

```bash
pnpm build
```

A build is the real test. Nothing is left behind at the old paths, so an import you missed fails with "Module not found" instead of quietly running something stale. A bump you didn't build is a guess.

## What moved

The subpath stays the same; only the package name changes.

| Was                                                | Now                                         |
| -------------------------------------------------- | ------------------------------------------- |
| `@sonordev/site-kit/commerce`                      | `@sonordev/commerce-kit/commerce`           |
| `@sonordev/site-kit/commerce/server`               | `@sonordev/commerce-kit/commerce/server`    |
| `@sonordev/site-kit/memberships`                   | `@sonordev/commerce-kit/memberships`        |
| `@sonordev/site-kit/memberships/server`            | `@sonordev/commerce-kit/memberships/server` |
| Commerce types from the root, `@sonordev/site-kit` | `@sonordev/commerce-kit`                    |

Memberships never shipped in a site-kit release, so those two rows only matter if you tried an unreleased build. The root types are `CommerceOffering`, `CommerceCategory`, `CommerceVariant`, `CommerceSchedule`, `OfferingType`, `Cart`, `CartItem`, `EventCheckoutProps` and `CheckoutLineItem`.

```tsx
// before
import { OfferingCard, ProductPage } from '@sonordev/site-kit/commerce'
import { getOfferingBySlug } from '@sonordev/site-kit/commerce/server'
import type { CommerceOffering } from '@sonordev/site-kit'

// after
import { OfferingCard, ProductPage } from '@sonordev/commerce-kit/commerce'
import { getOfferingBySlug } from '@sonordev/commerce-kit/commerce/server'
import type { CommerceOffering } from '@sonordev/commerce-kit'
```

The new entries export what the old ones did, under the same names. `/commerce` is a client entry and `/commerce/server` is server code, the same split as before. commerce-kit's root is types-only, so import anything that runs from one of its module entries.

What stays where it was:

- **Your CSS.** The components keep their `site-kit-` class names (`site-kit-product-grid`, `site-kit-calendar-event` and the rest), so your styles still apply. Brand tokens still come from site-kit's `brand.css`, which your site imports as before.
- **Your credential.** commerce-kit uses the credential `SiteKitLayout` already publishes, so there's no new environment variable.

What's different besides the import path, all in [commerce-kit's changelog](https://sonor.dev/commerce-kit/changelog):

- The shopping events (`product_view`, `add_to_cart`, `begin_checkout`, `checkout_start` and `purchase`) reach Sonor. site-kit 7 reported them through a hook that returns nothing under `SiteKitLayout`, so they were never sent.
- The server helpers return whole offerings (gallery, size chart and options included), and they're server-only now. Each response is cached for 60 seconds under the `commerce` tag, which Sonor expires when you edit an offering, and a request gives up after 30 seconds. Pass `{ revalidate: 0 }` in the config if a page needs every request fresh.
- A product marked sold out in Sonor keeps its page, shown as unavailable, instead of answering "no such product".
- Checkout works in a browser set to block site data.
- When a server fetch fails, the log line starts with `[commerce-kit]` instead of `[site-kit/commerce]`. If you filter or alert on that prefix, update it.

## What was removed

These modules were retired in Sonor, and their entries were kept through 7.x so old imports kept building. They're gone in 8, and nothing replaces them:

| Removed                                                                                                     | Use instead                                                                                                                                                                                                                |
| ----------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `@sonordev/site-kit/engage`                                                                                 | `@sonordev/site-kit/chat` and `@sonordev/site-kit/website/popups`. The 7 codemod (`npx sonor-setup codemod --write`) moves the names that have a home there and flags `EngageWidget`, which `SiteKitLayout` already mounts |
| `@sonordev/site-kit/cms`, `/cms/server`, `/website/cms`, `/website/cms/server`                              | Managed copy: `ManagedSlot`, `ManagedRichText`, `ManagedLink` and `ManagedList` from `@sonordev/site-kit/website/slots`. You can uninstall `@portabletext/react` and `@sanity/image-url`                                   |
| `ManagedContent`, `getManagedContentData`, `getContentBlock` and their types, from `@sonordev/site-kit/seo` | `ManagedRichText` from `@sonordev/site-kit/website/slots`                                                                                                                                                                  |
| `EngageElement`, `WidgetConfig`, `EngageConfig` and the other Engage type names, from the root              | `SitePopup`, `SitePopupConfig` and the rest of `@sonordev/site-kit/website/popups`. (The `engage` prop on `SiteKitLayout` and its own `EngageConfig` type stay.)                                                           |

They rendered nothing in 7.x, so most sites never imported them and have nothing to do. `npx sonor-setup manifest --json` lists them under `removed`.

## The MCP change

`list_offerings` is the opt-in agent tool that lists priced products, services and events. It used to read the commerce fetchers directly. It takes its reader as an option now, so site-kit doesn't depend on commerce-kit:

```ts
// before (site-kit 7)
sonorMcpServer({ businessName: 'Example Studio', offerings: { path: '/shop' } })

// after (site-kit 8)
import { getOfferingsResult } from '@sonordev/commerce-kit/commerce/server'

sonorMcpServer({ businessName: 'Example Studio', offerings: { path: '/shop', read: getOfferingsResult } })
```

`read` is `getOfferingsResult` from `@sonordev/commerce-kit/commerce/server`. It reports a failed request as "the catalog is unavailable" instead of an empty catalog; the plain `getOfferings` from the same entry also works, but it reads an outage as empty. `path` is still optional and still defaults to `/shop`. The tool, how it ranks a query, and its rule that a price the business keeps private is never shown all stay in site-kit.

If `read` is missing, `sonorMcpServer` throws when the server is built, with a message that says what to pass. A TypeScript site sees the same reminder as a type error. A site that doesn't pass `offerings` changes nothing, and neither does `list_services`: it reads the site's core data, not commerce. See [MCP and agent tools](https://sonor.dev/site-kit/mcp).

## Check your work

1. The first two searches from [Who's affected](#whos-affected) come back empty, and none of the root imports the last one finds are commerce or Engage types.
2. `pnpm build` passes.
3. `pnpm ls @sonordev/site-kit @sonordev/commerce-kit` (or `npm ls`) shows site-kit 8.x and commerce-kit 1.x.
4. On a preview deploy or in dev, load a product or events page and start a checkout.
5. `npx sonor-setup verify` still exits 0.

## Staying on 7.x

Staying on 7 is fine while you plan the move. A `^7` range never picks up 8, and everything in 7.4 keeps working, commerce included. Two things to know: commerce-kit needs site-kit 8, so a site on 7 can't install it, and the memberships module ships only in commerce-kit. If a dependency update raised your range to 8 before you were ready, pin it back with `pnpm add @sonordev/site-kit@^7.4.0`.

## Questions

### Why is commerce a separate package?

It versions and releases on its own. Checkout is the busiest money path on a site, and most sites don't sell anything, so a fix there shouldn't wait for a site-kit release (or the other way round). commerce-kit builds on site-kit and takes its transport, credential, analytics and server data from it instead of carrying copies. The one entry site-kit added for it, [`@sonordev/site-kit/runtime`](https://sonor.dev/site-kit/runtime), is public API, and a site doesn't need to import it.

### Does this make my site's JavaScript smaller?

No, and it was never meant to. site-kit's root entry was already types-only and no core entry referenced commerce, so visitors to a site without commerce never downloaded any of it. For a site that does sell, the components are the same code, now built and shipped from commerce-kit. What shrinks is site-kit's package: about 8% fewer bytes to download and about 13% fewer on disk than 7.4.0, which is install size, not page weight.

### Why 8.0, and not a 7.x release with the old paths still working?

Removing a published entry is a breaking change, and a `^7` range would have picked it up on the next install. Keeping the old paths alive would have meant a second copy of the code, or site-kit depending on a package that depends back on it. It wouldn't have saved an edit either, since commerce-kit has to be added to package.json whichever way. (The retired Engage and CMS entries were promised for 8.0 in the 7.0, 7.1 and 7.2 notes, so they go with it.)

### Do my styles, environment variables or API key change?

No. The class names, the brand tokens and the credential are all as they were. See [What moved](#what-moved).

### Did anything else leave site-kit in 8.0?

Only [what was removed](#what-was-removed). Every other entry works as it did in 7.4, including the deprecated aliases for renamed modules (`./middleware`, `./sitemap`, `./robots`, `./redirects`, `./og`, `./llms`, `./images`, `./slots`, `./landing` and `./cta-bar`), because sites still import them. Earlier notes said those would go in 8.0; they stay, and will go in a later major once sites have moved off them. [Migrating to 7](https://sonor.dev/site-kit/migrating-to-7) lists them, and the codemod (`npx sonor-setup codemod --write`) that moves a site onto their new homes.

### Where are the commerce docs now?

At [sonor.dev/commerce-kit](https://sonor.dev/commerce-kit): [Commerce](https://sonor.dev/commerce-kit/commerce), [Memberships](https://sonor.dev/commerce-kit/memberships) and the [changelog](https://sonor.dev/commerce-kit/changelog).
