All the parts

Build · Add powers

Realtime

How do I show live updates without the user refreshing?

What it is

Normally the browser asks and the server answers. For live updates the browser keeps a connection open, and the server pushes new data down it. Chat messages, live scores and cursors in a shared doc all work this way.

It is a phone call instead of mailing letters: the line stays open, so either side can speak at any moment.

Add it when Several users watch the same data change, such as chat, dashboards, bids or collaboration.

It can wait when Updating on a page refresh or every few seconds is good enough.

1 Which one should I pick?

Answer what you can. The ranking updates as you go, and you can ignore it if you disagree.

Where does your app run?
Who sends messages?
What matters most right now?

Most people start with

2 Side by side

Three pips is strong, one is weak. Pick a row to read about it below.

OptionQuick to set upCheap while smallEasy to leaveRoom to growPrice
Free tier, then pay per connection and message
Free tier, then bundled with the project plan
Free; you pay for the server it runs on
Free library; you pay for a server

3 Look closer, then wire it in

Managed service

Pusher or Ably

A hosted service that holds the open connections for you.

Pick it when You deploy on Vercel or similar and need live updates without running a server.

Skip it if You want zero extra vendors or very low cost at high message volume.

  • Works with serverless hosts
  • Handles reconnecting and scaling
  • A few lines to publish an event
  • Another bill that grows with connections
  • Your live features depend on them
Quick to set up
Cheap while small
Easy to leave
Room to grow

PriceFree tier, then pay per connection and message

How to connect it

  1. Create an app in the dashboard and copy the keys.
  2. Publish an event from your server when data changes.
  3. Subscribe to the channel in the browser with the public key.
  4. Use private channels so only allowed users can listen.
Put these in your .env file
  • PUSHER_APP_ID
  • PUSHER_KEY
  • PUSHER_SECRET
  • PUSHER_CLUSTER
publish.ts
import Pusher from "pusher";

const pusher = new Pusher({
  appId: process.env.PUSHER_APP_ID!,
  key: process.env.PUSHER_KEY!,
  secret: process.env.PUSHER_SECRET!,
  cluster: process.env.PUSHER_CLUSTER!,
});

await pusher.trigger("room-42", "new-message", { text: "Hello" });

4 Watch it work

Press play, or step through one message at a time.

Press Play to watch it run · 7 steps

Clients subscribe once. When something changes, the server publishes and every subscriber gets it.

A live update, step by stepClients subscribe once. When something changes, the server publishes and every subscriber gets it.Subscribe to room 42Subscribe to room 42Send message: Hi!Save it, check Alice is allowedPublish event to room 42New message: Hi!New message: Hi!
  • Client
  • Service
  • Pub/Sub
Text description
  1. Alice's browser → Realtime service: Subscribe to room 42
  2. Bob's browser → Realtime service: Subscribe to room 42
  3. Alice's browser → Your app: Send message: Hi!
  4. Your app: Save it, check Alice is allowed
  5. Your app → Realtime service: Publish event to room 42
  6. Realtime service replies to Alice's browser: New message: Hi!
  7. Realtime service replies to Bob's browser: New message: Hi!

5 Mistakes to skip

  • Trying to run WebSockets in a serverless function.

    Serverless functions end after each request. Use a managed realtime service or a separate always-on server.

  • Letting anyone subscribe to any channel.

    Use private channels or access rules, and check who the user is before they can listen.

  • Not handling dropped connections.

    Phones lose signal. Reconnect automatically and refetch what you missed.

See where it sits in a whole app

The stack builder draws your product with realtime in it and animates a request through it.

Open the stack builder

Works closely with