Fredrik Höglund
banner
ephem.dev
Fredrik Höglund
@ephem.dev
Freelance developer - React Query maintainer - Occasional OSS contributor - Been putting React on servers for a decade - Father of two - Homebrewer - Stockholm

ephem.dev
Does defer also guarantee rendering always happen on the client? My guess is no?
November 1, 2025 at 2:22 PM
That's my understanding too, but it's not what I'm seeing at least in Next so might have been fixed there, or in general, or this only happens in certain conditions or for certain changes. 🤷‍♂️

Either way, I wont sweat about it until it becomes an actual problem. Still a good caveat to mention though!
October 22, 2025 at 8:25 PM
Yeah, that’s a good point. I’ve never actually experienced this to be a problem in practice though. Maybe I’m just not observant enough but I like co-locating this kind of thing enough I wont stop doing it if I don’t have a strong enough reason. 😀
October 22, 2025 at 8:43 AM
I've seen so many creative solutions to avoid hoisting, like mutating refs, misusing useImperativeHandle, syncing state in effects etc, etc, often leading to hard to catch bugs.

Just hoist it.
October 21, 2025 at 3:55 PM
Seems harder to work around, guess you would have to wrap the promise-generating API in something like const promise = alwaysSuspendInSSR(() => fetch(…))?

Besides being a bit ugly and not very straightforward to understand I’m sure this has other implications I’m not considering too though.
July 11, 2025 at 11:48 PM
Only downside I see of this is that with const { promise } = useQuery(…); and use(promise) further down, we’d have to suspend with the ”useBrowserOnly” from the fetch and not the use, unless we build a wrapper around use() or leak calling the hook to userland which defeats the purpose.
July 11, 2025 at 11:30 PM
React.use(React.isSSRInProgressPromise) 🙃😆

Landing an API that feels right for this might definitely take some thinking and iteration, I’m happy as long as it’s conditional! 🫶
July 11, 2025 at 11:30 PM
Thanks, I agree. True algebraic effects in JS would be nice.
July 11, 2025 at 11:08 PM
Do you know if there are any plans for notFound()/redirect() for these same reasons?
July 11, 2025 at 7:12 AM
So if you prefetch, we render during SSR.

The conditionality would also let us make this behaviour configurable, so existing experiments with fetching in SSR would still work, and we could introduce this in a minor and keep existing behaviour and flip the default behaviour in next major.
July 11, 2025 at 7:09 AM
For React Query it might be if it had useBrowserOnlyRender({ enabled: true|false }) or equivalent. If that hook does what I think (suspend on server, resolve in browser), we could use it in our useSuspenseQuery _if existing data or promise is missing_ to avoid fetching during SSR.
July 11, 2025 at 7:03 AM
Isn’t that usually done by transporting the promise to the client, but still resolving it on the server though? Not sure I’ve seen any libs/solutions using infinite promises, so if you know any I would love to take a look!
July 10, 2025 at 7:59 AM
In Next there is a super secret internal error you can throw to get around this.. 🫣🤫

I would never of course, let’s say I’ve heard it from a friend.
July 10, 2025 at 7:49 AM
Getting the promise in there is no problem, what I worry about is, is it defined when React(/frameworks?) will stop waiting for that promise and release the stream? Even if stream is released, will request hang on the server and not close?
July 10, 2025 at 7:46 AM
Btw, I don’t think this was an intentional API design choice. I think when useSuspenseQuery was built it just ended up doing the same thing on the server and client because SSR wasn’t part of the suspense story yet.

I agree with you that erroring by default in SSR would be less of a footgun.
July 10, 2025 at 7:37 AM
I would also love a way to cleanly suspend on both server and client, but only fetch on the client. I’m not sure if this is possible though. We cant just suspend with an infinite promise on the server. Ideas are welcome. 😀
July 10, 2025 at 7:32 AM
Depending on your retry settings, that error might get retried and fetched on the client instead. This kind of achieves what you want, but in an ugly way (via errors).
July 10, 2025 at 7:32 AM
So we pass the option to you. I’m throwing a ”Missing prefetch: …” error in the app I’m working on, this is something we should document.
July 10, 2025 at 7:32 AM
The swr and RQ APIs are quite similar in this regard with the exception you mention @tom.sherman.is, if you forget to provide data for a query via HydrationBoundary/initialData, RQ will actually fetch, while swr will error.

You can still error in userland by configuring your fetcher to do so.
July 10, 2025 at 7:32 AM
Agreed, and also, some advice is great for new projects but not worth the change and/or fragmentation cost in existing ones. I will also definitely be having this in mind next time I set up tests.
June 14, 2025 at 1:27 PM
Just to be clear, they are still very, very common and there is nothing wrong with them, it’s just the stats I’m pushing back on, not the conclusion. 😀
June 14, 2025 at 8:42 AM
I also have a problem with equalling SPA with client side only. Most SSR approaches are still SPAs in the sense they don’t do full page reloads, so it’s SSR+SPA. So I think that answer might include some noise like that.
June 14, 2025 at 8:05 AM
June 14, 2025 at 8:02 AM