@netiva-ai/site-kit
v0.0.6
Published
Netiva AI sandbox runtime — providers, hooks, design tokens, commerce, leads, plans, and analytics for AI-generated Next.js sites.
Maintainers
Readme
@netiva-ai/site-kit
In-sandbox React runtime for Netiva AI editor projects. Providers, hooks, fetch clients, and Stripe wiring an AI-generated Next.js site needs to actually transact — sell products, capture leads, track analytics, and resell SaaS subscriptions — without re-implementing the data layer or hand-rolling Stripe Elements.
Three core subpaths (/core, /commerce, /leads) plus integrated SaaS billing
(provided automatically by <NetivaShell> via @netiva-ai/billing), mounted once
in app/layout.tsx.
Install
pnpm add @netiva-ai/site-kitPeer dependencies
| Package | Required for | Optional? |
| --- | --- | --- |
| react, react-dom (>=18) | All subpaths | No |
| @stripe/react-stripe-js@^3, @stripe/stripe-js@^5 | commerce + billing features inside <NetivaShell> | Optional — only if you use Stripe-powered features |
A leads-only site doesn't pay the Stripe bundle cost.
Subpath exports
| Subpath | Purpose | Stripe peer deps |
| --- | --- | --- |
| @netiva-ai/site-kit/core | Session/UTM capture, <NetivaShell>, shared utilities (re-exports from @netiva-ai/billing/core) | Transitively via shell |
| @netiva-ai/site-kit/commerce | Products, cart, Stripe Elements checkout, orders | Required |
| @netiva-ai/site-kit/leads | Lead capture, event tracking | No |
SaaS billing / subscriptions live in the separate package @netiva-ai/billing.
When you render <NetivaShell>, CustomerAuthProvider and PlansProvider (from @netiva-ai/billing/plans)
are mounted automatically for the entire tree. If you need the hooks or client directly
(e.g. for a custom pricing UI outside of the shell), import them from @netiva-ai/billing/plans
or @netiva-ai/billing/core.
Runtime config
<NetivaShell config={NETIVA_CONFIG}> receives the workspace's runtime
config as a prop and exposes it via React context to every provider
underneath. The platform-managed app/netiva-config.ts file is the
canonical source — Editor V2 stamps it on every cold-create, resume, and
file sync. You don't write to it manually.
| Field | Used by | Required? |
| --- | --- | --- |
| apiUrl | All clients (/products, /v2/leads, /members/.../plans) | Required |
| workspaceId | Storefront URL paths, cart localStorage key, lead/event payloads | Required for any backend call |
The workspace's Stripe publishable key is not part of this config —
<StripeProvider> fetches it on demand from /members/:workspaceId/config.
Mounting <NetivaShell>
The shell is a Client Component (it carries 'use client') so it can be
mounted from a Server Component or another Client Component. The Next.js
App Router root layout is the canonical mount point.
// app/layout.tsx — Server Component (keeps `export const metadata`)
import type { Metadata } from 'next'
import { NetivaShell } from '@netiva-ai/site-kit/core'
import { NETIVA_CONFIG } from '@/netiva-config'
import './globals.css'
export const metadata: Metadata = { title: 'Your site' }
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html lang="en">
<body>
<NetivaShell config={NETIVA_CONFIG} clientToken={/* optional short-lived token */}>
{children}
</NetivaShell>
</body>
</html>
)
}Props:
config(required): The workspace runtime config fromapp/netiva-config.ts.clientToken(optional): A short-lived, customer-scoped JWT minted by your server using the SDK (netiva.billing.generateSessionToken). When provided it authenticates the billing flows inside the shell without requiring a magic-link login. This is the recommended path for embedded checkouts.
Rules:
- Mount exactly once. App Router unmounts per-page providers on
navigation — a second
<NetivaShell>deeper in the tree silently wipes the cart, customer auth, and analytics session. - Pass
configfrom the platform-managedapp/netiva-config.tsfile. Server components that need the same values can importNETIVA_CONFIGdirectly; client components should calluseSiteConfig()from@netiva-ai/site-kit/core.
Setup requirements per feature
The site-kit's clients route to the Netiva backend's customer-facing routes. Different features require different workspace-level setup.
Commerce (products, cart, checkout)
| Requirement | Where | Field |
| --- | --- | --- |
| Workspace's own Stripe API keys | Dashboard → Workspace Settings → Integrations → Stripe | apiIntegrations.stripe.{server_token, client_token, endpoint_secret} |
| At least one Product | Workspace admin via POST /workspaces/:id/products | n/a |
| Stripe webhook endpoint | Stripe dashboard → Webhooks | URL: ${API_URL}/workspaces/:workspaceId/stripe-webhook |
Without Stripe configured, useStoreClient().placeOrder(...) fails at
checkout time. useProducts() still works (the product list endpoint is
public; payment is what's blocked).
Leads (forms, events)
Public — no workspace setup beyond having a workspace.
SaaS Subscriptions & Billing (magic-link auth, pricing tables, checkout)
| Requirement | Where | Field |
| --- | --- | --- |
| Workspace's own Stripe API keys | Same as commerce | Same as commerce |
| At least one Plan (Stripe Product) | Workspace admin via POST /workspaces/:id/plans | Metadata: netiva_workspace_id = <ws>, netiva_visible = 'true' |
| Email provider (for magic link delivery) | Dashboard → Workspace Settings → Integrations → Email Provider | workspace.mailProvider, mailProviderSettings |
| Stripe webhook endpoint | Same as commerce | Same as commerce |
Without Stripe configured, GET /members/:workspaceId/plans returns
503 { code: 'stripe_not_configured' }. usePlans() swallows the error
and returns [] — render a clear "this site isn't ready to sell yet"
empty state.
Without an email provider, useMagicLinkRequest() succeeds in the UI
but the visitor never receives the email. Configure SES / Resend / SMTP
/ Mailgun / SparkPost in the dashboard.
Critical safety rule
The workspace's server_token must be its own Stripe secret key —
NOT a copy of the platform's STRIPE_PRIVATE_KEY. The backend blocks
this on save with 422 { code: 'platform_key_blocked' }.
Per-subpath reference
/core
| Export | Purpose |
| --- | --- |
| NetivaShell | Single provider that composes /core, /commerce, /leads, plus the billing providers (CustomerAuthProvider + PlansProvider) from @netiva-ai/billing. Required config: SiteConfig prop. |
| SessionProvider / useSession() / SessionState / UTMParams | Anonymous session id (localStorage), first landing page (sessionStorage), UTM params (sessionStorage). |
| useSiteConfig() / SiteConfig | Client-side hook returning the workspace config passed into <NetivaShell>. |
| cn(...classes) | clsx + tailwind-merge wrapper. |
| formatPrice(value, currency?) | Intl.NumberFormat wrapper with $X.XX fallback. |
| formatStock(stock) | Returns { level: 'in' \| 'low' \| 'out', label: string } for inventory display. |
/commerce
Talks to /workspaces/:id/products and /workspaces/:id/orders.
| Export | Purpose |
| --- | --- |
| NetivaStoreProvider / useStoreClient() | Wraps NetivaStoreClient. |
| CartProvider / useCart() | localStorage-persisted cart, per-workspace key. Stock-aware quantity clamping. |
| StripeProvider | <Elements> from @stripe/react-stripe-js, lazy-loads Stripe.js with the workspace's publishable key. |
| useProducts() / useProduct(id) | Async hooks returning { data, loading, error, refetch }. |
| NetivaStoreClient | REST client — listProducts, getProduct, placeOrder. |
| Types | Product, Variant, CartItem, Address, Order, OrderDTO, MediaInfo, ProductProperty, PropertyValue |
| Re-exports | cn, formatPrice, formatStock |
/leads
Talks to /v2/leads and /v2/events.
| Export | Purpose |
| --- | --- |
| LeadsProvider / useLeadsClient() | Wraps NetivaLeadsClient. |
| AnalyticsProvider / useAnalytics() / useTrackEvent() | Auto-fires page_view on every navigation. |
| useFormSubmit() | Submit a lead, auto-attaches sessionId, firstLandingPage, conversionPage, UTM source. Fires linked form_submit event on success. |
| NetivaLeadsClient | REST client — trackEvent (fire-and-forget with keepalive), submitLead. |
| Types | EventType, LeadStatus, EventDTO, LeadDTO, LeadRecord, FormSubmissionResult, TrackEventOptions, SubmitLeadInput, UseFormSubmitState |
Billing & Subscriptions
SaaS subscription features (previously exposed via @netiva-ai/site-kit/plans) have been
moved to the dedicated, framework-agnostic package @netiva-ai/billing.
When you use <NetivaShell> the required providers are mounted for you automatically:
CustomerAuthProvider(from@netiva-ai/billing/plans)PlansProvider(from@netiva-ai/billing/plans)
This means hooks such as usePlans(), useCurrentSubscription(), useSubscribe(),
useMagicLinkRequest(), and useCustomerAuth() will work anywhere inside the shell
without any extra setup.
If you need direct access to the billing primitives (for a custom pricing page, embedded checkout, or non-React code), import from the billing package:
import {
usePlans,
useCurrentSubscription,
useSubscribe,
useCustomerAuth,
NetivaPlansClient,
} from '@netiva-ai/billing/plans';
import type { Plan, ActiveSubscription } from '@netiva-ai/billing/plans';See the @netiva-ai/billing README for full documentation of the client, hooks,
clientToken authentication model, and provider composition.
Troubleshooting
| Symptom | Likely cause | Fix |
| --- | --- | --- |
| usePlans() returns [] even though plans exist in Stripe | Workspace's apiIntegrations.stripe not configured, OR plans missing netiva_workspace_id metadata | Dashboard → Workspace Settings → Integrations → Stripe; ensure plans are tagged with the workspace id |
| Subscribe button does nothing after click | Customer JWT missing — visitor hasn't completed magic-link auth | The pricing-table recipe should redirect unauthed visitors to /auth/login?next=/checkout?price=... |
| Charges land on the wrong Stripe account | Workspace's apiIntegrations.stripe.server_token equals the platform's secret key | Reconnect Stripe with the workspace's own keys (the backend blocks platform keys with 422 platform_key_blocked) |
