> For the complete documentation index, see [llms.txt](https://docs.sealights.io/knowledgebase/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.sealights.io/knowledgebase/setup-and-configuration/sealights-agents-and-plugins/node.js-agent/advanced-features/next.js-with-sealights-node-and-browser-agents-multi-module-scan.md).

# Next.js with SeaLights Node and Browser Agents (Multi-Module Scan)

## Next.js with SeaLights Node and Browser Agents (Multi-Module Scan)

> **TL;DR:** For Next.js apps you run **two scans** against one build session — one for the browser bundles in `.next/static/`, one for the rest of `.next/`. Then start the server with the SeaLights Node preload.
>
> **Requires:** `slnodejs` ≥ 6.2.10

***

### Overview

Next.js produces two distinct bundle types during a production build:

| Bundle type          | Location         | How SeaLights instruments it                         |
| -------------------- | ---------------- | ---------------------------------------------------- |
| **Client (browser)** | `.next/static/`  | Build-time scan with `--instrumentForBrowsers`       |
| **Server (Node.js)** | Rest of `.next/` | Runtime preload via `-r .../slnodejs/lib/preload.js` |

Both scans share a single build session and are distinguished by `--uniqueModuleId`, so the SeaLights dashboard surfaces them as one unified application.

***

### Prerequisites

* Node.js project with Next.js (15+ recommended)
* `slnodejs` ≥ 6.2.10 installed (`npm install slnodejs` or use `npx`)
* A SeaLights agent token from **Dashboard → Settings → Agent Tokens**

***

### Step 1 — Enable Source Maps in Next.js

Add both flags to `next.config.mjs`:

```js
/** @type {import('next').NextConfig} */
const nextConfig = {
  productionBrowserSourceMaps: true,
  experimental: { serverSourceMaps: true },
};

export default nextConfig;
```

{% hint style="danger" %}
**Do not skip this step.** SeaLights uses the generated `.js.map` files to map runtime coverage hits back to your original source files. Without source maps the server will report **0% coverage**.
{% endhint %}

***

### Step 2 — Suppress Framework Noise from the Build Map

Create `.slignore.generated` at the **project root**:

```
webpack:/**
.next/**
```

Without this file, the SeaLights coverage tree shows \~200 internal Next.js/webpack entries that obscure your actual `src/` code.

***

### Step 3 — Provision Your Agent Token

Place your SeaLights agent token in `sltoken.txt` at the project root.

{% hint style="danger" %}
**Never commit this file.** Add the following entries to `.gitignore`:
{% endhint %}

```
sltoken.txt
buildSessionId
sl-debug.log
.next/
sl-dist/
node_modules/
```

***

### Step 4 — Run the SeaLights Build and Scan Flow

The script below executes the complete build → scan → start lifecycle.

```bash
#!/usr/bin/env bash
#
# End-to-end SeaLights flow for a Next.js SSR project:
#   1. Build the app (Next.js produces `.next/`)
#   2. Open a SeaLights build session
#   3. Scan client bundles (`.next/static/`)
#   4. Scan server bundles (the rest of `.next/`)
#   5. Mark the build session as complete
#   6. Start the server with the runtime preload so coverage is collected

set -euo pipefail

if [ ! -f sltoken.txt ]; then
  echo "ERROR: sltoken.txt not found in $(pwd)." >&2
  echo "       Copy sltoken.txt.example, paste your SeaLights agent token, then re-run." >&2
  exit 1
fi

# 1. Clean previous builds
rm -rf .next sl-dist

# 2. Build the Next.js app (production mode, source maps enabled in next.config.mjs)
npm install
npm run build

# 3. Open a build session
#    In CI, replace the timestamp with your commit SHA or build system ID.
BUILD_ID="$(date +%s)"
npx slnodejs config \
  --appname "<YOUR_APP>" \
  --branch main \
  --build "$BUILD_ID"

# 4. Client scan — instruments browser bundles into sl-dist/
#    and registers them as the "client" sub-module in the build session.
npx slnodejs scan \
  --workspacepath .next/static \
  --es6Modules \
  --scm none \
  --instrumentForBrowsers \
  --outputpath sl-dist \
  --useRelativeSlMapping \
  --newInstrumenter \
  --uniqueModuleId client

# Remove the un-instrumented client output so it is not re-scanned
# by the server pass. The instrumented copy in sl-dist/ is restored below.
rm -rf .next/static

# 5. Server scan — walks the rest of .next/ (server bundles only)
npx slnodejs scan \
  --workspacepath .next \
  --es6Modules \
  --scm none \
  --uniqueModuleId server

# 6. Finalise the build session (signals all sub-modules have been submitted)
npx slnodejs scanned --buildsessionidfile buildSessionId --ok

# 7. Restore the instrumented client bundles so Next.js serves them at runtime
mv sl-dist .next/static

# 8. Start the server with the SeaLights Node preload
#    This script does NOT open or close a test session — see Step 5.
NODE_DEBUG=sl \
SL_LOG_LEVEL=debug \
NODE_OPTIONS='-r ./node_modules/slnodejs/lib/preload.js' \
npm run start
```

Replace `<YOUR_APP>` with your SeaLights application name. In CI, replace `--branch main` and `--build "$BUILD_ID"` with your real branch name and build identifier (e.g. the commit SHA).

***

### Step 5 — Open a Test Session, Exercise the App, Then Close It

Once the server from Step 4 is running, open a named test stage in a **separate terminal** at the same project root. This associates all incoming coverage data with the correct test stage on the dashboard.

```bash
# Open the test session
npx slnodejs start --testStage Manual --buildSessionIdFile buildSessionId

# ... exercise the app: browser clicks, curl calls, your e2e suite, etc.

# Close — flushes pending footprints and finalises the test stage
npx slnodejs end --buildSessionIdFile buildSessionId
```

{% hint style="info" %}
The test session lifecycle is intentionally separate from the build/scan/start script. This lets you run multiple test passes (`Manual`, `RegressionSuite`, etc.) against the same running server without rebuilding or re-scanning the application.
{% endhint %}

***

### Step 6 — Verify Coverage on the Dashboard

1. Stop the server (`Ctrl+C`).
2. Wait **30–60 seconds** for data ingestion to complete.
3. Open the SeaLights coverage dashboard and navigate to the build named `<YOUR_APP>` under the **Manual** test stage.

You should see a single `src/` tree with both server-side and client-side files populated.

***

### Why Two Scans?

Next.js splits your code into two fundamentally different execution environments, each requiring a different instrumentation approach:

**Client bundles** (`.next/static/`) These are the JavaScript files Next.js delivers to the user's browser. SeaLights must rewrite them at build time using `--instrumentForBrowsers` so that code execution in the browser reports coverage back to the SeaLights backend.

**Server bundles** (rest of `.next/`) These are Node.js modules that run server-side. They do not need build-time rewriting. Instead, the runtime preload (`-r .../slnodejs/lib/preload.js`) hooks into Node's module loader and collects coverage on the fly as each module is required.

Both scans write to the **same build session** and are differentiated by `--uniqueModuleId client` and `--uniqueModuleId server`. The dashboard therefore presents them as a single unified application.

***

***

### Troubleshooting

| Symptom                                                          | Likely Cause                                             | Fix                                                                                 |
| ---------------------------------------------------------------- | -------------------------------------------------------- | ----------------------------------------------------------------------------------- |
| `src/` shows 0% coverage                                         | Source maps not enabled                                  | Add `productionBrowserSourceMaps` and `serverSourceMaps` flags to `next.config.mjs` |
| `webpack:/` and `.next/` folders appear in the dashboard         | Missing or unreadable `.slignore.generated`              | Place the file at the project root; ensure `slnodejs` ≥ 6.2.10                      |
| `lib/` and `layout.tsx` stay at 0% even though pages are covered | Cross-bundle source-map fallback bug in older `slnodejs` | Upgrade to `slnodejs` ≥ 6.2.10                                                      |
| `/api/...` routes show 0%                                        | Routes are never called implicitly by Next.js            | Add explicit `curl` or integration test calls for each API route in your test run   |
| `sltoken.txt not found`                                          | Agent token not provisioned                              | Copy `sltoken.txt.example` and paste your JWT token into it                         |


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://docs.sealights.io/knowledgebase/setup-and-configuration/sealights-agents-and-plugins/node.js-agent/advanced-features/next.js-with-sealights-node-and-browser-agents-multi-module-scan.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
