-
-
Notifications
You must be signed in to change notification settings - Fork 2.2k
Description
Describe the bug
When experimental.async: true is enabled in SvelteKit config, hovering over links (which triggers preloading) causes derived values to update inconsistently in the DOM. Specifically, when a $derived value is used in both an attribute spread and as text content, the attribute updates immediately on hover while the text content doesn't update until the link is actually clicked.
This inconsistent behavior only occurs with preloading (on hover by default). Setting data-sveltekit-preload-data="off" resolves the issue, confirming it's related to the preloading mechanism.
Reproduction
Minimal reproduction: https://stackblitz.com/edit/sveltejs-kit-template-default-dqe2gmpu?description=The%20default%20SvelteKit%20template,%20generated%20with%20create-svelte&title=SvelteKit%20Default%20Template
Steps to reproduce:
- Open the reproduction link above
- Navigate to /posts/1
- Hover over the link to /posts/2 (without clicking)
- Observe that the data-active attribute in the DOM becomes true
- Notice the text content still shows false
- Click the link
- Now the text content updates to true
Expected behavior: Neither the attribute nor the text content should update on hover. Both should only update when the link is actually clicked and navigation occurs. Preloading should be invisible to the UI.
<!-- src/routes/posts/[postId]/+page.svelte -->
<script lang="ts">
import { page } from '$app/state';
import Link from './link.svelte';
const posts = [
{ id: '1', title: 'Post 1' },
{ id: '2', title: 'Post 2' },
{ id: '3', title: 'Post 3' }
];
</script>
<ul>
{#each posts as post (post.id)}
<li>
<Link href="/posts/{post.id}" isActive={page.params.postId == post.id} />
</li>
{/each}
</ul><!-- src/routes/posts/[postId]/link.svelte -->
<script lang="ts">
const { isActive, href }: { isActive: boolean; href: string } = $props();
const id = $props.id();
const attrs = $derived({
'data-active': isActive
});
</script>
<a {href} {...attrs}>
{id}: {attrs['data-active']}
</a>
<style>
[data-active='true'] {
font-weight: bold;
}
</style>// svelte.config.js
import adapter from '@sveltejs/adapter-auto';
/** @type {import('@sveltejs/kit').Config} */
const config = {
kit: {
// adapter-auto only supports some environments, see https://kit.svelte.dev/docs/adapter-auto for a list.
// If your environment is not supported or you settled on a specific environment, switch out the adapter.
// See https://kit.svelte.dev/docs/adapters for more information about adapters.
adapter: adapter(),
},
compilerOptions: {
experimental: {
async: true
}
}
};
export default config;Logs
System Info
npmPackages:
@sveltejs/adapter-auto: ^7.0.0 => 7.0.0
@sveltejs/kit: ^2.48.5 => 2.48.5
@sveltejs/vite-plugin-svelte: ^6.2.1 => 6.2.1
svelte: ^5.43.7 => 5.43.7
vite: ^7.2.2 => 7.2.2Severity
serious, but I can work around it
Additional Information
- With
experimental.async: false, everything works correctly - nothing updates on hover, both attribute and text update together on click - Setting
data-sveltekit-preload-data="off"on the link prevents the issue - The issue appears to be that preloading is somehow causing
page.params(or values derived from it) to update prematurely for attributes but not for text content - The same
$derivedvalue (attrs['data-active']) behaves differently depending on where it's used (attribute spread vs text content)