docs
    site-kit: Migrating to 7
    v7.2.0.md

    Moving a site to site-kit 7

    site-kit 7 is organised the way Sonor's dashboard is: one entry per module. Most sites need no changes at all. Move a site when you next touch it; nothing forces it before then, because a site's ^6 range never picks up 7.

    The short version

    pnpm add @sonordev/site-kit@^7
    npx sonor-setup codemod --check      # lists what needs moving (writes nothing)
    npx sonor-setup codemod --write      # moves it
    pnpm build                           # a bump you didn't build is a guess

    Run the codemod in each app or workspace package that imports site-kit (in a monorepo, inside each workspace package too). It's idempotent: running it twice changes nothing the second time. It works from any 2.x-6.x site: copies of sites on 4.2, 5.8 and 6.5 were migrated with it and built.

    It leaves nothing in the project but the change. Since sonor-setup 7.1.2 the originals of the files it writes go to a folder in your OS temp directory (it prints where, and git has them too), not .bak files beside them, and it never creates or edits .env.example: .env.local is the one env file. An old fallback such as process.env.SONOR_API_KEY || process.env.UPTRADE_API_KEY becomes process.env.SONOR_API_KEY.

    Requirements: Next 16 and Node 20.19+ or 22.12+ (Netlify's default is 22).

    What changed, and what the codemod does about it

    1. The root entry is types-only

    @sonordev/site-kit exports types and SITE_KIT_VERSION, nothing that runs. A stray root import used to be able to pull commerce, signal or redirect code into a page's bundle.

    WasNow
    import { BookingWidget } from '@sonordev/site-kit'@sonordev/site-kit/sync
    import { AffiliatesWidget, useAffiliates } from '@sonordev/site-kit'@sonordev/site-kit/affiliates (new entry)
    commerce components and fetchers@sonordev/site-kit/commerce
    ManagedImage and the image helpers@sonordev/site-kit/website/images
    SignalBridge, experiments, useSignal*@sonordev/site-kit/signal
    TestimonialSection, fetchReviews@sonordev/site-kit/reputation
    handleManagedRedirects and friends@sonordev/site-kit/seo/redirects
    LandingPage, landingPageMetadata@sonordev/site-kit/website/landing
    formatBookingTime, formatBookingDateformatTime, formatDate from @sonordev/site-kit/sync

    The codemod rewrites these imports, including the two renamed ones, and keeps type imports on the root. It flags, rather than rewrites, a namespace import (import * as SK), a require, or a re-export from the root. The full list is src/shared/module-map.json (rootRuntime).

    2. The setup CLI is its own package: sonor-setup

    The CLI (init, scaffold, codemods, OG cards, GEO wiring, verify) was 3 MB of every site's install and forced a site-kit release for every setup-only fix. It's now sonor-setup.

    • npx sonor-setup … keeps working unchanged: npx fetches the package.
    • A site whose package.json scripts run sonor-setup (an OG step that runs sonor-setup og, say) needs it as a dev dependency. The codemod adds "sonor-setup": "^7.0.0" for you.
    • sonor-register-sitemap stays in site-kit, since sites run it from their postbuild. Nothing to change.
    • The site-kit bin alias for the CLI is gone (no site used it).

    3. One entry per Sonor module (old paths still work)

    New homes, each the same module as before:

    • Website: ./website/{popups,images,slots,cms,landing,cta-bar}
    • SEO: ./seo/{sitemap,robots,indexnow,redirects,og,og/route,llms,llms/client,llms/contract,meta/contract,pages/contract}
    • Website chat: ./chat, and popups: ./website/popups (both out of ./engage, which Sonor retired with the Engage module)

    The old paths keep working through 7.x and are removed in 8.0. The codemod moves them anyway, so a touched site is done in one pass: chat imports go to ./chat, popup types to ./website/popups under their new names (EngageElement is SitePopup). It flags EngageWidget, which draws both and which SiteKitLayout already mounts. SiteKitLayout gains chat and popups props; engage is an alias (engage={false} still turns both off). Since 7.2.0, popups render only from blocks: one made in Engage Studio isn't drawn, so make it again in Sonor (Website → Popups & Banners).

    4. ESM only, Next 16 only

    site-kit ships ES modules alone ("type": "module"). Next, Turbopack and the kits don't notice. Node 20.19+/22.12+ can require() it too, so a next.config.ts that imports @sonordev/site-kit/config keeps working. A site-side script that require()s site-kit on an older Node would break.

    The next peer is ^16. Next 16 renamed middleware.ts to proxy.ts, and the codemod moves it: the file, a named middleware export (to proxy), and createMiddleware from @sonordev/site-kit/middleware (to createProxy from /proxy). The old names work through 7.x.

    The proxy goes beside the app directory. Next reads it from the project root, or from src/ when the app is src/app, and silently ignores one anywhere else: the build passes, but the site sends no security headers and runs no managed redirects. So on a src/app site the codemod writes src/proxy.ts, with the file's relative imports re-pointed. Before sonor-setup 7.1.2 it renamed the file in place, which left a root proxy.ts on those sites; rerunning the codemod moves it, and npx sonor-setup doctor warns about any proxy Next doesn't read.

    It won't move a file that sets runtime (a proxy file can't; the build throws), and says so: remove the export, check the logic still runs on your host, rerun. It never moves onto a proxy Next already runs: with one in each place, it reports both and changes neither. npx sonor-setup next16 runs just this part.

    5. Removed (nothing used them)

    • @sonordev/site-kit/search and /search/contract
    • @sonordev/site-kit/redirects/not-found (resolveManagedRedirect), and with it the x-sk-path request header the proxy stamped for it and the SK_PATH_HEADER export. Managed redirects run in the proxy.
    • The postinstall GEO bootstrap (SITE_KIT_AUTO_GEO=1). Run npx sonor-setup geo instead. site-kit no longer runs anything at install.
    • @sonordev/site-kit/setup (an empty stub since 2.0), LocationPageContent and getLocationSection (they sent a project_id), identifyTopicClusters (use getTopicCluster), formatContentSignals, and the SiteKitConfig type (not withSiteKitConfig, which stays).
    • Source maps. The package no longer ships .map files (7.3 of 6.x's 11 MB).

    Still accepted and ignored until 8.0, because sites pass them: contentSignals on createRobotsTxtHandler, nativeReturnTo on forms, and <ManagedScripts />.

    6. Behavior worth knowing

    • react-markdown is no longer installed with site-kit. The chat widget renders markdown with marked, already a dependency. A site that imports react-markdown itself without listing it (it only worked because npm hoisted site-kit's copy) must add it.
    • sonor-register-sitemap reads .env files the way Next does: a variable the host set wins over every file, then .env.production.local, .env.local, .env.production, .env. It used to let .env.local beat the host.

    Optional, and new in 7.x

    • Cache Components. cacheComponents: true in next.config works with site-kit now (it failed every page through 6.x). Remove route segment configs (dynamic, revalidate) when you turn it on; Next rejects them.
    • Agent tools. npx sonor-setup mcp gives the site an MCP endpoint with the built-in Sonor tools (business profile, services, FAQ, pages, articles, reviews; inquiries, availability and offerings when you ask), the server card, and on Netlify the rate-limited relay. Sonor's AI Visibility tab then shows which agents used them. A site that already runs its own MCP server (a hand-written one) is left exactly as it is: nothing in 7.0 turns anything on for it. To have Sonor see its calls, add onToolCall: reportToolCallsToSonor() to its createMcpHandler.
    • @sonordev/contracts is for the APIs and the dashboard; sites keep importing @sonordev/site-kit/*/contract, which is the same code.
    • Live updates (7.1). npx sonor-setup scaffold writes the route Sonor calls when content changes, app/api/seo-revalidate/route.ts, and npx sonor-setup doctor checks for it. A site with articles gets createRevalidateRoute({ publicationBasePath: '/insights' }) with its own path, read from the app directory (a dynamic page that renders site-kit articles, or a [slug] route under a folder like /blog or /news). When more than one folder looks like the publication, it writes the one-line route and lists them.

    Kits built on site-kit

    agency-site-kit and re-site-kit import only entries 7 keeps unchanged (/client, /server, /llms, /forms, /mcp, /portfolio/contract), so their >= peer ranges already accept 7.