Web Development
Next.jsReal-timeWebSocketsSSESupabaseArchitectureScalability

Next.js Real-Time Features: WebSockets vs SSE vs Supabase Realtime (When to Use What)

AO
Adrijan Omićević
·17 min read

# The Current Landscape for Next.js Real Time in 2026#

"Next.js real time" usually means one of four product needs: chat, live dashboards, notifications, or collaborative editing. Each of those has different requirements for latency, fan-out, ordering, delivery guarantees, and security.

Next.js itself does not provide a single built-in real-time transport. You choose a transport and then fit it into Next.js constraints: serverless timeouts, edge runtime limitations, connection limits, and how you handle auth and multi-region delivery.

ℹ️ Note: This comparison focuses on three practical choices that ship today in production: WebSockets, Server-Sent Events (SSE), and Supabase Realtime. WebRTC can be relevant for audio and video, but it is a different problem space.

If you are unsure which runtime you are deploying to, start here: Next.js Edge Runtime vs Node.js Runtime on Vercel and Cloudflare. Runtime choices directly impact whether long-lived connections are even possible.

# Quick Comparison Table#

CriteriaWebSocketsSSESupabase Realtime
DirectionBidirectionalServer to client onlyBidirectional channels and database change feeds
TransportSingle TCP connection upgraded from HTTPStandard HTTP streamWebSockets to Supabase Realtime service
Browser supportExcellentExcellentExcellent
Fits serverless and edgeUsually no for hosting inside Next.jsSometimes, but often fragile on serverlessYes, because the long-lived connection is not hosted in your Next.js app
Scaling modelHarder, needs pub-sub and sticky sessionsEasier than WS for fan-out, still needs state handlingManaged scaling, depends on Supabase plan and architecture
Auth complexityMedium to highMediumMedium, integrates with Supabase Auth and JWT
Typical latencyLowLow to mediumLow to medium, depends on region and load
Best forChat, presence, multiplayer-like interactionsLive dashboards, notification feeds, progress updatesRapid development with Postgres-backed events and channels
Biggest pitfallHosting and horizontal scalingNot bidirectional and can be buffered by proxiesLock-in and cost surprises at scale

# What Actually Matters in Real-Time Systems#

Picking a protocol is not the first decision. The first decision is the interaction model.

Decision criteria that change the outcome#

QuestionWhy it mattersStrong signal to pick
Do clients need to send events frequently?If yes, server to client only streams are awkwardWebSockets or Supabase channels
Is your source of truth Postgres?If yes, database change feeds remove a lot of glue codeSupabase Realtime
How many concurrent connections?Costs and architecture explode with long-lived connectionsSSE for broadcast, managed realtime for speed
Do you need ordering and idempotency?Real-time without dedupe creates double updatesAny option, but must add event IDs and replay handling
Are you on Vercel serverless or edge?Long-lived connections may not be supportedSSE only if tested, or external WS provider
Do you need presence and typing indicators?Requires fast bidirectional messagesWebSockets or Supabase channels
Is multi-region required?Latency and cross-region sync become the main issueManaged services or global pub-sub

🎯 Key Takeaway: The best "Next.js real time" solution is the one that matches your interaction model and hosting constraints, not the one with the lowest latency on paper.

# Option 1: WebSockets in Next.js#

WebSockets provide a persistent, bidirectional connection. That makes them ideal for interactive workloads: chat, presence, collaborative cursors, and anything where the client is not only consuming updates but also sending small events frequently.

The hosting reality: where WebSockets actually live#

If you deploy Next.js to serverless functions, you generally should not host WebSocket servers inside your Next.js app. Serverless is designed for short-lived requests, not long-lived connections. Even when you can technically open a socket, you hit practical issues: cold starts, connection limits, timeouts, and no guarantee the same instance handles the same client later.

In practice, WebSockets for Next.js apps usually live in one of these places:

Hosting approachProsConsTypical use
Dedicated Node server (VPS, container, Kubernetes)Full control, predictable behaviorYou own scaling, patching, and uptimeSerious chat, presence, collaboration
Managed WS providerFast setup, scales wellVendor cost, vendor APIProducts needing predictable realtime without DevOps
"Hybrid" with Next.js for HTTP and separate WS serviceBest of both worldsMore moving partsMost production setups

Minimal WebSocket server example#

Keep the WebSocket server separate from Next.js if you are on serverless. This example uses ws on Node.

JavaScript
// server.js
import http from "http";
import { WebSocketServer } from "ws";
 
const server = http.createServer();
const wss = new WebSocketServer({ server });
 
wss.on("connection", (socket) => {
  socket.on("message", (raw) => {
    const msg = raw.toString();
    wss.clients.forEach((client) => client.send(msg));
  });
});
 
server.listen(8080);

This is intentionally simple. A production version needs authentication, per-room routing, message validation, and rate limiting.

Scaling WebSockets: the hidden work#

A single WebSocket process works until it does not. The common inflection point is when you need horizontal scaling. At that point, you need a shared pub-sub layer so clients connected to different instances still receive the same events.

Typical architecture:

  • WebSocket nodes handle connections.
  • A pub-sub system fans out events: Redis PubSub, Redis Streams, NATS, Kafka, or managed equivalents.
  • A shared store holds presence and room membership, or you implement consistent hashing.

The reason this matters: chat might be 1 message per second per active user, but presence and typing often multiply your event volume. A team chat with 5,000 concurrent users can easily generate tens of thousands of small events per minute when you include typing, read receipts, presence, and retries.

Auth for WebSockets in Next.js#

The safest pattern is to issue a short-lived token from Next.js over HTTPS, then validate it during the WebSocket handshake. Use the same provider you use for your web sessions.

If you need a practical overview of auth choices in Next.js, including Supabase and other common options, use: Next.js authentication guide: NextAuth vs Clerk vs Supabase.

⚠️ Warning: Do not trust "roomId" or "userId" sent by the client over WebSocket messages. Treat every message like an API request: validate auth, authorize access to the room, and enforce rate limits.

# Option 2: Server-Sent Events (SSE) in Next.js#

SSE uses a standard HTTP response that stays open and streams events. The client receives events, but it cannot send events back on the same connection. That makes SSE excellent for "push-only" needs: dashboards, notification feeds, job progress, and streaming logs.

Why SSE often wins for dashboards and notifications#

SSE is simple:

  • Works over HTTP and is proxy-friendly.
  • Automatic reconnection is built into the browser EventSource API.
  • One-way nature reduces complexity and abuse surface area.

For many products, dashboards are read-heavy. If you have 10,000 dashboard viewers and only a handful of writers, SSE is often cheaper and easier than WebSockets because your servers do not need to track bidirectional state.

SSE caveats that matter in production#

SSE is still a long-lived connection. That means your hosting platform must support streaming responses reliably. Some serverless environments buffer responses, which breaks real-time streaming. You must test on your target platform.

If you are deploying to edge runtimes, confirm streaming support and timeouts. The runtime implications are covered here: Next.js Edge Runtime vs Node.js Runtime on Vercel and Cloudflare.

SSE route handler example in Next.js#

This example shows a basic SSE endpoint. It does not include a real event source, but it demonstrates correct headers and periodic heartbeats.

JavaScript
// app/api/events/route.js
export async function GET() {
  const encoder = new TextEncoder();
 
  const stream = new ReadableStream({
    start(controller) {
      const send = (event, data) => {
        controller.enqueue(encoder.encode(`event: ${event}\n`));
        controller.enqueue(encoder.encode(`data: ${data}\n\n`));
      };
 
      send("connected", "ok");
 
      const interval = setInterval(() => {
        send("heartbeat", String(Date.now()));
      }, 15000);
 
      // No clean shutdown shown here for brevity
    },
  });
 
  return new Response(stream, {
    headers: {
      "Content-Type": "text/event-stream",
      "Cache-Control": "no-cache, no-transform",
      Connection: "keep-alive",
    },
  });
}

Client side:

JavaScript
const es = new EventSource("/api/events");
es.addEventListener("heartbeat", (e) => console.log(e.data));

Scaling SSE#

SSE scales well for broadcast-style updates, but you still need an event source that can fan out. The common patterns:

  • Poll your database and stream deltas to clients.
  • Consume from a queue or pub-sub and stream to connected clients.
  • Use a managed pub-sub and attach SSE as the delivery mechanism.

SSE becomes awkward when you need high-frequency upstream messages from clients. You can combine SSE plus regular HTTPS POST requests for client actions, but at that point WebSockets may be simpler.

💡 Tip: For dashboards, reduce event volume by sending aggregated updates every 250 to 1000 milliseconds, not per-row changes. Users perceive "real time" as anything under ~1 second for most business dashboards, and you save significant compute and bandwidth.

# Option 3: Supabase Realtime with Next.js#

Supabase Realtime gives you two main real-time capabilities:

  1. 1
    Postgres change feeds for insert, update, delete events.
  2. 2
    Realtime channels for broadcasting messages, presence, and state sync.

The big advantage for Next.js is hosting: the long-lived WebSocket connection is between the browser and Supabase, not between the browser and your Next.js deployment. That avoids the biggest operational pain point of WebSockets on serverless platforms.

Where Supabase Realtime fits best#

Supabase Realtime is a strong fit when:

  • Your source of truth is Postgres.
  • You want "database-driven" real-time updates without building a pub-sub layer.
  • You need auth integrated with database permissions via Row Level Security.
  • You want to ship quickly and accept some vendor coupling.

It is especially effective for internal tools, SaaS admin panels, and early-stage products where time-to-market matters more than custom scaling architecture.

Supabase Realtime subscription example#

This example subscribes to Postgres changes for a table. Keep authorization in mind: RLS policies still apply.

JavaScript
// subscribe.js
import { createClient } from "@supabase/supabase-js";
 
const supabase = createClient(
  process.env.NEXT_PUBLIC_SUPABASE_URL,
  process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY
);
 
const channel = supabase
  .channel("orders-feed")
  .on(
    "postgres_changes",
    { event: "*", schema: "public", table: "orders" },
    (payload) => console.log(payload)
  )
  .subscribe();

For chat-like features, channels are often a better fit than raw database change feeds, because chat needs message validation, per-room access checks, and sometimes message transformations.

Auth and authorization with Supabase Realtime#

Supabase’s real advantage is consistent auth across:

  • Client SDK session handling
  • JWT claims
  • Postgres Row Level Security policies

That said, you still need to design authorization carefully. A common mistake is subscribing to broad table change feeds and filtering on the client. The correct approach is to enforce access at the database level using RLS and query patterns that scope data per tenant and per user.

If you need to compare auth approaches in Next.js before committing to Supabase, use: Next.js authentication guide: NextAuth vs Clerk vs Supabase.

Cost and scaling considerations#

Supabase Realtime cost is usually not the first cost you hit. The bigger drivers are:

  • Number of connected clients
  • Message rate and payload size
  • Database workload caused by writes and triggers
  • Egress bandwidth

If your product becomes event-heavy, you need to measure and budget based on actual usage. For example, presence updates every 2 seconds for 5,000 users is 2,500 updates per second if you model presence naïvely. Even if each update is small, you pay in bandwidth and processing.

# Feature-by-Feature Trade-Offs That Decide the Winner#

This is where most teams make the real decision: hosting, scalability, auth, and cost are not abstract, they become day-two problems.

Hosting constraints#

ConstraintWebSocketsSSESupabase Realtime
Works on typical serverless Next.js hostingUsually noSometimes, must testYes
Works across proxies and CDNsSometimes trickyUsually yesUsually yes
Operational burdenHigh if self-hostedMediumLow to medium

If you cannot run long-lived connections in your Next.js environment, WebSockets and SSE inside Next.js are the wrong starting point. That pushes you toward managed realtime or a separate realtime service.

Scalability and fan-out#

Scaling concernWebSocketsSSESupabase Realtime
Horizontal scalingNeeds pub-subNeeds pub-subManaged
Broadcast to many usersGood with pub-subVery goodGood, depends on plan and patterns
Presence and per-room stateCustom codeNot idealBuilt-in primitives via channels

Authentication and access control#

Auth requirementWebSocketsSSESupabase Realtime
Use existing Next.js session cookiesPossible, but handshake needs carePossibleUsually separate session using Supabase client
Per-room authorizationCustomCustomChannel policies and RLS-backed data patterns
Multi-tenant securityPossible, but you must build itPossible, but you must build itStrong if RLS is designed correctly

Cost model and common surprises#

Cost driverWebSocketsSSESupabase Realtime
Idle connectionsYou pay for memory and connection slotsSimilarPaid indirectly via plan limits and bandwidth
Fan-outPub-sub infra and networkPub-sub and networkManaged, but still network and plan limits
Engineering costHighestMediumLowest initially

# Decision Criteria by Use Case#

This section maps product needs to a concrete choice. These are not theoretical recommendations; they reflect common production patterns and failure modes.

Chat apps#

Chat is bidirectional. You need message send, delivery acknowledgements, typing indicators, presence, read receipts, and moderation workflows.

RequirementRecommended optionWhy
Bidirectional messagesWebSockets or Supabase channelsSSE alone is not enough
Presence and typingWebSockets or Supabase channelsRequires frequent low-latency upstream events
Large scale roomsWebSockets with pub-subYou need control over fan-out and throttling
Fast MVP with PostgresSupabase RealtimeLess glue code, fast iteration

If your Next.js app is on serverless hosting, chat almost always ends up as a separate realtime service. Supabase is a good option if your data model fits Postgres and you accept managed constraints.

Live dashboards#

Dashboards are mostly server to client. They benefit from simple broadcast updates and resilient reconnection.

RequirementRecommended optionWhy
Live KPIs and chartsSSESimple, reliable, low overhead
Updates from DB writesSupabase change feeds or SSE fed by queueDepends on whether you want DB-driven realtime
Strict low latency under 200 msWebSockets or managed realtimeSSE can still be fast, but buffering can happen

For dashboards, you typically get better ROI by reducing event volume than by shaving latency. The real metric is user-perceived freshness, not protocol choice.

Notifications#

Notifications are often bursty and do not require strict ordering. They also frequently need offline support, which points to queues and persistence.

RequirementRecommended optionWhy
In-app notification streamSSEGreat fit for server push
Cross-device and offlineSSE plus persistenceStore notifications and replay on reconnect
Mobile push integrationSeparate push systemWeb push and APNs are separate from realtime transports

A common pattern is: persist notifications to DB, stream new notifications in real time, and on reconnect fetch missed ones using Last-Event-ID or a timestamp.

Collaborative editing#

Collaboration is the hardest. You need concurrency control and conflict resolution, not just transport.

RequirementRecommended optionWhy
Cursor and presenceWebSockets or Supabase channelsHigh-frequency state sync
Document updates with conflict resolutionWebSockets plus CRDT or OTTransport is the easy part
MVP with low concurrencySupabase channelsGood enough before you hit hard scaling needs

⚠️ Warning: Collaborative editing fails when teams treat it as "real-time messaging." You need a data model for merges and conflicts. Without CRDT or OT, users will overwrite each other even if your transport is perfect.

# Observability: How to Keep Next.js Real Time Stable#

Real-time systems are sensitive to small issues: reconnect storms, message amplification, and silent disconnects. If you do not measure, you will only see the problem when users complain.

Track these minimum metrics:

MetricWhy it mattersTypical symptom
Concurrent connectionsCapacity and cost planningSudden spikes during releases
Reconnect rateDetect instabilityConnection flapping or proxy timeouts
Message rate per userAbuse and runaway clientsCosts grow linearly, UX degrades
Event delivery lagReal-time freshnessDashboards show stale values
Error rates per channel or roomPermissions and data issuesUsers missing updates

Instrument both server and client. Client-side logs are often the only way to understand mobile network behavior.

A practical baseline for logs, metrics, and tracing is here: Web app observability guide: logging, metrics, tracing.

💡 Tip: Add a unique eventId to every event and store the last processed eventId in memory. It prevents double-applying updates after reconnect, which is one of the most common real-time bugs in production.

# Our Practical Recommendation#

If your goal is to ship "Next.js real time" features quickly with minimal DevOps, Supabase Realtime is hard to beat, especially for Postgres-backed dashboards and early-stage chat. You get managed WebSockets, integrated auth, and database-driven eventing without building a pub-sub layer.

If you need deep control, predictable scaling under heavy bidirectional traffic, or you are building collaboration at scale, you will eventually outgrow simplistic setups. WebSockets with a dedicated realtime service and a proper pub-sub backbone becomes the long-term architecture.

If your use case is mostly broadcast updates, SSE is the simplest and often the most reliable. It is also easier to secure and observe because the server is the only writer on the stream.

# Key Takeaways#

  • Choose based on interaction model: bidirectional needs point to WebSockets or Supabase channels, broadcast-only feeds often fit SSE.
  • Do not host WebSockets inside serverless Next.js route handlers for production; run a separate realtime service or use a managed provider.
  • Use Supabase Realtime when Postgres is your source of truth and you want faster delivery with integrated auth and RLS.
  • For dashboards, optimize event frequency and aggregation first; users rarely need per-row updates faster than 1 second.
  • Invest in observability early: track concurrent connections, reconnect rate, message rate, and delivery lag to prevent silent failures.

# Conclusion#

WebSockets, SSE, and Supabase Realtime can all power Next.js real time features, but they solve different product problems and come with different operational costs. Pick the simplest option that meets your interaction model and hosting constraints, then add the missing pieces: authorization, replay and idempotency, and observability.

If you want help choosing the right architecture or implementing a production-grade realtime layer in Next.js, contact Samioda and we will review your use case, hosting setup, and scaling goals, then propose an implementation plan you can ship confidently.

FAQ

Share
A
Adrijan OmićevićFounder & Senior Developer

Founder & Senior Developer at Samioda. 8+ years building React, Next.js, Flutter and n8n automation solutions for clients across Europe.

Need help with your project?

We build custom solutions using the technologies discussed in this article. Senior team, fixed prices.