diff --git a/.changeset/sour-jeans-collect.md b/.changeset/sour-jeans-collect.md
new file mode 100644
index 0000000000..3e39d9ae04
--- /dev/null
+++ b/.changeset/sour-jeans-collect.md
@@ -0,0 +1,5 @@
+---
+"svelte": patch
+---
+
+fix: update value like attributes in a separate template_effect
diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/template.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/template.js
index cf4f3adc73..7b317a1f1b 100644
--- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/template.js
+++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/template.js
@@ -467,9 +467,16 @@ function serialize_dynamic_element_attributes(attributes, context, element_id) {
* @param {import('estree').Identifier} node_id
* @param {import('#compiler').Attribute} attribute
* @param {import('../types.js').ComponentContext} context
+ * @param {boolean} needs_isolation
* @returns {boolean}
*/
-function serialize_element_attribute_update_assignment(element, node_id, attribute, context) {
+function serialize_element_attribute_update_assignment(
+ element,
+ node_id,
+ attribute,
+ context,
+ needs_isolation
+) {
const state = context.state;
const name = get_attribute_name(element, attribute, context);
const is_svg = context.state.metadata.namespace === 'svg';
@@ -514,7 +521,7 @@ function serialize_element_attribute_update_assignment(element, node_id, attribu
}
if (attribute.metadata.dynamic) {
- if (contains_call_expression) {
+ if (contains_call_expression || needs_isolation) {
state.init.push(serialize_update(update));
} else {
state.update.push(update);
@@ -2065,7 +2072,24 @@ export const template_visitors = {
const is =
is_custom_element && child_metadata.namespace !== 'foreign'
? serialize_custom_element_attribute_update_assignment(node_id, attribute, context)
- : serialize_element_attribute_update_assignment(node, node_id, attribute, context);
+ : serialize_element_attribute_update_assignment(
+ node,
+ node_id,
+ attribute,
+ context,
+ /**
+ * if the input needs input or content reset we also
+ * want to isolate the template effect or else every
+ * unrelated change will reset the value (and the user could)
+ * change the value outside of the reactivity
+ *
+ *
+ *
+ * should only be updated when val changes and not when another
+ * unrelated variable changes.
+ * */
+ needs_content_reset || needs_input_reset
+ );
if (is) is_attributes_reactive = true;
}
}
diff --git a/packages/svelte/tests/runtime-legacy/samples/value-attribute-isolated-update/_config.js b/packages/svelte/tests/runtime-legacy/samples/value-attribute-isolated-update/_config.js
new file mode 100644
index 0000000000..d2a0703301
--- /dev/null
+++ b/packages/svelte/tests/runtime-legacy/samples/value-attribute-isolated-update/_config.js
@@ -0,0 +1,34 @@
+import { test, ok } from '../../test';
+import { flushSync } from 'svelte';
+
+export default test({
+ mode: ['client'],
+
+ async test({ assert, target }) {
+ /**
+ * @type {HTMLInputElement | null}
+ */
+ const input = target.querySelector('input[type=text]');
+ const button = target.querySelector('button');
+ /**
+ * @type {HTMLInputElement | null}
+ */
+ const checkbox = target.querySelector('input[type=checkbox]');
+ const textarea = target.querySelector('textarea');
+ ok(input);
+ ok(button);
+ ok(checkbox);
+ ok(textarea);
+
+ flushSync(() => {
+ input.value = 'foo';
+ checkbox.click();
+ textarea.innerHTML = 'bar';
+ button.click();
+ });
+
+ assert.equal(input.value, 'foo');
+ assert.equal(checkbox.checked, true);
+ assert.equal(textarea.innerHTML, 'bar');
+ }
+});
diff --git a/packages/svelte/tests/runtime-legacy/samples/value-attribute-isolated-update/main.svelte b/packages/svelte/tests/runtime-legacy/samples/value-attribute-isolated-update/main.svelte
new file mode 100644
index 0000000000..897c141b65
--- /dev/null
+++ b/packages/svelte/tests/runtime-legacy/samples/value-attribute-isolated-update/main.svelte
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/packages/svelte/tests/runtime-runes/samples/value-attribute-isolated-update-spread/_config.js b/packages/svelte/tests/runtime-runes/samples/value-attribute-isolated-update-spread/_config.js
new file mode 100644
index 0000000000..d2a0703301
--- /dev/null
+++ b/packages/svelte/tests/runtime-runes/samples/value-attribute-isolated-update-spread/_config.js
@@ -0,0 +1,34 @@
+import { test, ok } from '../../test';
+import { flushSync } from 'svelte';
+
+export default test({
+ mode: ['client'],
+
+ async test({ assert, target }) {
+ /**
+ * @type {HTMLInputElement | null}
+ */
+ const input = target.querySelector('input[type=text]');
+ const button = target.querySelector('button');
+ /**
+ * @type {HTMLInputElement | null}
+ */
+ const checkbox = target.querySelector('input[type=checkbox]');
+ const textarea = target.querySelector('textarea');
+ ok(input);
+ ok(button);
+ ok(checkbox);
+ ok(textarea);
+
+ flushSync(() => {
+ input.value = 'foo';
+ checkbox.click();
+ textarea.innerHTML = 'bar';
+ button.click();
+ });
+
+ assert.equal(input.value, 'foo');
+ assert.equal(checkbox.checked, true);
+ assert.equal(textarea.innerHTML, 'bar');
+ }
+});
diff --git a/packages/svelte/tests/runtime-runes/samples/value-attribute-isolated-update-spread/main.svelte b/packages/svelte/tests/runtime-runes/samples/value-attribute-isolated-update-spread/main.svelte
new file mode 100644
index 0000000000..b5c47f0131
--- /dev/null
+++ b/packages/svelte/tests/runtime-runes/samples/value-attribute-isolated-update-spread/main.svelte
@@ -0,0 +1,17 @@
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/packages/svelte/tests/runtime-runes/samples/value-attribute-isolated-update/_config.js b/packages/svelte/tests/runtime-runes/samples/value-attribute-isolated-update/_config.js
new file mode 100644
index 0000000000..d2a0703301
--- /dev/null
+++ b/packages/svelte/tests/runtime-runes/samples/value-attribute-isolated-update/_config.js
@@ -0,0 +1,34 @@
+import { test, ok } from '../../test';
+import { flushSync } from 'svelte';
+
+export default test({
+ mode: ['client'],
+
+ async test({ assert, target }) {
+ /**
+ * @type {HTMLInputElement | null}
+ */
+ const input = target.querySelector('input[type=text]');
+ const button = target.querySelector('button');
+ /**
+ * @type {HTMLInputElement | null}
+ */
+ const checkbox = target.querySelector('input[type=checkbox]');
+ const textarea = target.querySelector('textarea');
+ ok(input);
+ ok(button);
+ ok(checkbox);
+ ok(textarea);
+
+ flushSync(() => {
+ input.value = 'foo';
+ checkbox.click();
+ textarea.innerHTML = 'bar';
+ button.click();
+ });
+
+ assert.equal(input.value, 'foo');
+ assert.equal(checkbox.checked, true);
+ assert.equal(textarea.innerHTML, 'bar');
+ }
+});
diff --git a/packages/svelte/tests/runtime-runes/samples/value-attribute-isolated-update/main.svelte b/packages/svelte/tests/runtime-runes/samples/value-attribute-isolated-update/main.svelte
new file mode 100644
index 0000000000..35bd481a4c
--- /dev/null
+++ b/packages/svelte/tests/runtime-runes/samples/value-attribute-isolated-update/main.svelte
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
+
+
\ No newline at end of file