Layout — @sonordev/site-kit/layout
RSC-compatible master layout that auto-composes all site-kit features.
Usage
// app/layout.tsx
import { SiteKitLayout } from '@sonordev/site-kit/layout'
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html lang="en">
<body>
<SiteKitLayout>{children}</SiteKitLayout>
</body>
</html>
)
}Zero-config: reads SONOR_API_KEY from env, injects it into client modules automatically.
Props
interface SiteKitLayoutProps {
children: React.ReactNode
apiKey?: string // Defaults to SONOR_API_KEY env var
apiUrl?: string // Defaults to SONOR_API_URL, then https://api.sonor.io
projectId?: string // For chat routing (auto-resolved if omitted)
analytics?: boolean | AnalyticsConfig // Default: true
chat?: boolean | ChatLayoutConfig // Default: true (website chat; config = launcher placement)
popups?: boolean // Default: true (Website → Popups & Banners)
engage?: boolean | EngageConfig // Deprecated: false turns chat and popups off; an object configures the launcher
signal?: boolean | SignalConfig // Default: false
sitemapSync?: boolean // Default: false (build-time + server reconciler own this)
fleet?: boolean // Default: true (once-per-session kit version heartbeat)
defer?: boolean // Default: true (client modules wait for load + idle)
favicon?: boolean // Default: true
managedScripts?: boolean // Default: true
debug?: boolean // Default: false
showLlmsTxtFooterLink?: boolean // Default: false (prefer middleware discovery headers)
speculation?: boolean | { mode?: 'prerender' | 'prefetch'; exclude?: string[] } // Default: false
}Module options live with each module: Analytics (trackPageViews, excludePaths, site, allowInFrame, allowLocalhost) Website chat (position, offsetBottom, zIndex, allowInFrame) and Popups and banners.
What It Composes
Server-side (RSC):
ManagedFavicon— Sonor logo as<link>tagsManagedScripts— tracking pixels/analytics tags in<head>and body-end positions- API preconnect/dns-prefetch hints
Client-side (lazy-loaded island):
AnalyticsProvider— page views, scroll depth, heatmap clicks, Web VitalsSitePopups— popups, banners and toastsSiteChat— website chat (Echo)SignalBridge— A/B experiments, behavior tracking (opt-in)SitemapSync— parses/sitemap.xmland syncs to Sonor (opt-in)FleetHeartbeat— reports the kit version and enabled modules once per session
Since 4.0.0 none of these wrap your page. {children} renders first and every module mounts after it as a childless sibling, so SiteKitLayout never pushes a route to client rendering. Analytics, chat, popups, SitemapSync and the heartbeat also wait for window load + idle (or the first interaction) unless you pass defer={false}. SignalBridge isn't deferred, so experiment variants apply early. Visitor and session IDs come from a shared storage singleton rather than a provider.
Note
SiteKitProvider was removed in 4.0.0. It wrapped the whole tree client-side, which broke RSC. Migrate an older layout with npx sonor-setup codemod --only provider-to-layout --write.