data-sveltekit-preload-data="hover" results in unexpected hard navigation when network is sketchy / offline
Describe the bug
When using data-sveltekit-preload-data="hover", you can invoke an unexpected hard navigation when the network is unavailable. When you're offline, and hover over a link, SvelteKit tries to preload the data/code, that fails (because you're offline, of course), and then it tries to load the error page (which also fails), and the eventual fallback is a hard navigation to the link's href, which I would not expect to happen while you are just hovering over a link.
This requires a certain combination of circumstances:
- root layout has a load function (this does not happen without that)
- you are using
data-sveltekit-preload-data="hover"either on the link or set globally on thebody(it's still a problem with"tap", but it's way more obviously a bug with"hover", because you're not clicking on the link) - the link goes to a different page route
- you are offline or under some other adverse network situation, such that the preload requests and the subsequent requests for the error page all fail
What appears to be happening stepping through the code in client/client.js, it looks like when you try to preload the data on a link, SvelteKit is eventually doing load_route(), which sends you via a catch block to load_root_error_page(), which has a conditional when your root layout has a load function that lands you in native_navigation.
So, this is easiest to reproduce when offline, but it could happen in other adverse network situations.
Reproduction
https://github.com/leereamsnyder/sveltekit-offline-preload-bug
This is a skeleton SvelteKit project, with the addition of a simple layout load fn.
The readme has repro steps, copied here:
-
npm install -
npm run dev - Open up http://localhost:5173 in Chrome (this is most straightforward in Chrome)
- In the command palette (CMD + SHIFT + P), choose Go Offline, or select Offline from the throttling selector in the Network panel, typically next to Disable cache
- Hover over the Sverdle link in the nav. In a flash, SvelteKit will do a hard navigation to
http://localhost:5173/sverdle, which will fail because you're offline
Logs
No response
System Info
System:
OS: macOS 13.2
CPU: (10) arm64 Apple M1 Pro
Memory: 112.14 MB / 16.00 GB
Shell: 5.8.1 - /bin/zsh
Binaries:
Node: 18.13.0 - /opt/homebrew/bin/node
Yarn: 1.22.19 - /opt/homebrew/bin/yarn
npm: 8.19.3 - /opt/homebrew/bin/npm
Browsers:
Chrome: 111.0.5563.110
Safari: 16.3
Safari Technology Preview: 16.4
npmPackages:
@sveltejs/adapter-auto: ^2.0.0 => 2.0.0
@sveltejs/kit: ^1.5.0 => 1.13.0
svelte: ^3.54.0 => 3.57.0
vite: ^4.2.0 => 4.2.1
Severity
serious, but I can work around it
Additional Information
Just to be clear, I don't expect SvelteKit to, ya know, work when the network is unavailable.
It's the unexpected hard navigation that happens when you just hover over a link that's the problem.
This is not a throrough or ironclad workaround, but to the extent that the browser will tell you that you're offline with built-in APIs, you can mitigate this by turning preload behavior "off" when detected. I have a component like this floating around that changes the data-sveltekit-preload-data on the <body>:
import { onMount } from 'svelte'
onMount(() => {
const originalPreloadData = document.body.getAttribute('data-sveltekit-preload-data')
// this is not often accurate!
if (navigator.onLine === false) {
document.body.setAttribute('data-sveltekit-preload-data', 'off')
}
window.addEventListener('offline', () => {
document.body.setAttribute('data-sveltekit-preload-data', 'off')
})
window.addEventListener('online', () => {
document.body.setAttribute('data-sveltekit-preload-data', originalPreloadData)
})
})
I am experiencing a similar thing. I have
- a prerendered +layout.server.js file that is loading data
- a data-sveltekit-preload-data="hover" attribute on the body tag
- a page transition handled by the +layout.svelte file on route change
Everything works fine in dev mode. Also npm run preview works great. But once deployed with Firebase, the route change happens when I'm just hovering over a link! And it's also happening when I'm online - not just when offline.
Deleting data-sveltekit-preload-data="hover" from the body tag solves the issue with the navigation on hover. But the app ist still performing a full page reload, thus my page transition doesn't work anymore.
I'm facing this exact same problem without having to deal with that last point.
you are offline or under some other adverse network situation, such that the preload requests and the subsequent requests for the error page all fail
I built my SvelteKit app, and I ran a local preview using that vite preview and it works fine. Furthermore, I even throttled the network speed to GPRS in Firefox, and it's still not forcefully switching routes.
But, when I deploy my app to Firebase, it's the exact same behavior in the deployed application as described above.
To deploy, I didn't use any adapters, just regular firebase deploy command because Firebase has experimental support for deploying SvelteKit apps now.
We have also seen this problem on our site where hovering over a link sometimes randomly triggers a navigation. But our site is deployed on Vercel, not Firebase.
Regardless of network, when a link would error (403), just hovering over it redirects the user to the error page. We'd expect the error to be silent, rather than redirecting to the error page!
This is also happening to me when I use cloudflare DDoS protection. I think it is because the network request is initially not a success because it hits the DDoS protection so it does a hard navigation.
I have a similar issue where hard-navigation occurs when __data.json?x-sveltekit-invalidated=01 results in a 403 for whatever reason (deployed to cloudfront/s3)