---
title: "Add Realtime to a Next.js App with NoLag"
description: A step-by-step guide to adding realtime messaging to a Next.js app with NoLag, including a server route that mints tokens so your API key never reaches the browser.
excerpt: "NoLag runs in the browser over WebSocket, so it lives in client components. Here is the clean pattern for Next.js, including keeping your API key on the server."
date: '2026-07-30'
readTime: 7 min read
category: Integration
---

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

```bash
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` stays in your server environment.

```ts
// 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: 'device' }),
  })

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

For production, prefer short-lived [client tokens](/docs/agents) minted from your project signing keys so browser sessions get scoped, expiring credentials.

## 3. Connect from a client component

```tsx
'use client'
import { useEffect, useState } from 'react'
import { NoLag } from '@nolag/js-sdk'

export default function Chat() {
  const [messages, setMessages] = useState<any[]>([])

  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('chat').setRoom('general')
      room.subscribe('message')
      room.on('message', (data) => setMessages((m) => [...m, data]))
    })()

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

  const send = () => {
    // room.emit('message', { text: 'Hello!' })
  }

  return (
    <ul>
      {messages.map((m, i) => (
        <li key={i}>{m.text}</li>
      ))}
    </ul>
  )
}
```

The `active` flag guards against React running effects twice in development and against a connection resolving after the component unmounts.

## Why the client-component rule matters

The NoLag SDK opens a WebSocket, which only exists in the browser. Importing and connecting it in a server component or during SSR will fail. Keep the `NoLag(...)` call inside `useEffect` (or an event handler) in a `'use client'` file and you avoid the whole class of hydration and "window is not defined" errors.

## Next steps

- Read the [5-minute quick start](/docs/getting-started) for the full connect, subscribe, and publish flow.
- Skip the plumbing with the [chat blueprint](/docs/high-level-sdks), which wraps rooms, typing, and history.
- Building agent features into your app? See the [AI agents](/docs/agents) guide.
