RFC: Pending export #2391
Replies: 22 comments 8 replies
-
I love this idea, specially this
I think for SSR the pending should be ignored, YouTube does respect it in the first paint (check https://youtu.be/dQw4w9WgXcQ) but most likely they are not doing SSR anyway. Maybe have a way to enable it for SSR in the |
Beta Was this translation helpful? Give feedback.
-
export function Pending() {}
Pending.ssr = true
// default false |
Beta Was this translation helpful? Give feedback.
-
This is really elegant. I was actually thinking about how I'd get that "pending loading spinner UX" that I previously had in my client rendered app. Would this also support a timeout, similar to |
Beta Was this translation helpful? Give feedback.
-
Is there a way for my loader to know whether it's being called for SSR or from a client-side render? If it did, then I could decide whether I want to skip loading the perf-problematic request during SSR. Not sure I like that any better than the |
Beta Was this translation helpful? Give feedback.
-
I don't think so. The React team has backpedaled on that in suspense, too. It seems you really just want to "wait for this indefinitely" or "transition to spinners ASAP". One very tricky problem is when the |
Beta Was this translation helpful? Give feedback.
-
I'm not sure how this helps (or what you're supposed to return from the loader?). We just need some config. Loaders are simply request in, response out. Configuring the behavior of a Remix transition/initial render isn't their job. |
Beta Was this translation helpful? Give feedback.
-
I was just suggesting that if you didn't want the loader to run on SSR, it could just return |
Beta Was this translation helpful? Give feedback.
-
Ah gotcha. You can already return |
Beta Was this translation helpful? Give feedback.
-
Clearly I'm striking out on suggestions tonight 😅 I'll get back to doing the dishes and stop being a distraction 🤣 |
Beta Was this translation helpful? Give feedback.
-
Haha, no worries, it's good to help think about it from other angles. I think the core issue is that we're trying to figure out how to define the pending/transition behavior but loaders are just the data, so trying to give loaders the responsibility of defining both behavior and data is mixing responsibilities. In this case the behavior (don't wait for this data and transition to pending if other loaders are ready) and the data (this route still has data!) are both needed. So returning |
Beta Was this translation helpful? Give feedback.
-
Alternative idea for a name: It's more of a noun, like |
Beta Was this translation helpful? Give feedback.
-
Fallback to me made the most sense with suspense's "timeoutMs", so you "fallback to this if the timeout expires" where pending is like "This is the pending ui", no idea of "falling back" it's just the pending state. I'm fine with either, but that's how I think about it. |
Beta Was this translation helpful? Give feedback.
-
Oops, accidentally submitted. Additionally, everything else about loading in remix is "pending", But again, I'm fine with either. |
Beta Was this translation helpful? Give feedback.
-
Yeah, I'm fine with either too. The way I think about it is "fall back to this when we aren't ready to show the component yet, for whatever reason". |
Beta Was this translation helpful? Give feedback.
-
Although neat in theory, the issue would be that it would break the "no javascript" case as the client would never request the SSR data after initial render. I think for "heavy" loaders, you would want to implement a server-side cache and return that... even serving up stale data if it's not critical. |
Beta Was this translation helpful? Give feedback.
-
Not sure if it's a bad idea, will just throw it here: Is it possible to have the
|
Beta Was this translation helpful? Give feedback.
-
@norman-ags check #179 |
Beta Was this translation helpful? Give feedback.
-
I'm testing Remix on a just for fun currently and came across this topic. I have an app with some data-heavy routes and a skeleton state would be a big improvement to the UI. If there is something I could help with, let me now. |
Beta Was this translation helpful? Give feedback.
-
This would be amazing to have in While your suggestions @sergiodxa are definite solutions
It would be great to have this functionality. Prefetching data can be costly, especially with long running IO on the loader, I'm actually resorting to performing these intensive fetches on the client side, which is going against the framework quite a bit. |
Beta Was this translation helpful? Give feedback.
-
@ryanflorence would the following work as a simple alternative to
This may slow down the TTI quite a bit, so its probably not a great solution - what do you think? |
Beta Was this translation helpful? Give feedback.
-
I've been using Remix for +4 months now, and I can also say this feature would be very handy. Sometimes it's not possible or easy to prefetch the data for a particular page and you want the page transition to be instant even if the data hasn't loaded. In my use case, I have a login page that (after successful login) redirects to a data-heavy page. Most of the time, the data is cached, but not always, so with server rendering, the user would be stuck on the login page and the UI would feel unresponsive until the data has completely loaded. Similar to @dan-cooke, that's why I ended up fetching data on the client-side. I felt guilty about going against Remix philosophy 🙈 |
Beta Was this translation helpful? Give feedback.
-
This use case will be handled better than you're probably imagining by the upcoming deferred API :) Learn more: https://www.youtube.com/watch?v=95B8mnhzoCM We're likely weeks away from a stable release of this, but if you'd like to try it we've got it published under the tag titled |
Beta Was this translation helpful? Give feedback.
-
Route Module Pending Component Export
There are two primary approaches to page transitions (ignoring suspense's ditched attempt at a third)
Right now Remix has picked (1), but with a new export to a route module, we could support both.
Today, if you have this, Remix will wait for all data to load before displaying the page
Like
ErrorBoundary
, we could add aPending
export:If a route module exports a
Pending
component, Remix could switch to mode (2) and immediately display this screen when the location changes, dispalying<Pending/>
until the route data all lands.What about nested routes?
Remix will wait for any routes that don't export a
Pending
before displaying any other routes'Pending
export. Some scenarios:Pending
: can transition immediately.In summary, wait for all routes w/o pending, then transition.
So what?
On web and native, both types of transitions are common, and both have their tradeoffs depending on the data being fetched or the type of app you're building. In apps with very "app like" layouts with lots of persistent UI between location changes (rather than typical "pages" on the web with very little persistent UI), immediate transitions to skeleton UI is feels much better. For example, in Discord, it would feel weird to click on a channel and not go immediately to a shimmer/skeleton page. Conversely, we all know how terrible many webpages feel when clicking a link results in 12 spinners bouncing around before the page is built.
There's room for both transitions.
What's really interesting with Remix is that the
Pending
components in layouts can still render an outlet. This means that if a parent route's data is not as important as a child's you don't have to block the transition on it.For example, load up a youtube video on a slower connection and you'll notice the primary content loads first, then the layout shows up around it.
Consider a typical master/detail view with these routes:
And let's say the UI has a sidebar of the users on the left, and the profile on the right:
The most important data at
/users/bob-thornton
is Bob's profile, not the user list.So to get the users's profile displaying as fast as possible it could look like this:
Now if you're looking at
/recent-activity
and click on a user's name, navigating to/users/sally-mae
Remix will:Users.findAll()
andUsers.find("sally-mae")
in parallel for bothusers.tsx
andusers/$userId.tsx
users/$userId.tsx
is complete, Remix will transition to the pageusers.tsx
has not finished loading (probably more expensive anyway), the sidebar will just keep shimmering until it's loaded, but Sally's profile will be up!users.tsx
finished beforeusers/$userId.tsx
, then you're transitioned to a fully formed page.Implementation
Transition hook: just like
hasLoader
andhasAction
, we can easily know which loaders to wait on in the transition hook with ahasPending
.Transition hook: while we still kick off the fetches for the routes with pending ui and loaders, we don't await them. When they land, we just setState into
routeData
.Client side redirects will be a little tricky, to get consistent behavior across document requests, no pending UI, and pending UI, we'd need to wait for all loaders to land (even the pending ones) before deciding to redirect. This means a child route without pending UI could render, and then a parent route with pending ui could redirect after. Also, a child route with pending UI that redirects, should stay in the pending state until all routes data has landed and then decide where to redirect.
<RemixRoute>
: With a mix ofhasLoader
andhasPending
, when it comes time to render the route, we can put a placeholder in therouteData
state, or add a new piece of state to track which routes we're still waiting on, to decide to render the pending UI or not.Server rendering: if a route has pending UI, do we skip the loader on the server render and go straight to pending? Ignore pending and always render the full page on the server? Add a way to let apps decide? I think we just render the whole thing. This would bring consistent results across document/fetch requests and then we could add a way to skip it later if we decide it's worth it.
Beta Was this translation helpful? Give feedback.
All reactions