← Back to articles

How to Add Real-Time Features to Your App (2026)

Users expect live updates — chat messages, notifications, collaborative editing, live dashboards. Here's how to add real-time features to your app without overcomplicating your architecture.

The Options

ApproachBest ForComplexity
Server-Sent Events (SSE)One-way updates (notifications, feeds)Low
WebSocketsTwo-way communication (chat, gaming)Medium
LiveblocksCollaborative features (cursors, editing)Low
Pusher/AblyManaged real-time infrastructureLow
ConvexReal-time database (auto-updates)Low
PartyKitCustom real-time logic on the edgeMedium

Option 1: Server-Sent Events (Simplest)

For one-way updates (server → client). No library needed.

Server (Next.js API Route)

// app/api/notifications/route.ts
export async function GET() {
  const encoder = new TextEncoder()
  const stream = new ReadableStream({
    start(controller) {
      const interval = setInterval(() => {
        const data = JSON.stringify({ type: 'notification', message: 'New update!' })
        controller.enqueue(encoder.encode(`data: ${data}\n\n`))
      }, 5000)

      // Cleanup on close
      return () => clearInterval(interval)
    },
  })

  return new Response(stream, {
    headers: {
      'Content-Type': 'text/event-stream',
      'Cache-Control': 'no-cache',
      Connection: 'keep-alive',
    },
  })
}

Client

useEffect(() => {
  const source = new EventSource('/api/notifications')
  source.onmessage = (event) => {
    const data = JSON.parse(event.data)
    setNotifications(prev => [data, ...prev])
  }
  return () => source.close()
}, [])

Best for: Live notifications, activity feeds, dashboard updates.

Option 2: WebSockets (Two-Way)

For bidirectional communication. Use Socket.io or native WebSockets.

npm install socket.io socket.io-client

Server

import { Server } from 'socket.io'

const io = new Server(3001, { cors: { origin: '*' } })

io.on('connection', (socket) => {
  console.log('User connected:', socket.id)

  socket.on('chat:message', (message) => {
    io.emit('chat:message', { ...message, timestamp: Date.now() })
  })

  socket.on('disconnect', () => {
    console.log('User disconnected:', socket.id)
  })
})

Client

import { io } from 'socket.io-client'

const socket = io('http://localhost:3001')

socket.on('chat:message', (message) => {
  setMessages(prev => [...prev, message])
})

const sendMessage = (text: string) => {
  socket.emit('chat:message', { text, userId })
}

Best for: Chat, gaming, live collaboration.

Limitation: WebSockets don't work on serverless (Vercel Functions). Need a persistent server (Fly.io, Railway) or use a managed service.

Option 3: Liveblocks (Collaboration)

For multiplayer features — cursors, presence, collaborative editing.

npm install @liveblocks/client @liveblocks/react
// liveblocks.config.ts
import { createClient } from '@liveblocks/client'
import { createRoomContext } from '@liveblocks/react'

const client = createClient({ publicApiKey: 'pk_...' })

export const { RoomProvider, useOthers, useMyPresence, useStorage } =
  createRoomContext(client)

Live Cursors

function Cursors() {
  const others = useOthers()

  return others.map(({ connectionId, presence }) => (
    <Cursor
      key={connectionId}
      x={presence.cursor?.x}
      y={presence.cursor?.y}
      color={presence.color}
    />
  ))
}

Collaborative State

function TodoList() {
  const todos = useStorage((root) => root.todos)
  const addTodo = useMutation(({ storage }, text) => {
    storage.get('todos').push({ text, done: false })
  }, [])

  // todos auto-syncs across all users in real-time
}

Best for: Figma-like collaboration, shared whiteboards, multiplayer editing.

Option 4: Pusher (Managed WebSockets)

Works on serverless. No persistent server needed.

npm install pusher pusher-js

Server

import Pusher from 'pusher'

const pusher = new Pusher({
  appId: '...', key: '...', secret: '...', cluster: 'us2',
})

// Trigger an event
await pusher.trigger('chat-room', 'new-message', {
  text: 'Hello!',
  userId: '123',
})

Client

import Pusher from 'pusher-js'

const pusher = new Pusher('key', { cluster: 'us2' })
const channel = pusher.subscribe('chat-room')

channel.bind('new-message', (data) => {
  setMessages(prev => [...prev, data])
})

Pricing: Free: 200K messages/day. Paid: from $49/month.

Best for: Adding real-time to serverless apps without managing infrastructure.

Option 5: Convex (Real-Time Database)

The database itself is real-time. Queries automatically update when data changes.

// convex/messages.ts
export const list = query(async ({ db }) => {
  return await db.query('messages').order('desc').take(50)
})

// React component — auto-updates when messages change
function Chat() {
  const messages = useQuery(api.messages.list)
  // messages automatically updates in real-time!
}

Best for: Apps where real-time is core (chat, dashboards, collaboration).

Decision Framework

Need: Live notifications only?
→ Server-Sent Events (zero dependencies)

Need: Chat or bidirectional communication?
→ Pusher (serverless) or WebSockets (traditional server)

Need: Collaborative editing (cursors, shared state)?
→ Liveblocks

Need: Real-time database (everything updates live)?
→ Convex

Need: Custom real-time logic at the edge?
→ PartyKit

FAQ

Can I use WebSockets on Vercel?

Not in serverless functions (they terminate). Use Pusher, Ably, or Liveblocks for managed real-time on Vercel. Or deploy WebSocket servers on Fly.io/Railway.

How many concurrent connections can I handle?

SSE/WebSockets: depends on your server (typically 10K-100K per instance). Managed services (Pusher, Liveblocks): handle scaling for you.

What about Supabase Realtime?

Supabase has built-in real-time via Postgres changes. Good if you're already using Supabase. Limited compared to dedicated real-time tools.

Do I need real-time?

Maybe not. Polling every 5-10 seconds works for many use cases (dashboards, feeds) and is much simpler. Add real-time only when users expect instant updates.

Bottom Line

Start with the simplest option. SSE for one-way updates. Pusher for serverless two-way. Liveblocks for collaboration. Convex for real-time-first apps. Don't over-engineer — polling is fine for most use cases. Add real-time when users notice the delay.

Get AI tool guides in your inbox

Weekly deep-dives on the best AI coding tools, automation platforms, and productivity software.