-
-
Notifications
You must be signed in to change notification settings - Fork 2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
option to render layout before page is loaded #7213
Comments
The problem is that you can use |
That's true, but your layout must already accommodate this lack of data because your layout will still be rendered when there is an error. If error pages inherit layouts then a loading page must inherit layouts as well. I am not against |
+1 Not sure if related, but when clicking on links there's a slight lag before the page moves away on my end: I currently have my internal links trigger a page fade out and then a page fade in of the new page (via ready = false onDestroy() ready = true onMount()), but there's still a slight lag on click which makes it feel less snappier. The onDestroy() doesn't get called immediately it seems, only about 200-400mms later? |
that sounds unrelated. the animation slows this down because the page fades out before being destroyed (which you notice by the delayed onDestroy) |
I don't think the animation is an issue here. I'm attaching a video where there is no animation between pages. What it shows: clicking a link takes a second too long for anything to change for the user, which doesn't feel snappy/performant. Could it be the page.js load() function is running before the page changes and only once data is loaded does it trigger a chance? I'd hope not, as it's best if the page changes or triggers a fade out immediately on click and then shows the new page to the user. This is expected behavior of quick sites/SPAs/html-only sites. EXAMPLE: And for when I have the animation on: a click on a link doesn't trigger fade out immediately on click, it takes a second too long, like @JacobZwang describes, then triggers the animation and then shows the page. Seems like if I want something to happen immediately I'd have to cover the page with a separate "loading screen" element, trigger a fade in on click and then fade it out once the other page has loaded? |
The page redirect happens after your |
@dummdidumm Understood. I ended up doing exactly that and working with navigating store and show/hiding an overlay -- worked like a charm for perceived performance. Ditto that the feature request would be cool to have then for more app-like stuff. Thank you! |
For inspiration: https://reactrouter.com/en/main/guides/deferred |
I think this needs to be a
|
@Rich-Harris Just throwing another idea out there. If we could define default data and then add a store to know when page data is loading, we could show our UI before the data arrived. I believe SvelteKit already has this functionality when using <script>
import { loading } from '$app/stores';
export let data = {
users: []
// or maybe even something like:
// users: localStorage.getItem('users')
};
</script>
<h1>Users</h1>
{#each data.users as user}
<div>
{user.name}
</div>
{:else}
{#if $loading}
loading users...
{:else}
no users found
{/if}
{/each} I suppose this could also be solved using a service worker to return default or cached data immediately, then when invalidate is called with some extra identifier, the service worker queries the data? Semi-related, I don't think SvelteKit has a way of loading "more" data. For instance loading 10 users, then loading 10 more users. Our only option would be to make those requests separately and store their results separately from page data. |
will point out that nextjs (which people will invariably compare sveltekit to) has it appears to be the only major feature we lack in comparison |
… in feed, waiting for sveltejs/kit#7213
Bullet points out of a discussion with Rich:
|
I'm thinking maybe something like |
Looks like I made a duplicate discussion on #8573 which can probably be marked as resolved in favor of this. Linking to it for posterity. Seeing it already actively being talked about is exciting 😅 |
btw to match the UX that React is delivering we shouldn't just expose the "loading screen" for timeouts, we may also want to offer a store or something that tracks the navigation process. hopefully more intuitive than startTransition.
edit: i think what dummdidumm mentions below is correct, so withdrawing this |
What specifically do you want to track in the navigation process? There's $navigating already which tells you if a navigation is currently in progress. What do you mean by "regular |
I think this needs to be a Boolean. I'm not sure this fixes the blipping problem. If I set my loadTimeout to 50, is it supposed to hang for 50ms, time out and trigger the loading UI, and then the real data happens to load in at 52ms? So if I want optimistic I set the loadTimeout to zero? The longer the timeout, the longer the page hangs unresponsive. The shorter the timeout, the more chance of blip. And it's all arbitrary based on internet speeds. Having the loadTimeout on layout does introduce a gotcha. Both layout and page must agree to be optimistic in order to get the loading UI. Kind of weird for DX but not so much UX. Thankfully, this is a problem Next would have already solved. UPDATE: Silly me, the answer was right in my face. Next solved the aforementioned race-condition easily because if Layout is busy showing a loading UI, the page is not even rendered yet. The whole layout gets the loading UI, including the place where the page would be |
This is awesome! Just a question this does not tackle granular control I.e streaming the data correct? Just the whole route/page control of what to show while data is awaited on? I.e a suspense with fallback provided which can be a loading component for specific UI on the page like in Next or as @dummdidumm mentioned it could be like defer in react router and similar to the feature just released in Remix https://github.com/remix-run/remix/releases/tag/remix%401.11.0 |
I'm not sure I understand — what would the boolean indicate? I take your point about delaying 50ms only for the data to come in after 52ms. As far as I can see this is unavoidable; the main goal is to prevent loading UI appearing when the data is available instantly (because it's cached, or whatever), rather than to eliminate flashes entirely which is an impossible problem. A default of 50ms prevents loading UI in those cases where data is available more-or-less-immediately while still providing instantaneous feedback in other cases (anything below ~100ms is perceived as instantaneous by humans, with their inefficient wet brains).
We haven't really articulated the behaviour in detail yet, and there's a few different approaches we could take, so I'm going to try and sketch it out now. Suppose you had files like this (I'm omitting the
One possibility is that you'd show the
Another possibility is that we show loading UI for any invalid node rather than any changed node:
In other words, we need to decide whether loading UI applies to changed or invalid nodes. Initially I leaned towards changed, since it would be jarring to temporarily replace layout UI that's supposed to persist across a navigation. But perhaps there are situations where it makes sense? Hoping we can collectively figure out the 'right' answer here rather than punting it to configuration. Another question to resolve is whether we show one loading state per navigation — the
In the Eager to hear people's thoughts on these two questions in particular. |
Correct. One idea we've been pondering — and this is just an idea, it may not happen — is whether we should serialize all non-top-level promises returned from // +page.js or +page.server.js
export function load() {
return {
a: Promise.resolve(1),
b: {
c: Promise.resolve(2)
}
};
} <!-- +page.svelte -->
<script>
export let data;
</script>
<h1>{data.a}</h1>
{#await data.b.c}
<p>loading...</p>
{:then c}
<p>resolved: {c}</p>
{/await} |
Thank you, that clears things up in my head. Boolean in the sense that either the +loading.svelte file is there or it isn't. I am not seeing the point of having a
Maybe
Taking another bite off SolidStart here, but I would think I would want to show a loading UI only on changed nodes when routing around, but all invalid ones if I specifically call a |
It exists — https://kit.svelte.dev/docs/load#invalidation. In fact that's another question: should programmatic invalidation cause loading UI to appear? Intuitively it feels like the answer is 'no' (for example you probably don't want loading UI to appear while you're waiting for an enhanced form action), but it's hard to articulate exactly why it should appear in some cases but not others. All of which makes me wonder if...
...this really is a desirable feature after all. I'm starting to have doubts. There are too many design questions it throws up where the answer is 'it depends'. |
Heh. You guys continue to impress; I'll definitely be using that. In my opinion there is definitely merit to having a SPA-like experience with the security of having all of your data processed by the server; I hope these design questions can be answered. In practice, it doesn't really make sense to substitute the entire page when loading. It's usually only a small section of the page that is waiting; some navigation items, a list, or maybe a table. Funny enough, it almost seems like maybe components should be the ones to have a +loading.svelte and not pages. I'm entirely joking, but conceptually makes more sense. Personally I think I'm going back to @JacobZwang's original suggestion of just having an |
After starting to implement this in #8582, more and more questions came up whether or not this is the right approach. I'll try to summarize our findings and technical things to consider here: +loading.svelte is nice when ..
+loading.svelte has flaws when ..
Something like
|
Really love the idea of having that defer option as well for granular control. It would be super awesome |
Yes Sir, it will be better. Right now the lengthy loader in Using ExampleTo avoid Promise auto resolving simply put them "deeper" in returning object:export const load: PageLoad = async () => {
return {
loong_promises: {
bigData: new Promise<string>((resolve) => {
setTimeout(() => {
resolve('long awaited data');
}, 3000);
})
}
};
}; ..and handle with <script lang="ts">
import type { PageData } from './$types';
export let data: PageData;
</script>
{#await data.loong_promises.bigData}
loading.. (or some fancy ui)
{:then bigD}
loaded: {JSON.stringify(bigD)}
{/await} IMO |
Hi, When the website is opened, the layout starts and shows a loading screen till a WebSocket connection is established. Then, the condition changes and now, the slot takes place which loads the page.svelte. This page uses a video as a bg., which, for the time being, is 10MB. But the file is not fetched when the loading window is being shown, it is only fetched after the loading component is removed and the page(slot) is loaded. What happens is that till the video is fetched from the server, there is a black bg. and except for the video, everything can be seen on the page. Is there a way so that page is only shown after the video is fetched? A bit bad explanation, but I hope I was able to convey what I am trying to achieve. |
Describe the problem
When building application-like websites, users expect instant feedback when changing pages, even if the page's content isn't ready yet. It's confusing to a user when they click a button and nothing happens, then a second later the page changes. Loading indicators help but are still not ideal.
Describe the proposed solution
Provide an option that will allow a
+layout.svelte
file to be rendered before the+page.svelte
is ready. For example, say clicking a button opens up a page that is a modal. We place the markup for the modal inside the+layout.svelte
file and the content inside the+page.svelte
file. As soon as the user clicks, we can render an empty modal. Then once the page is ready, the content will appear. While the page is loading, we can utilize the slot default as a loading page or skeleton UI.Alternatives considered
#await
block so that surrounding markup is not dependent on data: This destroys SSR and sveltekit's entire data model.+loading.svelte
file: Seems like unnecessary complexity when it can be done without it. If for some reason someone requires separate default and placeholder page contents they can always use the$navigating
store to switch between them.Importance
would make my life easier
Additional Information
+layout.svelte
also relies on data we can still render it as soon as its own data is ready.The text was updated successfully, but these errors were encountered: