diff --git a/.changeset/eight-cougars-watch.md b/.changeset/eight-cougars-watch.md
new file mode 100644
index 0000000000..8a7c234e58
--- /dev/null
+++ b/.changeset/eight-cougars-watch.md
@@ -0,0 +1,5 @@
+---
+"svelte": patch
+---
+
+fix: better handle img loading attribute
diff --git a/packages/svelte/src/internal/client/constants.js b/packages/svelte/src/internal/client/constants.js
index a13c040fdd..d087948b5e 100644
--- a/packages/svelte/src/internal/client/constants.js
+++ b/packages/svelte/src/internal/client/constants.js
@@ -17,3 +17,4 @@ export const EFFECT_TRANSPARENT = 1 << 14;
export const LEGACY_DERIVED_PROP = 1 << 15;
export const STATE_SYMBOL = Symbol('$state');
+export const LOADING_ATTR_SYMBOL = Symbol('');
diff --git a/packages/svelte/src/internal/client/dom/elements/attributes.js b/packages/svelte/src/internal/client/dom/elements/attributes.js
index abd1f1640a..08b6704a17 100644
--- a/packages/svelte/src/internal/client/dom/elements/attributes.js
+++ b/packages/svelte/src/internal/client/dom/elements/attributes.js
@@ -6,6 +6,7 @@ import { delegate } from './events.js';
import { autofocus } from './misc.js';
import { effect, effect_root } from '../../reactivity/effects.js';
import * as w from '../../warnings.js';
+import { LOADING_ATTR_SYMBOL } from '../../constants.js';
/**
* The value/checked attribute in the template actually corresponds to the defaultValue property, so we need
@@ -51,6 +52,11 @@ export function set_attribute(element, attribute, value) {
if (attributes[attribute] === (attributes[attribute] = value)) return;
+ if (attribute === 'loading') {
+ // @ts-expect-error
+ element[LOADING_ATTR_SYMBOL] = value;
+ }
+
if (value === null) {
element.removeAttribute(attribute);
} else {
@@ -345,10 +351,15 @@ export function handle_lazy_img(element) {
// templates.
if (!hydrating && element.loading === 'lazy') {
var src = element.src;
- element.removeAttribute('loading');
+ // @ts-expect-error
+ element[LOADING_ATTR_SYMBOL] = null;
+ element.loading = 'eager';
element.removeAttribute('src');
requestAnimationFrame(() => {
- element.loading = 'lazy';
+ // @ts-expect-error
+ if (element[LOADING_ATTR_SYMBOL] !== 'eager') {
+ element.loading = 'lazy';
+ }
element.src = src;
});
}
diff --git a/packages/svelte/tests/runtime-runes/samples/image-loading-attribute/_config.js b/packages/svelte/tests/runtime-runes/samples/image-loading-attribute/_config.js
new file mode 100644
index 0000000000..d0301360e3
--- /dev/null
+++ b/packages/svelte/tests/runtime-runes/samples/image-loading-attribute/_config.js
@@ -0,0 +1,7 @@
+import { test } from '../../test';
+
+export default test({
+ async test({ assert, target }) {
+ assert.htmlEqual(target.innerHTML, `
`);
+ }
+});
diff --git a/packages/svelte/tests/runtime-runes/samples/image-loading-attribute/main.svelte b/packages/svelte/tests/runtime-runes/samples/image-loading-attribute/main.svelte
new file mode 100644
index 0000000000..7d451788f7
--- /dev/null
+++ b/packages/svelte/tests/runtime-runes/samples/image-loading-attribute/main.svelte
@@ -0,0 +1,9 @@
+
+
+