Skip to content
Josselin Guillozet edited this page Oct 19, 2026 · 2 revisions

Integrating the iProov Web SDK with Next.js requires a dynamic import because the SDK accesses browser APIs (window, document) that are not available during server-side rendering.

ℹ️ Supported versions. We support any Next.js release still receiving security patches — that is Active or Maintenance LTS under the Next.js support policy, which at the time of writing is v15 and above. This guide uses the Pages Router.

The same dynamic import pattern works in the App Router, but the component must be a Client Component — add "use client" at the top of the file, because useEffect and useState are not available in a Server Component. Reaching for next/dynamic with ssr: false instead will not help: Next.js rejects that option in Server Components.

Integration

The key difference from a standard React integration is that @iproov/web-sdk must be imported dynamically inside a useEffect hook (or equivalent client-side lifecycle), not at the top of the file.

pages/index.tsx

import Head from "next/head"
import { useEffect, useState } from "react"

function IProovMe() {
  const [token, setToken] = useState(null)
  const [isLoading, setLoading] = useState(true)

  useEffect(() => {
    const fetchIproovMe = async () => {
      // Dynamic import is required because Next.js renders on the server first,
      // and the iProov SDK requires browser APIs (window, document) to initialise.
      return await import("@iproov/web-sdk")
    }

    const fetchToken = async () => {
      await fetchIproovMe()

      // Replace with your backend token endpoint
      const response = await fetch("***YOUR_BACKEND_TOKEN_ENDPOINT***", {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({
          resource: "https://iproov.app",
          user_id: "example@example.com",
          client: "Web SDK NextJS",
        }),
      })
      const data = await response.json()
      setToken(data.token)
      setLoading(false)
    }

    fetchToken().catch((err) => {
      setLoading(false)
      console.error(err)
    })
  }, [])

  if (isLoading) return <p>Loading...</p>
  if (!token) return <p>Error loading token</p>

  return (
    <iproov-me
      token={token}
      base_url="***YOUR_BASE_URL***"
      assets_url="***YOUR_ASSETS_URL***"
      aria_live="assertive"
    >
      <div slot="ready">
        <div>Ready to verify</div>
      </div>
    </iproov-me>
  )
}

export default function Home() {
  return (
    <>
      <Head>
        <title>iProov Web SDK - Next.js</title>
        <meta name="viewport" content="width=device-width, initial-scale=1" />
      </Head>
      <main>
        <IProovMe />
      </main>
    </>
  )
}

Why Dynamic Import?

Next.js pre-renders pages on the server by default. The iProov Web SDK registers a custom element (<iproov-me>) and accesses browser-only APIs during initialisation. A static import "@iproov/web-sdk" at the top of the file would execute during SSR and fail.

By using await import("@iproov/web-sdk") inside useEffect, the SDK is only loaded client-side after the component has mounted.

Support Checker

The Support Checker has its own subpath, so it can be loaded without pulling in the component at all — which is the point of it. Import it dynamically for the same SSR reason:

useEffect(() => {
  const init = async () => {
    const { iProovSupport } = await import("@iproov/web-sdk/iProovSupport.js")
    const supportChecker = new iProovSupport()
    const { supported, granted } = await supportChecker.check()
    console.log("Supported:", supported, "Granted:", granted)
  }
  init()
}, [])

⚠️ From 7.0.0 the window.IProov global has been removed, so new window.IProov.IProovSupport() no longer works. The .js extension on the subpath is required, because exports subpaths are matched exactly.

See the full Support Checker documentation.

Custom Slots

Pass custom slots as children of the <iproov-me> component:

<iproov-me token={token} base_url="***YOUR_BASE_URL***" assets_url="***YOUR_ASSETS_URL***">
  <div slot="ready">Ready to scan</div>
  <div slot="button">Start scanning</div>
  <div slot="passed">Verification passed!</div>
  <div slot="failed">Verification failed</div>
</iproov-me>

Next.js Configuration

next.config.js

const nextConfig = {
  reactStrictMode: true,
}

module.exports = nextConfig

No special Next.js configuration is required beyond the standard setup. The dynamic import handles the SSR/client boundary.

Assets

Set the assets_url attribute to the URL where your application serves the iProov SDK assets. For Next.js, copy the required assets from node_modules/@iproov/web-sdk into your public/ directory and set assets_url to your application's origin. See the Serving Assets and CORS guide for details.

HTTPS in Development

The iProov SDK requires HTTPS for camera access. Next.js generates a locally trusted certificate for you:

next dev --experimental-https

The dev server then serves https://localhost:3000. To use your own certificate instead, pass --experimental-https-key and --experimental-https-cert.

Clone this wiki locally