Skip to content

Conversation

@rmarscher
Copy link
Member

Starting with a failing test that uses window.location to demonstrate that refetch resolves early before the requested page has started streaming.

Once we figure out how to solve it, this will close #1437

It's using the window.location to test that refetch does
not resolve before the requested page has started
streaming. There might be a more direct way to test that.
@vercel
Copy link

vercel bot commented Jul 9, 2025

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Preview Updated (UTC)
waku Ready Ready Preview Sep 5, 2025 4:57am

@codesandbox-ci
Copy link

codesandbox-ci bot commented Jul 9, 2025

This pull request is automatically built and testable in CodeSandbox.

To see build info of the built libraries, click here or the icon next to each commit SHA.

@pkg-pr-new
Copy link

pkg-pr-new bot commented Jul 9, 2025

Open in StackBlitz

npm i https://pkg.pr.new/wakujs/waku@1516

commit: 96a9105

@rmarscher
Copy link
Member Author

My primary method of testing is to do this:

pnpm i
cd packages/waku
pnpm run compile
cd ../../e2e/fixtures/create-pages
pnpm i
pnpm run dev

Increase the setTimeout in SlowComponent in e2e/fixtures/create-pages/src/components/LongSuspenseLayout.tsx to a few seconds so it's easier to observe.

Open https://localhost:3000/long-suspense/1 and click on a link.

I added temp console logging to the resolved elements promise in the InnerRouter useEffect. elements is the createFromFetch (from react-server-dom-webpack/client) return value.

console.log('Elements promise resolved', {
  elements,
  rscPath,
  routeData,
  isStatic,
  prefetchOnly,
});

I can see that the page element is a React.lazy component.

So... the problem is that functions to fetch a new page or refetch the current page use fetchRsc which resolves quickly -- because the response does come back from the server with elements. But the page element has not loaded yet and hasn't even started streaming.

We need a function in router/client that wraps fetchRsc, finds the requested page element and awaits it's lazy promise before resolving.

@dai-shi
Copy link
Member

dai-shi commented Aug 20, 2025

fetchRsc which resolves quickly

What would be the timing of resolving the elements?

elements.then(() => console.log('elements resolved'))

Isn't it the timing you want? If React.lazy delays even after that, it feels tricky.


(edit) I'm probably missing something because refetch already returns a same-timing promise.

@hi-ogawa
Copy link
Collaborator

I might be missing some context but how does current behavior compare with Next.js? For example in this demo, navigation between two tabs updated urls immediately while showing loading.js fallback https://app-router.vercel.app/loading.

@rmarscher
Copy link
Member Author

I might be missing some context but how does current behavior compare with Next.js? For example in this demo, navigation between two tabs updated urls immediately while showing loading.js fallback https://app-router.vercel.app/loading.

That looks like a case where you want the UI to eagerly show a loader. In my use case, I want to continue showing the existing content - maybe with a loading indicator - until the next page has loaded -- which seems to work except the window.location is updated and page scroll are happening as soon as the response returns from the server.

I haven't read the NextJS code to see how they handle RSC payloads in their router, but I generated a demo and it has the desired behavior that I'm unable to acheive with Waku. The URL is not updated until the page has loaded. https://github.com/rmarscher/nextjs-long-suspense-demo

@rmarscher
Copy link
Member Author

I found fetchServerResponse and some code where it is matching rsc elements to route segments. I didn't see where it's awaiting the page segment yet... but it does seem that the inner router suspends indefinitely after routing... so maybe that doesn't re-render until the page component has loaded somehow.

https://github.com/vercel/next.js/blob/0fdaf93770d66d61b588bc8d06c5ec2ec0e66323/packages/next/src/client/components/layout-router.tsx#L413-L415

@hi-ogawa
Copy link
Collaborator

It's quite hard to follow next.js but I think window.history manipulation needs to be done inside a separate effect while the destination url is kept in its own state, something like HistoryUpdater and AppRouterState https://github.com/vercel/next.js/blob/0fdaf93770d66d61b588bc8d06c5ec2ec0e66323/packages/next/src/client/components/app-router.tsx#L125

This allows window.history manipulation effect to run only after suspending transition has finished (not just when fetch response has received), which naturally matches with the timing new async component page is rendered.

@rmarscher
Copy link
Member Author

This allows window.history manipulation effect to run only after suspending transition has finished (not just when fetch response has received)

I understand now that this is outside the scope of refetch from waku/minimal/client. That makes the fetch request and sets the elements from the response. It doesn't know if any of the elements are important and need to be awaited.

The changeRoute function in the waku/router/client is what calls refetch and then updates the window.history and scroll position.

It would be nice if there was a way to watch for the the page component to send something before resolving and updating the scroll state and window.history. I wonder if React Router uses the same process as NextJS.

@hi-ogawa
Copy link
Collaborator

hi-ogawa commented Aug 22, 2025

I experimented with "history mutation as effect" approach on vite rsc starter template. Here is the demo https://github.com/hi-ogawa/reproductions/tree/main/vite-rsc-coordinate-history-and-transition I didn't go through their entire ActonQueue system but I ported a basic ideas of router state and HistoryUpdater.

@rmarscher
Copy link
Member Author

I experimented with "history mutation as effect" approach on vite rsc starter template. Here is the demo https://github.com/hi-ogawa/reproductions/tree/main/vite-rsc-coordinate-history-and-transition I didn't go through their entire ActonQueue system but I ported a basic ideas of router state and HistoryUpdater.

Thanks for doing that. I'm interested to try extending it to return a layout and a page component in the rsc response. It's helpful to work with your minimal repro.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Routing completes before new page component has sent anything

3 participants