Oliver White
16 May 2026 · 7 min read
The contest page renders vote counts on the server — a Server Component fetches artworks and their current vote totals at request time. Then a Client Component takes over and subscribes to Supabase Realtime so the counts update live as other users vote. The problem is the gap between those two moments: if the Client Component fetches its own initial data, there is a window where it shows zero before the real count arrives.
This is part of the Directed Output build log. The full methodology lives at /process.
The intuitive approach when building a real-time counter is to fetch the initial count in a useEffect and then subscribe to updates. This is the pattern most Supabase Realtime examples show. It has a visible flaw: the component renders with whatever initial state you provide (usually 0 or undefined), then the useEffect fires after hydration, the fetch resolves, and the count updates. On a slow connection this flash is obvious. Even on a fast connection it introduces an unnecessary network request.
Fetch in useEffect
initialCount prop from SSR
The Server Component fetches artworks and passes vote_count as a prop to the Client Component. The Client Component uses that value as the useState initial value. The Realtime subscription only updates the state when a new vote arrives — it never needs to fetch the current count because it already has it.
Create the Supabase browser client inside useEffect, never at module level. The browser client calls createBrowserClient which accesses environment variables that only exist in the browser context. Creating it at module level causes SSR errors during build.
Without the filter clause, a Supabase Realtime subscription on the artworks table receives every UPDATE to every artwork row in the database. With filter: id=eq.artworkId, the subscription only fires when the specific artwork this component is displaying gets updated. On a contest page with 6 artworks, that is 6 filtered subscriptions rather than 6 unfiltered subscriptions each receiving all 6 updates on every vote.
Realtime events per vote
1 per component
with filter — targeted
Without filter
6x per vote
every component gets every update
Network messages saved
83%
on a 6-artwork contest
Initial render flash
0
count correct from first paint
Built with this methodology
A Supabase Realtime subscription updates vote counts live. The initial count comes from SSR. Get the handoff wrong and users see a flash of zero. Get it right and there is no visible transition at all — the count is correct from the first render.
From the build log