# Bug 3491: SSR/hydration

- Status: open
- Project: Zero
- Creator: @aboodman
- Created: 2025-01-30T21:20:01Z
- Modified: 2026-04-22T07:14:47Z
- Reactions: 🎉 ×6 (BenGrieveFitzell, MarkLyck, juliuslipp, liborvanek, neoantox, ximus) · 👀 ×6 (BenGrieveFitzell, Lvdwardt, bmvantunes, juliuslipp, neoantox, rhbuckley) · 👍️ ×7 (BenGrieveFitzell, Lvdwardt, MarkLyck, herkulano, jog1t, juliuslipp, snehalbaghel) · 🫨 ×5 (BenGrieveFitzell, andrictham, juliuslipp, kuatroka, nikhiljay)
- URL: https://bugs.rocicorp.dev/p/zero/issue/3491

## Description

Zero almost supports SSR / hydration today in a simple way:

You can run Zero React apps server side and Zero will in fact run under node. However:

1. userSyncExternalStore requires the caller (us) to return the server snapshot that was used to render server-side for correct hydration. We don't expose a way for app to provide this info for us.
2. As a convenience it would be nice to provide a unique ID for each query so that user can accumulate all the query results used to render a page on server and serialize them to the client. We already have the concept of a query hash internally, we should just expose this as an API.
3/ Query.run() doesn't wait for authoritative results, which is already tracked here: https://bugs.rocicorp.dev/issue/3243
4. Longer-term there is a concern about doing all these queries server-side through ZQL/IVM. Is this a reasonable way to do it? Should we instead use ZQL->PG, and if so how do we avoid the race where PG response is newer than what's in zero-cache. Or should we just optimize the ZQL/IVM/SQLite path.

## Comments (10)

### @aboodman — 2025-02-01T06:54:58Z

2 is already supported via the `hash()` method of `AdvancedQuery`, like so:

![bonk](https://i.imgur.com/ZNgbgeH.png)

### @aboodman — 2025-02-12T09:28:18Z

I havea PR on (1) here: https://github.com/rocicorp/mono/pull/3686 but it's not working yet.

Reactions: 🤚 ×1 (BenGrieveFitzell)

### @marceloclp — 2025-02-19T04:01:26Z

I must be missing something. is Zero able to run SSR at all today? I have had to disable SSR completely since node doesn't have APIs for indexeddb

### @aboodman — 2025-02-19T04:08:47Z

You just put Zero into memory mode by specifying `kvStore:"mem"` on its constructor.

### @aboodman — 2025-02-19T04:09:05Z

... but see (1) in issue description. This won't work super well.

### @marceloclp — 2025-02-19T04:39:22Z

Ah, ok, that's good to know!

What kind of use cases would it make sense to actually prefetch data on the server?

I think for most use cases you actually want to just display some loading state and let the client fetch the data directly. From my very limited usage so far, the only times where preloading data on the server is important is when not doing so would cause a huge amount of layout shift once the results load. And that happens mostly only when you have no results for that particular query yet (like the first time you visit the page).

If we do ZQL -> PG, then we can be sure the results from the server are always newer, but doesn't that mean we are back to where we are today with server-first paradigm?

### @aboodman — 2025-02-19T04:46:29Z

From my perspective the main reason to do it would be to support SEO. For example zbugs is not crawled right now. I'm not sure that having a static rendering is required to get crawled, but I could imagine it being so.

### @aboodman — 2025-02-19T04:48:06Z

For my own apps I think what I would likely do is detect crawlers server-side and just serve a fully static page. I'd likely do that using zql->pg + some framework that can render servers-side. I don't think I'd want to bother with hydration for my own apps, but some users have expressed interest in this.

### @aboodman — 2025-07-18T08:54:01Z

Latest thinking on this:

- I don't think we should run server-side queries against PG. They should run against zero-cache for consistency reasons.
- We should arrange it so that the query is not hydrated twice. It should be reused between server and client.
- But at the same time the server will not have the same state or subscribed queries as clients.

I think these together imply:

- queries should be opportunistically reused across CG within one view syncer (this was already something that would be very nice to do!)
- the server connection should be a _different CG_ from the client
- but it should send metadata in request that allows server to put it in same VS as clients

### @aboodman — 2025-10-10T07:26:41Z

Apparently googlebot will index "100%" of JS pages now. But it doesn't support WS so that's perhaps why zbugs isn't indexed. This is related to the SSR issue because if googlebot could index SPAs built with Zero that would alleviate some of the need for SSR in the first place:

https://discord.com/channels/830183651022471199/1424829404365852712/1425943937536884806
