# Better Stack Next.js client

## Start logging in 6 minutes

### 1. Install

Install Logtail Next.js NPM package:

```bash
[label Install Logtail Next.js]
npm install @logtail/next
```

[info]
#### Using the Pages Router?
See [legacy docs](https://betterstack.com/docs/logs/javascript/nextjs-legacy/) for `v0.1.*`, or [upgrade to the App Router](#upgrade-to-the-app-router).
[/info]


### 2. Setup

Set environment variables, e.g. in your console before running the server:

```bash
[label Set environment variable]
export NEXT_PUBLIC_BETTER_STACK_SOURCE_TOKEN="$SOURCE_TOKEN"
export NEXT_PUBLIC_BETTER_STACK_INGESTING_URL="https://$INGESTING_HOST"
```

Set up Better Stack Next.js client in `next.config.ts` or `next.config.js`:

```ts
[label Wrap Next.js config with Better Stack]
import { withBetterStack } from '@logtail/next';

export default withBetterStack({
  // Your existing config
});
```

### 3. Start logging 🎉

Use structured logging in client, edge, or server-side files.

You should see your logs in [Better Stack → Live tail](https://telemetry.betterstack.com/team/0/tail ";_blank").

[info]
#### Using older versions of Next.js?

For Next.js version 14, use `@logtail/next` in `0.2.*`. For slight differences in syntax, see the "in Next.js v14" tab in examples below.

Older versions 12.1.4 and above are supported in `@logtail/next` version `0.1.*`. See [legacy docs](https://betterstack.com/docs/logs/javascript/nextjs-legacy/).
[/info]

### Route handlers

Wrap your route handlers in `withBetterStack` to add a logger to your request and log exceptions automatically:

[code-tabs]
```ts
[label Wrap route handler with Better Stack]
import { withBetterStack, BetterStackRequest } from '@logtail/next';

export const GET = withBetterStack((request: BetterStackRequest) => {
  request.log.info('Login function called');

  // You can create intermediate loggers
  const log = request.log.with({ scope: 'user' });
  log.info('User logged in', { userId: 42 });

  return NextResponse.json({ hello: 'world' });
});
```
```ts
[label in Next.js v14]
import { withBetterStack, BetterStackRequest } from '@logtail/next';

export const GET = withBetterStack((req: BetterStackRequest) => {
  req.log.info('Login function called');

  // You can create intermediate loggers
  const log = req.log.with({ scope: 'user' });
  log.info('User logged in', { userId: 42 });

  return NextResponse.json({ hello: 'world' });
});
```
[/code-tabs]

### Client components

To send logs from client components, add `useLogger` to your component:

[code-tabs]
```ts
[label Send logs from client components to Better Stack]
'use client';
import { useLogger } from '@logtail/next/hooks';

export default function ClientComponent() {
  const log = useLogger();
  log.debug('User logged in', { userId: 42 });
  return <h1>Logged in</h1>;
}
```
```ts
[label in Next.js v14]
'use client';
import { useLogger } from '@logtail/next';

export default function ClientComponent() {
  const log = useLogger();
  log.debug('User logged in', { userId: 42 });
  return <h1>Logged in</h1>;
}
```
[/code-tabs]

### Server components

To send logs from server components, add `Logger` to your component, and call flush before returning:

```ts
[label Send logs from server components to Better Stack]
import { Logger } from '@logtail/next';

export default async function ServerComponent() {
  const log = new Logger();
  log.info('User logged in', { userId: 42 });

  // ...

  await log.flush();
  return <h1>Logged in</h1>;
}
```

### Configuring authentication middleware

If your Next.js application uses authentication middleware (like Clerk or NextAuth.js), it may block telemetry requests sent from the browser. These requests, sent to the `/_betterstack/*` proxy route, are essential for collecting [client-side logs](#client-components) and [Web Vitals](#web-vitals).

To prevent issues, you must configure your middleware to treat `/_betterstack/(.*)` as a public or ignored route.

Here are configuration examples for common middleware libraries:

[code-tabs]
```ts
[label middleware.ts (Clerk)]
import { clerkMiddleware, createRouteMatcher } from "@clerk/nextjs/server";

const isPublicRoute = createRouteMatcher([
  '/sign-in(.*)',
  '/sign-up(.*)',
  '/_betterstack/(.*)', // Allow Better Stack telemetry
]);

export default clerkMiddleware((auth, request) => {
  // Skip authentication for public routes
  if (isPublicRoute(request)) {
    return;
  }

  // Protect all other routes
  auth().protect();
});

export const config = {
  matcher: ["/((?!.*\\..*|_next).*)", "/", "/(api|trpc)(.*)"],
};
```
```ts
[label middleware.ts (NextAuth.js)]
export { default } from "next-auth/middleware"

export const config = { 
  matcher: [
    /*
     * Match all request paths except for the ones starting with:
     * - api (API routes)
     * - _next/static (static files)
     * - _next/image (image optimization files)
     * - favicon.ico (favicon file)
     * - _betterstack (Better Stack telemetry)
     */
    '/((?!api|_next/static|_next/image|favicon.ico|_betterstack).*)',
  ]
}
```
[/code-tabs]

By correctly configuring your middleware, you ensure that all telemetry data from the browser is successfully collected.

## Need help?

Please let us know at hello@betterstack.com.  
We're happy to help! 🙏

## Advanced usage

### Capture traffic requests

To capture traffic requests, create a `middleware.ts` file in the root folder of your Next.js app:

```ts
[label Capture traffic requests using middleware]
import { Logger } from '@logtail/next'
import { NextResponse } from 'next/server'
import type { NextFetchEvent, NextRequest } from 'next/server'

export async function middleware(request: NextRequest, event: NextFetchEvent) {
  const logger = new Logger({ source: "middleware" });
  await logger.middleware(request, { logRequestDetails: ["body", "nextUrl"] });

  event.waitUntil(logger.flush());
  return NextResponse.next();
}
```

### Log levels

The log level defines the lowest level of logs sent to Better Stack. Choose one of the following levels (from lowest to highest):

- `debug` is the default setting. It means that you send all logs to Better Stack.
- `info`
- `warn`
- `error` means that you only send the highest-level logs to Better Stack.
- `off` means that you don't send any logs to Better Stack.

For example, to send all logs except for debug logs to Better Stack:

```sh
[label Set environment variable to change log level]
export NEXT_PUBLIC_BETTER_STACK_LOG_LEVEL=info
```


### Web Vitals

To send Web Vitals to Better Stack, add the `BetterStackWebVitals` component to the `app/layout.tsx` file:

[code-tabs]
```ts
[label Send Web Vitals to Better Stack]
import { BetterStackWebVitals } from '@logtail/next/webVitals';

export default function RootLayout() {
  return (
    <html>
      ...
      <BetterStackWebVitals />
      <div>...</div>
    </html>
  );
}
```
```ts
[label in Next.js v14]
import { BetterStackWebVitals } from '@logtail/next';

export default function RootLayout() {
  return (
    <html>
      ...
      <BetterStackWebVitals />
      <div>...</div>
    </html>
  );
}
```
[/code-tabs]

### Capture errors

To capture routing errors, use the [error handling mechanism of Next.js](https://nextjs.org/docs/app/building-your-application/routing/error-handling):

1. Go to the `app` folder.
2. Create an `error.tsx` file.
3. Inside your component function, add `useLogger` to send the error to Better Stack. For example:

[code-tabs]
```ts
[label Send errors to Better Stack]
"use client";

import { useLogger } from "@logtail/next/hooks";
import { LogLevel } from "@logtail/next";
import { usePathname } from "next/navigation";

export default function ErrorPage({
  error,
}: {
  error: Error & { digest?: string };
}) {
  const pathname = usePathname()
  const log = useLogger({ source: "error.tsx" });
  let status =  error.message == 'Invalid URL' ? 404 : 500;

  log.logHttpRequest(
    LogLevel.error,
    error.message,
    {
      host: window.location.href,
      path: pathname,
      statusCode: status,
    },
    {
      error: error.name,
      cause: error.cause,
      stack: error.stack,
      digest: error.digest,
    },
  );

  return (
    <div className="p-8">
      Ops! An Error has occurred:{" "}
      <p className="text-red-400 px-8 py-2 text-lg">`{error.message}`</p>
      <div className="w-1/3 mt-8">
        <NavTable />
      </div>
    </div>
  );
}
```
```ts
[label in Next.js v14]
"use client";

import { useLogger, LogLevel } from "@logtail/next";
import { usePathname } from "next/navigation";

export default function ErrorPage({
  error,
}: {
  error: Error & { digest?: string };
}) {
  const pathname = usePathname()
  const log = useLogger({ source: "error.tsx" });
  let status =  error.message == 'Invalid URL' ? 404 : 500;

  log.logHttpRequest(
    LogLevel.error,
    error.message,
    {
      host: window.location.href,
      path: pathname,
      statusCode: status,
    },
    {
      error: error.name,
      cause: error.cause,
      stack: error.stack,
      digest: error.digest,
    },
  );

  return (
    <div className="p-8">
      Ops! An Error has occurred:{" "}
      <p className="text-red-400 px-8 py-2 text-lg">`{error.message}`</p>
      <div className="w-1/3 mt-8">
        <NavTable />
      </div>
    </div>
  );
}
```
[/code-tabs]

### How can I extend the logger?

You can use `log.with` to create an intermediate logger, for example:

```typescript
[label Extend logger with additional context]
const logger = userLogger().with({ userId: 42 });
logger.info('Hi'); // will ingest { ..., "message": "Hi", "fields" { "userId": 42 }}
```

## Upgrade to the App Router

`@logtail/next` switched to support the App Router starting with version `0.2.0`.
If you are upgrading a Pages Router app with `@logtail/next` `v0.1.x` to the App Router, you will need to make the following changes:

- Upgrade `@logtail/next` to version `0.2.0` or higher
- Make sure that exported variables has `NEXT_PUBLIC_` prefix, e.g: `NEXT_PUBLIC_BETTER_STACK_SOURCE_TOKEN`
- Use `useLogger` hook in client components instead of `log` prop
- For server side components, you will need to create an instance of `Logger` and flush the logs before component returns.
- For web-vitals, remove `reportWebVitals()` and instead add the `BetterStackWebVitals` component to your layout.
