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).