Cosmic Insights for Next.js

Cosmic Insights ships as a single <script> tag that works in every framework. No npm package, no framework-specific build step. The tracker hooks history.pushState / popstate natively, so SPA navigations are captured in both the App Router and the Pages Router with zero extra wiring.

If your app is built with Cosmic AI Build, the tag is injected for you. The instructions below cover manual installs.

Project id

You need your Cosmic project id. Find it in the dashboard under Project, Settings, Basic settings: the PROJECT ID field has a copy button. It is not a secret (it's the Mongo _id of your project and appears in dashboard URLs).

Store it as an env var so you can swap projects per environment:

# .env.local
NEXT_PUBLIC_COSMIC_INSIGHTS_PROJECT=your_project_id

App Router install

Add the script to app/layout.tsx using next/script:

// app/layout.tsx
import Script from 'next/script';

export default function RootLayout({ children }: { children: React.ReactNode }) {
  return (
    <html lang="en">
      <body>
        {children}
        <Script
          defer
          src="https://insights.cosmicinsights.dev/script.js"
          data-project={process.env.NEXT_PUBLIC_COSMIC_INSIGHTS_PROJECT}
          strategy="afterInteractive"
        />
      </body>
    </html>
  );
}

Pages Router install

Add the script to pages/_document.tsx:

// pages/_document.tsx
import Document, { Html, Head, Main, NextScript } from 'next/document';

export default class MyDocument extends Document {
  render() {
    return (
      <Html lang="en">
        <Head>
          <script
            defer
            src="https://insights.cosmicinsights.dev/script.js"
            data-project={process.env.NEXT_PUBLIC_COSMIC_INSIGHTS_PROJECT}
          />
        </Head>
        <body>
          <Main />
          <NextScript />
        </body>
      </Html>
    );
  }
}

Object attribution

Render a single <meta name="cosmic-context"> tag whose JSON content identifies the Cosmic object on the page. The tracker re-reads it on every SPA route change.

App Router

// app/blog/[slug]/page.tsx
import type { Metadata } from 'next';

export async function generateMetadata({ params }): Promise<Metadata> {
  const post = await getPost(params.slug);
  return {
    other: {
      'cosmic-context': JSON.stringify({
        object_id: post.id,
        object_type: post.type_slug,
      }),
    },
  };
}

Pages Router

// pages/blog/[slug].tsx
import Head from 'next/head';

export default function PostPage({ post }) {
  return (
    <>
      <Head>
        <meta
          name="cosmic-context"
          content={JSON.stringify({
            object_id: post.id,
            object_type: post.type_slug,
          })}
        />
      </Head>
      <article>{/* ... */}</article>
    </>
  );
}

The dashboard's per-object Performance card lights up automatically once events with object_id start landing.

Custom events

window.cosmicInsights('signup', { plan: 'team' });
window.cosmicInsights('order_paid', { revenue_cents: 4900, currency: 'USD' });

Recognized top-level fields like revenue_cents and object_id are promoted onto the event row; everything else is stored in event_props.

See Attribution for the full precedence model.