Configuring Sentry for Next.js Apps

-

For many teams using Next.js, setting up exception monitoring is a critical requirement before going to production. This guide will show you how to use Sentry to catch & report errors on both client and server-side.

Brief Next.js Overview

Next.js allows you to easily server-side render (SSR) your React components. All files inside the /pages directory will be routed based on their filename (e.g. /pages/home.js routes to your-domain.com/home).

There are a few Next-specific routes you should understand:

  • _document.js is server-side only and is used to change the initial server-side rendered document markup.
  • _app.js is client-side only and is used to initialize pages.

Implementing Sentry

There are a few different ways to integrate Sentry into your Next.js app, each with their own pros and cons. This guide will focus on a simple example and provide a reference for how to expand your Sentry setup as needed.

Simple Setup

You can view the completed example here.

  1. Set up an account with Sentry and retrieve your DSN.
  2. Add Sentry to your project. Here is an example package.json.
{
    "name": "with-sentry-simple",
    "version": "1.0.0",
    "license": "ISC",
    "scripts": {
        "dev": "next",
        "build": "next build",
        "start": "next start"
    },
    "dependencies": {
        "@sentry/browser": "^5.1.0",
        "@zeit/next-source-maps": "0.0.4-canary.1",
        "next": "latest",
        "react": "^16.8.6",
        "react-dom": "^16.8.6"
    }
}
  1. Override _app.js to initialize Sentry and capture client-side exceptions using the componentDidCatch React lifecycle method.
import React from 'react';
import App, {Container} from 'next/app';
import * as Sentry from '@sentry/browser';

Sentry.init({
    dsn: 'ENTER_YOUR_SENTRY_DSN_HERE'
});

class MyApp extends App {
    static async getInitialProps({Component, ctx}) {
        let pageProps = {};

        if (Component.getInitialProps) {
            pageProps = await Component.getInitialProps(ctx);
        }

        return {pageProps};
    }

    componentDidCatch(error, errorInfo) {
        Sentry.withScope((scope) => {
            Object.keys(errorInfo).forEach((key) => {
                scope.setExtra(key, errorInfo[key]);
            });

            Sentry.captureException(error);
        });

        super.componentDidCatch(error, errorInfo);
    }

    render() {
        const {Component, pageProps} = this.props;

        return (
            <Container>
                <Component {...pageProps} />
            </Container>
        );
    }
}

export default MyApp;
  1. Override _document.js to capture server-side exceptions by listening at the node process level.
import Document, {Html, Head, Main, NextScript} from 'next/document';
import * as Sentry from '@sentry/browser';

process.on('unhandledRejection', (err) => {
    Sentry.captureException(err);
});

process.on('uncaughtException', (err) => {
    Sentry.captureException(err);
});

class MyDocument extends Document {
    static async getInitialProps(ctx) {
        const initialProps = await Document.getInitialProps(ctx);
        return {...initialProps};
    }

    render() {
        return (
            <Html>
                <Head />
                <body>
                    <Main />
                    <NextScript />
                </body>
            </Html>
        );
    }
}

export default MyDocument;
  1. Override next.config.js to produce source maps.
const withSourceMaps = require('@zeit/next-source-maps')();

module.exports = withSourceMaps({
    webpack(config, _options) {
        return config;
    }
});

Complex Setup

Next has an example with-sentry which contains more functionality but is also more complex. Building off the simple example, it also:

  • Reports the BUILD_ID as a release inside Sentry.
  • Contains a custom error reporting page to allow users to submit feedback.
  • Uses cookies for a more accurate user count between client and server.

There are still a few issues with it, so I wouldn't recommend using it as a base for now. You might be able to extract pieces of the complex example and apply them as you see fit.

Other Notes

  • Source maps will not be sent to Sentry successfully when running locally.
  • It's possible you will see duplicate errors sent when testing locally due to hot reloading. For a more accurate simulation, please deploy to whatever service you use for hosting (e.g. Now).

Looking to Learn React & Next.js? 🏆

I've launched a new video course Mastering Next.js containing 55 jam-packed lessons. Pre-order now and get 50% off!

Mastering Next.js

Discuss on TwitterEdit on GitHub

Want More? Be Notified When I Post New Articles 🚀