Change in `realtime-js` affecting Node.js < 22

Aug 12, 2025

What changed#

Using realtime-js library in Node.js < 22 will require to set transport for Realtime

What do I need to do#

For most users (Browser, Node.js 22+): No changes required For Node.js < 22 users: You’ll need to make a small change to explicitly set the WebSocket transport (see line 9 below).


_10
npm i ws


_18
import "dotenv/config";
_18
import { createClient, SupabaseClient } from "@supabase/supabase-js";
_18
import express, { Application } from "express";
_18
import ws from "ws";
_18
_18
const supabaseUrl = process.env.SUPABASE_URL!;
_18
const supabaseAnonKey = process.env.SUPABASE_ANON_KEY!;
_18
const supabase: SupabaseClient = createClient(supabaseUrl, supabaseAnonKey, {
_18
// Explicitly set the WebSocket transport here
_18
realtime: { transport: ws as any },
_18
});
_18
_18
const startRealtime = () =>
_18
supabase.channel("realtime:server").subscribe(console.log);
_18
_18
const app: Application = express();
_18
startRealtime();
_18
app.listen(3000);

[!WARNING]
We are working on a fix for the type definition as currently it's not being accepted as a WebSocketLike interface.

Why did we change it#

We have been facing multiple issues where the dynamic import of ws was breaking several runtimes and environments. The first environment where this become a serious issue was with Expo which required us to tackle the issue.

Previous attempts#

Multiple NPM entrypoints#

PR: https://github.com/supabase/realtime-js/pull/476

First approach chosen in conjunction with the Expo team but ended up creating several issues in other runtimes (namely Deno and Browser) so we reverted to use another method

Polymorphic client#

PR: https://github.com/supabase/realtime-js/pull/485

Second approach chosen as it's usually the "default" as we accepted the overhead of having a polymorphic WS connector but this created issues with Vercel ( https://github.com/supabase/supabase-js/issues/1437 )

Current approach#

After our several attempts in handling how Javascript runtimes handle dynamic imports we had to change the way we support Node.js < 22 as it was the original source of the issue due to the lack of native WebSocket support

The changes are implemented in realtime-js@2.15.1 with the changes from https://github.com/supabase/realtime-js/pull/514 The changes are implemented in supabase-js@2.55.0 with the changes from https://github.com/supabase/supabase-js/pull/1529

We opted to use this approach as it fully prevents the usage of dynamic imports; it avoids external library issues; has a path for upgradability and avoids multiple entrypoints which proven to be error prone.

Build in a weekend, scale to millions