> ## Documentation Index
> Fetch the complete documentation index at: https://docs.grainql.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Next.js Quick Start

> Integrate Grain Analytics with Next.js App Router or Pages Router

This guide covers both the App Router (Next.js 13+) and Pages Router (Next.js 12 and earlier).

## Install the Package

```bash npm theme={null}
npm install @grainql/tag
```

```bash yarn theme={null}
yarn add @grainql/tag
```

```bash pnpm theme={null}
pnpm add @grainql/tag
```

## App Router (Next.js 13+)

### Create an Analytics Component

Grain Tag must be initialized on the client. Create a client component that runs once:

```tsx theme={null}
// app/grain.tsx
'use client';

import { useEffect } from 'react';
import { init, isInitialized } from '@grainql/tag';

export function GrainAnalytics() {
  useEffect(() => {
    if (!isInitialized()) {
      init({ tenantId: process.env.NEXT_PUBLIC_GRAIN_TENANT_ID! });
    }
  }, []);
  return null;
}
```

### Add to Your Root Layout

```tsx theme={null}
// app/layout.tsx
import { GrainAnalytics } from './grain';

export default function RootLayout({ children }: { children: React.ReactNode }) {
  return (
    <html lang="en">
      <body>
        <GrainAnalytics />
        {children}
      </body>
    </html>
  );
}
```

### Add Environment Variable

Create a `.env.local` file:

```bash theme={null}
NEXT_PUBLIC_GRAIN_TENANT_ID=your-tenant-id
```

Replace `your-tenant-id` with the alias from your [dashboard](https://grainql.com/dashboard).

### Use in Components

Now you can track events from any Client Component:

```tsx theme={null}
// app/page.tsx
'use client';

import { track } from '@grainql/tag';

export default function HomePage() {
  return (
    <div>
      <h1>Welcome!</h1>
      <button onClick={() => track('cta_clicked', { location: 'hero' })}>
        Get Started
      </button>
    </div>
  );
}
```

<Note>
  **Server vs Client Components**: `track()`, `identify()`, and other Grain Tag functions must be called from Client Components (with the `'use client'` directive) or inside `useEffect`. Grain Tag is SSR safe — `init()` returns a no-op in non-browser environments — but tracking functions need the browser to work.
</Note>

<Info>
  **Automatic page views**: Grain Tag hooks into the History API to track page views and navigation automatically. You do not need a page view tracker component — this works out of the box with Next.js App Router.
</Info>

### Identify Users

```tsx theme={null}
// Any client component
'use client';

import { identify, track } from '@grainql/tag';

export function LoginHandler() {
  const handleLogin = async (email: string, password: string) => {
    const user = await yourLoginFunction(email, password);
    identify(user.id);
    track('user_logged_in', { method: 'email' });
  };

  return <form onSubmit={handleLogin}>...</form>;
}
```

### Consent Management

```tsx theme={null}
'use client';

import { getInstance } from '@grainql/tag';

export function ConsentBanner() {
  const handleAccept = () => {
    const grain = getInstance();
    grain?.consent.grant();
  };

  const handleDecline = () => {
    const grain = getInstance();
    grain?.consent.revoke();
  };

  return (
    <div>
      <p>We use analytics to improve your experience.</p>
      <button onClick={handleAccept}>Accept</button>
      <button onClick={handleDecline}>Decline</button>
    </div>
  );
}
```

***

## Pages Router (Next.js 12)

### Initialize in \_app.tsx

```tsx theme={null}
// pages/_app.tsx
import type { AppProps } from 'next/app';
import { useEffect } from 'react';
import { init, isInitialized } from '@grainql/tag';

export default function App({ Component, pageProps }: AppProps) {
  useEffect(() => {
    if (!isInitialized()) {
      init({ tenantId: process.env.NEXT_PUBLIC_GRAIN_TENANT_ID! });
    }
  }, []);

  return <Component {...pageProps} />;
}
```

### Add Environment Variable

Create a `.env.local` file:

```bash theme={null}
NEXT_PUBLIC_GRAIN_TENANT_ID=your-tenant-id
```

### Use in Pages

```tsx theme={null}
// pages/index.tsx
import { track } from '@grainql/tag';

export default function HomePage() {
  return (
    <div>
      <h1>Welcome!</h1>
      <button onClick={() => track('cta_clicked', { location: 'hero' })}>
        Get Started
      </button>
    </div>
  );
}
```

<Info>
  **Automatic page views**: Grain Tag automatically tracks navigation via the History API for both App Router and Pages Router. No manual page view tracking is needed.
</Info>

***

## Server-Side Tracking

For tracking events from API routes or Server Actions, use `@grainql/analytics-web` (a different package designed for server-side use):

```bash theme={null}
npm install @grainql/analytics-web
```

### API Routes

```typescript theme={null}
// app/api/checkout/route.ts (App Router)
// Server-side tracking uses @grainql/analytics-web (a different package)
import { createGrainAnalytics } from '@grainql/analytics-web';

const grain = createGrainAnalytics({
  tenantId: process.env.GRAIN_TENANT_ID!,
  authStrategy: 'SERVER_SIDE',
  secretKey: process.env.GRAIN_SECRET_KEY!
});

export async function POST(request: Request) {
  const body = await request.json();

  // Track server-side event
  await grain.track('checkout_completed', {
    order_id: body.orderId,
    total: body.total
  }, { flush: true }); // Flush immediately for serverless

  return Response.json({ success: true });
}
```

<Tip>
  **Serverless tip**: Use `{ flush: true }` to send events immediately before the function terminates.
</Tip>

### Server Actions (App Router)

```typescript theme={null}
// app/actions.ts
'use server';

// Server-side tracking uses @grainql/analytics-web (a different package)
import { createGrainAnalytics } from '@grainql/analytics-web';

const grain = createGrainAnalytics({
  tenantId: process.env.GRAIN_TENANT_ID!,
  authStrategy: 'SERVER_SIDE',
  secretKey: process.env.GRAIN_SECRET_KEY!
});

export async function submitForm(formData: FormData) {
  // Your form logic...

  await grain.track('form_submitted', {
    form_name: 'contact'
  }, { flush: true });

  return { success: true };
}
```

***

## What's Next?

<CardGroup cols={2}>
  <Card title="Core API Reference" icon="book" href="/api-reference/core-methods">
    See all available methods and options
  </Card>

  <Card title="User Identification" icon="user" href="/core/user-identification">
    Track users across sessions
  </Card>

  <Card title="Event Best Practices" icon="chart-line" href="/core/event-tracking">
    Learn what to track and how to structure events
  </Card>

  <Card title="Server-Side Setup" icon="server" href="/advanced/configuration">
    Advanced server-side configuration
  </Card>
</CardGroup>

<Tip>
  **Vercel deployment**: All environment variables starting with `NEXT_PUBLIC_` are automatically available in the browser. Keep secret keys (for server-side tracking) private by omitting the `NEXT_PUBLIC_` prefix.
</Tip>

<Note>
  **Need remote configuration or feature flags?** See [@grainql/analytics-web](/react/overview) for remote config, React hooks (`useConfig`, `useTrack`, `GrainProvider`), and more.
</Note>
