Install Flawsnap in a Next.js app

Use next/script in the root layout so the widget loads once for the whole app and survives client-side navigation.

Steps

  1. 1

    Allow your hosts

    In Flawsnap, add your production domain, localhost:3000 and the preview hosts you test on under Security → Allowed domains.

  2. 2

    Store the key

    Add NEXT_PUBLIC_FLAWSNAP_KEY=pk_… to .env.local and to your hosting provider’s environment variables.

  3. 3

    Add next/script to the root

    App Router: add a <Script> to app/layout.tsx. Pages Router: add it to pages/_app.tsx. Use strategy="afterInteractive".

  4. 4

    Identify users in a client component

    Add a small 'use client' component that calls window.Flawsnap?.identify() when the session is known.

  5. 5

    Check it

    Run next dev, open localhost:3000, send a test report and confirm it lands in the project.

App Router: app/layout.tsx

The root layout renders once and stays mounted as people move between routes, which is exactly the lifetime the widget needs. next/script passes data-* props through to the tag.

app/layout.tsx
import Script from 'next/script'

export default function RootLayout({ children }: { children: React.ReactNode }) {
  return (
    <html lang="en">
      <body>
        {children}
        <Script
          src="https://backend.flawsnap.com/widget.js"
          data-key={process.env.NEXT_PUBLIC_FLAWSNAP_KEY}
          strategy="afterInteractive"
        />
      </body>
    </html>
  )
}

Pages Router: pages/_app.tsx

pages/_app.tsx
import type { AppProps } from 'next/app'
import Script from 'next/script'

export default function App({ Component, pageProps }: AppProps) {
  return (
    <>
      <Script
        src="https://backend.flawsnap.com/widget.js"
        data-key={process.env.NEXT_PUBLIC_FLAWSNAP_KEY}
        strategy="afterInteractive"
      />
      <Component {...pageProps} />
    </>
  )
}

Do not put the script in a page or a nested layout. It would load when that route first renders and be missing from the others.

Choosing the strategy

  • afterInteractive (recommended) loads the widget once the page is interactive. It does not compete with your own JavaScript for the first paint.
  • beforeInteractive loads it earlier, so errors during hydration are captured too. Next.js only allows it in the root layout (or _document on the Pages Router).
  • Avoid lazyOnload here: it waits for idle time, and a bug that happens early in the visit would have no console or network history.

Identify users from the session

window only exists in the browser, so identify from a client component rendered inside the layout. With NextAuth, for example:

app/flawsnap-identify.tsx
'use client'
import { useEffect } from 'react'
import { useSession } from 'next-auth/react'

export function FlawsnapIdentify() {
  const { data } = useSession()
  useEffect(() => {
    const u = data?.user
    if (u?.email) window.Flawsnap?.identify({ name: u.name ?? '', email: u.email })
  }, [data])
  return null
}

The window type

types/flawsnap.d.ts
// flawsnap.d.ts (anywhere your tsconfig includes)
export {}

declare global {
  interface Window {
    Flawsnap?: {
      open(): void
      identify(reporter: { name?: string; email?: string }): void
      setMetadata(details: Record<string, unknown>): void
      screen(name: string): void
      track(name: string): void
      resetUser(): void
    }
  }
}

Preview deployments and allowed domains

Each preview deployment on a platform such as Vercel gets its own host name. Flawsnap only accepts wildcards over a domain you own, and refuses them over shared hosting suffixes, so a wildcard over the platform’s domain is not the way to cover previews.

  • List the exact preview host you are testing, such as a branch alias that stays the same between pushes.
  • Or put previews on your own domain, such as pr-42.preview.example.com, and allow *.preview.example.com.
  • Remember localhost:3000 for next dev.

Troubleshooting

  • data-key is empty in production: NEXT_PUBLIC_ variables are fixed at build time. Set it in your host’s settings and rebuild.
  • The console says the widget is on the page twice: the <Script> is in more than one layout. Keep only the root one.
  • A strict CSP set in middleware.ts or next.config: allow https://backend.flawsnap.com in script-src and connect-src.

Questions

Does the widget work with server components?
Yes. The <Script> tag can sit in a server layout. Only code that touches window.Flawsnap, such as identify, needs a client component.
Are route changes tracked in Insights?
Yes. Insights (Pro) follows pushState navigation, which is how the Next.js router changes pages, and records each path as a screen with ids masked.

Try it on your own site

7-day free trial, then $9 a month for Starter or $19 for Pro, per organization, with unlimited team members.

Updated