← Back to blog
INTEGRATION7 min read

Add Realtime to a Next.js App with NoLag

HB
Henco Burger
July 30, 2026

Next.js renders on the server and hydrates in the browser, so the one rule for adding realtime is simple: the realtime connection belongs in a client component. This guide shows the clean pattern, including a server route that hands the browser a token so your project key never ships to the client.

1. Install the SDK

npm install @nolag/js-sdk

2. Mint a token on the server

Never put a project key in browser code. Add a route handler that creates a token server-side and returns it. The NOLAG_API_KEY (an nlg_live_... key) stays in your server environment.

// app/api/nolag-token/route.ts
import { NextResponse } from 'next/server'

export async function GET() {
  const res = await fetch('https://api.nolag.app/v1/actors', {
    method: 'POST',
    headers: {
      Authorization: `Bearer ${process.env.NOLAG_API_KEY}`,
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({ name: 'Web Client', actorType: 'user' }),
  })

  const actor = await res.json()
  return NextResponse.json({ token: actor.accessToken })
}

For production, prefer short-lived client tokens minted from your project signing keys so browser sessions get scoped, expiring credentials.

3. Connect from a client component

'use client'
import { useEffect, useRef, useState } from 'react'
import { NoLag } from '@nolag/js-sdk'
import type { RoomContext } from '@nolag/js-sdk'

// Create the app and its `general` room first. The slug you get back has a
// random suffix (for example `chat-a3f9`), and that suffixed slug is what
// setApp() takes.
const APP_SLUG = process.env.NEXT_PUBLIC_NOLAG_APP_SLUG!

type Message = { text: string }

export default function Chat() {
  const [messages, setMessages] = useState([] as Message[])
  const roomRef = useRef(null as RoomContext | null)

  useEffect(() => {
    let client: ReturnType<typeof NoLag> | undefined
    let active = true

    ;(async () => {
      const { token } = await fetch('/api/nolag-token').then((r) => r.json())
      if (!active) return

      client = NoLag(token)
      await client.connect()

      const room = client.setApp(APP_SLUG).setRoom('general')
      room.subscribe('message')
      room.on<Message>('message', (data) => setMessages((m) => [...m, data]))
      roomRef.current = room
    })()

    return () => {
      active = false
      roomRef.current = null
      client?.disconnect()
    }
  }, [])

  const send = (text: string) => {
    const msg: Message = { text }
    roomRef.current?.emit('message', msg)
    // Publishers never receive their own messages, so append it locally
    setMessages((m) => [...m, msg])
  }

  return (
    <div>
      <ul>
        {messages.map((m, i) => (
          <li key={i}>{m.text}</li>
        ))}
      </ul>
      <button onClick={() => send('Hello!')}>Send</button>
    </div>
  )
}

The active flag guards against React running effects twice in development and against a connection resolving after the component unmounts. The send function appends the message to local state as well as emitting it: NoLag never delivers a message back to the actor that published it, so the sender's own text would otherwise never appear on screen.

Why the client-component rule matters

The NoLag SDK runs in Node as well as in the browser, so this is not about where the code can execute. It is about lifecycle. A server component renders once, has no mount or unmount, and its output is serialised and hydrated in the browser, so there is nowhere for a long-lived connection to live and nothing it receives would survive hydration. Keep the NoLag(...) call inside useEffect (or an event handler) in a 'use client' file and the connection opens after hydration and closes on unmount, which avoids hydration mismatches and duplicated connections.

Next steps

  • Read the 5-minute quick start for the full connect, subscribe, and publish flow.
  • Skip the plumbing with the chat blueprint, which wraps rooms, typing, and presence.
  • Building agent features into your app? See the AI agents guide.