Next.js 15 Integration Guide

Install IssueCapture in Next.js 15

Add bug reporting to your Next.js 15 application in 5 minutes. Fully compatible with React 19, Turbopack, and the App Router.

What's New in Next.js 15

  • React 19: IssueCapture fully supports React 19 features
  • Turbopack: Faster dev builds with zero configuration changes
  • Async Request APIs: Works with new async cookies() and headers()
  • Enhanced Caching: IssueCapture respects Next.js 15 caching strategies

Quick Summary

  • Time: 2-5 minutes (7 simple steps)
  • Prerequisites: Next.js 15+, React 19, Jira account, IssueCapture account (free)
  • Method: Add Script component to root layout.tsx
  • Works with: App Router, Server Components, Client Components, Turbopack
  • Bundle size: 32KB initial (152KB for screenshot & annotation tools, lazy-loaded)

Prerequisites

  • Next.js 15 or higher installed (with React 19)
  • Jira Cloud account (Software or JSM)
  • IssueCapture account (sign up free)
  • Basic knowledge of Next.js App Router

Step-by-Step Installation

1

Get Your API Key

Sign up for IssueCapture and retrieve your widget API key from the dashboard

  • Go to https://issuecapture.com/signup and create a free account
  • Connect your Jira instance via OAuth
  • Navigate to the Widgets page and create a new widget
  • Copy your API key (starts with "ic_")
2

Add Environment Variable

Store your API key securely in .env.local

  • Create .env.local in your project root (if it doesn't exist)
  • Add NEXT_PUBLIC_ prefix to make it accessible in the browser
  • Never commit this file to version control (add to .gitignore)
  • Restart your dev server after adding environment variables
# .env.local
NEXT_PUBLIC_ISSUECAPTURE_API_KEY=ic_your_api_key_here
3

Install in Root Layout (App Router)

Add IssueCapture to your root layout.tsx for site-wide availability

  • Use Next.js Script component for optimized loading
  • strategy="afterInteractive" loads after page becomes interactive
  • The auto trigger creates a floating button automatically
  • Widget is available on all pages in your app
  • Works seamlessly with Next.js 15's React 19 and Turbopack
// app/layout.tsx
import Script from 'next/script'

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

        {/* IssueCapture Widget */}
        <Script
          src="https://issuecapture.com/widget.js"
          strategy="afterInteractive"
          onLoad={() => {
            if (window.IssueCapture) {
              window.IssueCapture.init({
                apiKey: process.env.NEXT_PUBLIC_ISSUECAPTURE_API_KEY!,
                trigger: 'auto', // Creates floating button
                button: {
                  position: 'bottom-right',
                  text: 'Report Bug'
                }
              })
            }
          }}
        />
      </body>
    </html>
  )
}
4

Option: Custom Trigger Button

Use your own button instead of the auto-generated one

  • Replace trigger: "auto" with a CSS selector
  • The selector can target any element on the page
  • Multiple elements can be targeted (e.g., ".report-bug-btn")
  • The widget opens when the element is clicked
// app/layout.tsx
import Script from 'next/script'

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

        {/* Your custom button */}
        <button id="custom-bug-button" className="your-styles">
          Report Issue
        </button>

        {/* IssueCapture Widget */}
        <Script
          src="https://issuecapture.com/widget.js"
          strategy="afterInteractive"
          onLoad={() => {
            if (window.IssueCapture) {
              window.IssueCapture.init({
                apiKey: process.env.NEXT_PUBLIC_ISSUECAPTURE_API_KEY!,
                trigger: '#custom-bug-button' // Use CSS selector
              })
            }
          }}
        />
      </body>
    </html>
  )
}
5

Client Component Example (React 19)

Open the widget programmatically from a Client Component with React 19

  • Use window.IssueCapture.open() to open programmatically
  • Use window.IssueCapture.close() to close programmatically
  • Check if window.IssueCapture exists before calling methods
  • Fully compatible with React 19 (Next.js 15 default)
  • Add TypeScript declarations (see troubleshooting below)
// components/BugReportButton.tsx
'use client'

import { useState } from 'react'

export default function BugReportButton() {
  const [isOpen, setIsOpen] = useState(false)

  const openWidget = () => {
    if (window.IssueCapture) {
      window.IssueCapture.open()
      setIsOpen(true)
    }
  }

  const closeWidget = () => {
    if (window.IssueCapture) {
      window.IssueCapture.close()
      setIsOpen(false)
    }
  }

  return (
    <button
      onClick={openWidget}
      className="px-4 py-2 bg-blue-600 text-white rounded"
    >
      Report Bug
    </button>
  )
}
6

Set User Context (Optional)

Pre-fill user information for authenticated users using async Server Components

  • Pre-fill reporter name and email for authenticated users
  • Component field maps to Jira components (useful for routing)
  • User data is included in the Jira issue description
  • Works with Next.js 15 async Server Components
  • This is optional but improves the developer experience
// app/layout.tsx
import Script from 'next/script'
import { auth } from '@/lib/auth' // Your auth library

export default async function RootLayout({
  children,
}: {
  children: React.ReactNode
}) {
  const session = await auth()

  return (
    <html lang="en">
      <body>
        {children}

        <Script
          src="https://issuecapture.com/widget.js"
          strategy="afterInteractive"
          onLoad={() => {
            if (window.IssueCapture) {
              window.IssueCapture.init({
                apiKey: process.env.NEXT_PUBLIC_ISSUECAPTURE_API_KEY!,
                trigger: 'auto',
                user: {
                  name: session?.user?.name || "",
                  email: session?.user?.email || "",
                  component: 'Frontend' // Optional: Jira component
                }
              })
            }
          }}
        />
      </body>
    </html>
  )
}
7

Test the Integration

Verify IssueCapture is working correctly with Next.js 15

  • Start your dev server: npm run dev (uses Turbopack by default in Next.js 15)
  • Open your app in the browser
  • You should see the floating bug report button
  • Click it and submit a test issue
  • Check your Jira project for the created issue
  • Open browser console and type: window.IssueCapture.isOpen()

Troubleshooting

TypeScript: Property 'IssueCapture' does not exist on type 'Window'

Add type declarations to your project:

// types/issuecapture.d.ts
interface IssueCaptureAPI {
  init(config: {
    apiKey: string
    trigger?: string | 'auto'
    button?: {
      position?: 'bottom-right' | 'bottom-left' | 'top-right' | 'top-left'
      text?: string
      icon?: string
    }
    user?: {
      name?: string
      email?: string
      component?: string
    }
    onSubmit?: (data: any) => void
    onOpen?: () => void
    onClose?: () => void
  }): void
  open(): void
  close(): void
  isOpen(): boolean
  destroy(): void
}

interface Window {
  IssueCapture?: IssueCaptureAPI
}

Widget not loading or button not appearing

  • Check browser console for errors
  • Verify API key is correct and starts with "ic_"
  • Ensure NEXT_PUBLIC_ prefix is used in .env.local
  • Restart dev server after changing environment variables
  • Check that your domain is whitelisted in IssueCapture dashboard

CORS errors when submitting issues

Add your domain to the allowed domains list:

  • Go to IssueCapture dashboard → Widgets → Your Widget → Domains
  • Add localhost:3000 for development
  • Add your production domain (e.g., app.example.com)

React 19 compatibility issues

IssueCapture is fully compatible with React 19. If you encounter issues:

  • Ensure you're using Next.js 15 (not 14 with React 19 canary)
  • IssueCapture uses Shadow DOM (unaffected by React version)
  • Clear .next cache if seeing unexpected behavior: rm -rf .next

Turbopack build issues

IssueCapture works with Turbopack (Next.js 15 default). No special configuration needed.

  • Script strategy="afterInteractive" is Turbopack-compatible
  • If you prefer webpack: next dev --webpack (not recommended)

Advanced Configuration

Event Listeners

Listen to widget events:

window.IssueCapture.init({
  apiKey: process.env.NEXT_PUBLIC_ISSUECAPTURE_API_KEY!,
  trigger: 'auto',
  onOpen: () => {
    console.log('Widget opened')
    // Track analytics
  },
  onClose: () => {
    console.log('Widget closed')
  },
  onSubmit: (data) => {
    console.log('Issue submitted:', data)
    // Show success message
  },
  onError: (error) => {
    console.error('Submission failed:', error)
    // Show error message
  }
})

Dynamic Configuration

Change settings based on route or user:

// Different button positions per page
const buttonPosition = pathname.includes('/admin')
  ? 'top-right'
  : 'bottom-right'

window.IssueCapture.init({
  apiKey: process.env.NEXT_PUBLIC_ISSUECAPTURE_API_KEY!,
  trigger: 'auto',
  button: {
    position: buttonPosition
  }
})

Production Best Practices for Next.js 15

  • Use environment variables for API keys
  • Whitelist production domains in dashboard
  • Test on staging environment before deploying
  • Monitor bundle size impact (32KB gzipped)
  • Configure CSP headers if using strict Content Security Policy
  • Leverage Turbopack for faster builds (default in Next.js 15)

Ready to get started?

Sign up for a free account and start collecting bug reports in your Next.js 15 app in minutes.