# See which campaign brings your Next.js customers

> Add MIRA FIVE to a Next.js app with @mirafive/sdk-next, one provider in the root layout, and paid orders sent from your route handlers.

URL: https://mirafive.io/for/nextjs  
Updated: 2026-09-28

One provider in the root layout counts every route change, and your route handlers report the orders that paid.

Package `@mirafive/sdk-next`

## Set up in 5 steps

1. ### Install the packages  
`@mirafive/sdk-next` brings the provider and a server entry. It works with Next.js 15.1 and 16, React 18.3 and 19, and Node.js 20 or later.  
```  
npm install @mirafive/sdk-next @mirafive/sdk-react @mirafive/sdk-browser @mirafive/sdk-server  
```
2. ### Set the two keys  
The website key of your website source goes to the browser. The secret key of a server source stays on the server, so never give it a `NEXT_PUBLIC_` prefix. Both are under **Data → Sources**.  
```  
NEXT_PUBLIC_MIRAFIVE_KEY=mf_…  
MIRAFIVE_SECRET_KEY=mf_…  
```
3. ### Wrap the root layout  
The provider creates the browser client once and counts the first page and every navigation, in the App Router and the Pages Router. You add no `usePathname` effect and no Suspense boundary.  
```  
import { MiraProvider } from '@mirafive/sdk-next'  
import type { ReactNode } from 'react'  
export default function RootLayout({ children }: { children: ReactNode }) {  
  return (  
    <html lang="en">  
      <body>  
        <MiraProvider>{children}</MiraProvider>  
      </body>  
    </html>  
  )  
}  
```
4. ### Record what matters  
Call `track` from any client component inside the provider. Event names that start with `$` are reserved.  
```  
'use client'  
import { useMira } from '@mirafive/sdk-next'  
export function AddToCart() {  
  const mira = useMira()  
  return <button onClick={() => mira.track('added_to_cart', { product: 'merino-crew' })}>Add to cart</button>  
}  
```
5. ### Send the paid order from the server  
`mira()` from `@mirafive/sdk-next/server` is the server SDK's `Mira`, one client per process, made from `MIRAFIVE_SECRET_KEY`. Inside a request it sends the events after the response, so nothing waits for analytics.  
```  
import { mira } from '@mirafive/sdk-next/server'  
// In the route handler that confirms the payment  
mira().track('order_paid', {  
  userId: order.customerId,  
  properties: { revenue: 328, currency: 'EUR' },  
})  
```

MIRA FIVE is analytics built around the person. In a Next.js app one package covers both sides: a provider for the browser, and a server entry for route handlers, server actions and Server Components.

## What will I see after installing?

The first three questions the dashboard answers, from the default setup:

1. **Which channels and campaigns send visitors?** Acquisition › Channels splits visits into nine channels, and each channel opens to its campaigns from your UTM tags. In the sample shop, Direct sent 525 visits, Organic search 438 and Paid search 240 over 30 days.
2. **Which pages do they land on?** Journeys › Pages lists every route with pageviews, entries, bounce rate, exits and time on page. Sort by entries to see your landing pages.
3. **Which goals do they reach?** Goals counts every conversion of the events you send, such as a sign-up or a paid order.

The default setup sets no cookies, stores nothing on the device and sends no identifier.

## How does a paid order reach its channel?

Through the person who paid. With their consent, the browser keeps an id for the visitor, and `identify` ties it to your own user id when they sign in. The route handler sends the order with the same user id, so MIRA FIVE puts the purchase on that person, next to the campaign that first brought them.

Create a purchase goal for `order_paid`, and Acquisition shows buyers and revenue per channel and campaign. With Google Ads connected, it adds [cost per buyer](https://mirafive.io/learn/google-ads-cost-per-buyer) for each campaign.

## How do I collect with consent?

Switch the website source to **Full** under **Data → Sources**. Plugins are functions, so set full mode in a client providers file and render it in the root layout instead of the plain provider:

```tsx
'use client'

import { identity } from '@mirafive/sdk-browser/identity'
import { MiraProvider } from '@mirafive/sdk-next'
import type { ReactNode } from 'react'

export function Providers({ children }: { children: ReactNode }) {
  return (
    <MiraProvider mode="full" plugins={[identity()]}>
      {children}
    </MiraProvider>
  )
}
```

Then, in client components, pass your banner’s answer and the signed-in user:

```tsx
const mira = useMira()

// From your consent banner's callback
mira.consent({ statistics: true, experiments: true, targeting: false })

// After sign-in, with your own user id
mira.identify(user.id, { plan: user.plan })

// After sign-out
mira.reset()
```

Before an answer, full mode stores and sends nothing. Ids live in localStorage, never in cookies. The [consent guide](https://docs.mirafive.io/guides/consent) explains the three scopes.

## How do I make a webhook count once?

Payment providers sometimes deliver a webhook twice. `send` delivers at once and takes an idempotency key, so a repeat is stored once:

```ts
await mira().send(
  [{ name: 'order_paid', userId: order.customerId, properties: { revenue: 328, currency: 'EUR' } }],
  { idempotencyKey: `order-${order.id}` },
)
```

The [server-side guide](https://docs.mirafive.io/guides/server-side) covers retries and idempotency.

## Can I render feature flags without a flicker?

Yes. `flagsFor()` from `@mirafive/sdk-next/server` reads the visitor’s [feature flags](https://mirafive.io/glossary/feature-flag) in the root layout, and the provider hands the answers to the browser, so the first paint shows the right variant. Client components read them with `useFlag` and `useFlagConfig`. A code [A/B test](https://mirafive.io/glossary/ab-test) is a flag read the same way. The [Next.js SDK docs](https://docs.mirafive.io/sdks/nextjs) show the three pieces and the caching rules.

## Other setups

The same numbers, whichever way your site is built.

- [Astro](https://mirafive.io/for/astro)
- [React](https://mirafive.io/for/react)
- [Laravel](https://mirafive.io/for/laravel)
- [Nuxt](https://mirafive.io/for/nuxt)
- [Symfony](https://mirafive.io/for/symfony)
- [PHP](https://mirafive.io/for/php)
- [Vue](https://mirafive.io/for/vue)
- [TanStack Start](https://mirafive.io/for/tanstack-start)
- [Node.js](https://mirafive.io/for/nodejs)
- [Convex](https://mirafive.io/for/convex)
- [JavaScript](https://mirafive.io/for/javascript)
- [any website](https://mirafive.io/for/website)

[Read the docs](https://docs.mirafive.io)

## Questions and answers

### Does it work with the Pages Router?

Yes. Put MiraProvider in pages/\_app.tsx. Add @mirafive/sdk-next to transpilePackages before you import the server entry, and await mira().flush() in API routes, because after() does not run there.

### Can I send events from middleware?

Yes. Use Mira from @mirafive/sdk-server with the secret key, and hand the flush to the event's waitUntil. The server entry runs on the Node.js and the Edge runtime.

### Which versions are supported?

Next.js 15.1 and 16, React 18.3 and 19, and Node.js 20 or later. The package is ESM only.

## See which channel brings buyers

Free for 25,000 events a month. No card needed.

[Start free](https://app.mirafive.io/register)[See pricing](https://mirafive.io/pricing)
