Using OpenReplay with Next.js

Getting the tracker to work on a Next.js application is quite straightforward. Since Next.js works on top of React, the process is quite similar, the main difficulty comes from the Server Side Rendering capabilities of Next.js.

But once you understand how they work and how to structure your client-only code, you’ll be tracking your users in no time.

While you can get a copy&paste version of the code directly from the platform when you create a new project, that code is too barebone and might not follow the best practices expected by your team or company.

This method provides minimalistic approach where we inject the tracker into the app as a component, which will load the library dynamically.

App Router (v14+) - use client

Section titled App Router (v14+) - use client
'use client'
import {useEffect} from "react"
import Tracker from '@openreplay/tracker'

const tracker = new Tracker({
  projectKey: "",
  ingestPoint: "",

const Openreplay = () => {
  useEffect(() => {
    if (typeof window !== 'undefined') {
  }, [])

  return null

export default Openreplay 

Regular component (Next up to v13)

Section titled Regular component (Next up to v13)

function TrackerComponent() {
const [tracker, setTracker] = React.useState(null);

  React.useEffect(() => {
    (async function () {
      const Tracker = await import("@openreplay/tracker");
      const openRelayTracker = new Tracker.default({
        projectKey: PROJECT_KEY,
        __DISABLE_SECURE_MODE: true,

  }, []);

this method lets you create a context, which in turn makes it easier to control the tracker state and call its various API.

import { createContext } from "react"
import {v4 as uuidV4} from 'uuid'
import { useReducer } from "react"

export const TrackerContext = createContext()

function defaultGetUserId() {
   return uuidV4() 

async function newTracker(config) {
    // we use dynamic import to make sure that tracker is started inside browser env
    const Tracker = await import("@openreplay/tracker");
    const getUserId = (config?.userIdEnabled && config?.getUserId) ? config.getUserId : defaultGetUserId
    let userId = null;

    const trackerConfig = {
        projectKey: config?.projectKey || process.env.NEXT_PUBLIC_OPENREPLAY_PROJECT_KEY

    const tracker = new Tracker(trackerConfig);
    if(config?.userIdEnabled) {
        userId = getUserId()
    return tracker


function reducer(state, action) {
        switch(action.type) {
            case 'init': {
                if(!state.tracker) {
                    console.log("Instantiaing the tracker for the first time...")
                    return {...state, tracker: newTracker(state.config)}
                return state
            case 'start': {
                console.log("Starting tracker...")
                return state

export default function TrackerProvider({children, config = {}}) {
    let [state, dispatch] = useReducer(reducer, {tracker: null, config})
    let value = {
        startTracking: () => dispatch({type: 'start'}),
        initTracker: () => dispatch({type: 'init'})

    return <TrackerContext.Provider value={value}>{children}</TrackerContext.Provider>

This provider makes 2 methods available to the user:

  • initTracker: which instantiates the tracker with the provided configuration.
  • startTracking: which triggers the start of the tracking process.

This provider also takes on a “configuration” object that you can extend for future features. This example shows you how to enable, optionally, the ability to uniquely identify users. You can use a default UUID which would be unique for every user every time they visit the page. You can alternatively provide a custom getUserId function as part of the configuration.

With your custom function, you could be providing the customer ID of your store, or the user’s email addressa or anything that uniquely identifies the user within your app. This way you can track their session replays across time in case they have a particular problem that others aren’t reporting.

💡A note for self-hosted users: if you’re using the self-hosted version of OpenReplay, you’ll also have to specify the ingestPoint configuration property. This property specifies the ingestion endpoint for the tracker’s data. Cloud users don’t need this, because by default the tracker will now where the SaaS version of this endpoint is, but if you’re self-hosting it, you’ll need to specify it (it should be something like

Handling the “projectKey” in your code

Section titled Handling the “projectKey” in your code

As a note, the projectKey that you configure is the one provided by OpenReplay’s platform. This value should not be stored hardcoded in your code for security reasons, but rather on some kind of configuration file/sytem.

For this example, I’m taking advantage of Next’s .env files. What you have to understand is that if you do not prefix the environmental variable with NEXT_PUBLIC your client-code (the tracker) will not have access to it.

If this is not acceptable, you can use getStaticProps from within the first page that your users visit to get that value prior to instantiating the tracker. You can see both examples below.

Import and wrap your application with the TrackerProvider we created before, like this:

import '../styles/globals.css'
import TrackerProvider from '../context/trackerContext'

function MyApp({ Component, pageProps }) {

  return <TrackerProvider>
        <Component {...pageProps} />

export default MyApp

This is the _app.js file, which acts as the entrypoint for the entire application.

And then, you can trigger the tracking from whatever component you wish, like so:

import { useContext, useEffect } from 'react'
import { TrackerContext } from '../context/trackerContext'
import styles from '../styles/Home.module.css'

export default function YourComponent({projectKey}) {
    const {initTracker, startTracking} = useContext(TrackerContext)
    useEffect( () => {
    }, [])
//.... rest of your code

If you don’t need as much control, you can join both, the “init message” and the “start tracking message”, so that you only need to call a single function to get the tracking going.

For the full code of a working Next-based application, you can check out this repository.

If you have any issues setting up the tracker on your Next-based project, please reach out to us on our Slack community and ask our devs directly!

SDKs to track and customize data

Section titled {title}