Browse documentation

Realtime with Redis

Turn on live list updates across multiple Next.js instances with the Redis SSE driver — one config field.

flowpanel pushes live updates to open browsers over Server-Sent Events. On a single instance the default memory driver works out of the box. The moment you run more than one Next.js instance, you need the redis driver so an event raised on one instance reaches a browser connected to another.

For the full production checklist (proxy buffering, TLS, LB idle timeouts, rolling-deploy hygiene), see Realtime in production.

1. Enable realtime on a resource

Opt a resource into live updates with realtime: true. Lists for that resource refresh automatically when a row is created, updated, or deleted:

import { resource } from "@flowpanel/kit";
import * as schema from "@/db/schema";

resource(schema.orders, {
  columns: ["id", "status", "total"],
  realtime: true,
});

2. Point the publisher at Redis

realtime takes the publisher options directly. Switch the driver to redis and pass your connection URL:

import { defineAdmin } from "@flowpanel/kit";

export default defineAdmin({
  // …adapter, auth, resources…
  realtime: {
    driver: "redis",
    url: process.env.REDIS_URL!,
    // Optional: namespaces channels so multiple admins can share one Redis.
    // Defaults to "flowpanel".
    keyPrefix: "fp",
  },
});

That's the entire switch. Every resource.realtime: true and every publishResource(...) call now fans out over Redis pub/sub instead of an in-process emitter. The app/api/flowpanel/stream/route.ts SSE endpoint — generated by flowpanel init — is unchanged.

flowpanel publishes only the resource name + action over Redis, never row contents. Browsers receive the ping and re-fetch the updated rows over HTTPS, so Redis is the notification channel, not the source of truth.

3. Publish custom events (optional)

For dashboards or background jobs, push your own resource event from a Server Action or route handler:

import { publishResource } from "@flowpanel/kit/next";

await db.update(schema.orders).set({ status: "shipped" }).where(/* … */);
await publishResource("orders", { action: "update", id: orderId });

Any list or widget subscribed to orders refreshes the moment the event lands.

When the memory driver is enough

  • next dev
  • A single-container / single-instance deployment
  • Previews where cross-instance fan-out doesn't matter

In those cases leave realtime as { driver: "memory" } (or omit it — memory is the default).