LogTide
Framework
Medium

Next.js Application Logging Integration

Send structured logs from Next.js applications to LogTide with instrumentation hooks, error boundaries, and navigation tracking.

Instrumentation hook Error boundary component Client + server support Navigation tracking

LogTide’s Next.js SDK (@logtide/nextjs) provides server-side instrumentation, client-side error boundaries, and navigation tracking for Next.js App Router applications.

Why use LogTide with Next.js?

  • Instrumentation hook: Initialize once in instrumentation.ts, capture all server errors
  • Error boundaries: Client-side React error capture with LogtideErrorBoundary
  • Navigation tracking: Track client-side route changes as breadcrumbs
  • Server + client: Separate DSNs for server and client-side logging
  • Self-hosted option: Keep logs in your infrastructure

Prerequisites

  • Next.js 14+ (App Router)
  • Node.js 18+
  • LogTide instance with a DSN

Installation

npm install @logtide/nextjs

Server-Side Setup

Initialize LogTide via the instrumentation hook:

// instrumentation.ts
import { registerLogtide } from '@logtide/nextjs/server';

export async function register() {
  registerLogtide({
    dsn: process.env.LOGTIDE_DSN!,
    service: 'nextjs-app',
    environment: process.env.NODE_ENV,
    release: process.env.NEXT_PUBLIC_APP_VERSION,
  });
}

Enable the hook in next.config.ts:

const nextConfig = {
  experimental: {
    instrumentationHook: true,
  },
};

export default nextConfig;

Client-Side Setup

Initialize LogTide on the client:

// app/layout.tsx
import { initLogtide } from '@logtide/nextjs/client';

initLogtide({
  dsn: process.env.NEXT_PUBLIC_LOGTIDE_DSN!,
  environment: process.env.NODE_ENV,
});

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

Environment Variables

# .env.local
LOGTIDE_DSN=https://[email protected]
NEXT_PUBLIC_LOGTIDE_DSN=https://[email protected]
NEXT_PUBLIC_APP_VERSION=1.0.0

Error Capture

Server-side errors in API routes, Server Components, and Server Actions are captured automatically via captureRequestError.

Error Boundary

Use LogtideErrorBoundary for client-side React error capture:

// app/error.tsx
'use client';

import { LogtideErrorBoundary } from '@logtide/nextjs/client';

export default function ErrorPage({
  error,
  reset,
}: {
  error: Error & { digest?: string };
  reset: () => void;
}) {
  return (
    <LogtideErrorBoundary error={error}>
      <div>
        <h2>Something went wrong</h2>
        <button onClick={reset}>Try again</button>
      </div>
    </LogtideErrorBoundary>
  );
}

Track client-side navigation as breadcrumbs:

// app/providers.tsx
'use client';

import { trackNavigation } from '@logtide/nextjs/client';
import { usePathname } from 'next/navigation';
import { useEffect } from 'react';

export function LogtideNavigationTracker() {
  const pathname = usePathname();
  useEffect(() => {
    trackNavigation(pathname);
  }, [pathname]);
  return null;
}

API Route Logging

// app/api/users/[id]/route.ts
import { hub } from '@logtide/core';

export async function GET(
  request: Request,
  { params }: { params: { id: string } }
) {
  hub.captureLog('info', 'Fetching user', { userId: params.id });

  try {
    const user = await getUser(params.id);
    return Response.json(user);
  } catch (error) {
    hub.captureError(error, {
      extra: { userId: params.id },
    });
    return Response.json({ error: 'Internal error' }, { status: 500 });
  }
}

Server Actions

// app/actions/user.ts
'use server';

import { hub } from '@logtide/core';

export async function updateUser(formData: FormData) {
  const userId = formData.get('userId') as string;

  hub.addBreadcrumb({
    category: 'action',
    message: 'Update user action triggered',
  });

  try {
    await db.user.update({ where: { id: userId }, data: { name: formData.get('name') } });
    hub.captureLog('info', 'User updated', { userId });
    return { success: true };
  } catch (error) {
    hub.captureError(error, { extra: { userId } });
    return { success: false };
  }
}

Docker Deployment

FROM node:20-alpine AS base

FROM base AS deps
WORKDIR /app
COPY package.json pnpm-lock.yaml ./
RUN corepack enable pnpm && pnpm install --frozen-lockfile

FROM base AS builder
WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules
COPY . .
RUN corepack enable pnpm && pnpm build

FROM base AS runner
WORKDIR /app
ENV NODE_ENV=production
COPY --from=builder /app/public ./public
COPY --from=builder /app/.next/standalone ./
COPY --from=builder /app/.next/static ./.next/static
EXPOSE 3000
CMD ["node", "server.js"]

Next Steps