diff --git a/.changeset/wild-parents-begin.md b/.changeset/wild-parents-begin.md
new file mode 100644
index 0000000000..ac98fa0e3e
--- /dev/null
+++ b/.changeset/wild-parents-begin.md
@@ -0,0 +1,5 @@
+---
+'svelte': patch
+---
+
+fix: ensure custom element attribute/prop changes are in their own context
diff --git a/packages/svelte/src/internal/client/dom/elements/attributes.js b/packages/svelte/src/internal/client/dom/elements/attributes.js
index 30c7712f1e..545cab20f8 100644
--- a/packages/svelte/src/internal/client/dom/elements/attributes.js
+++ b/packages/svelte/src/internal/client/dom/elements/attributes.js
@@ -7,6 +7,12 @@ import * as w from '../../warnings.js';
import { LOADING_ATTR_SYMBOL } from '../../constants.js';
import { queue_idle_task, queue_micro_task } from '../task.js';
import { is_capture_event, is_delegated, normalize_attribute } from '../../../../utils.js';
+import {
+ active_effect,
+ active_reaction,
+ set_active_effect,
+ set_active_reaction
+} from '../../runtime.js';
/**
* The value/checked attribute in the template actually corresponds to the defaultValue property, so we need
@@ -145,10 +151,24 @@ export function set_xlink_attribute(dom, attribute, value) {
* @param {any} value
*/
export function set_custom_element_data(node, prop, value) {
- if (get_setters(node).includes(prop)) {
- node[prop] = value;
- } else {
- set_attribute(node, prop, value);
+ // We need to ensure that setting custom element props, which can
+ // invoke lifecycle methods on other custom elements, does not also
+ // associate those lifecycle methods with the current active reaction
+ // or effect
+ var previous_reaction = active_reaction;
+ var previous_effect = active_effect;
+
+ set_active_reaction(null);
+ set_active_effect(null);
+ try {
+ if (get_setters(node).includes(prop)) {
+ node[prop] = value;
+ } else {
+ set_attribute(node, prop, value);
+ }
+ } finally {
+ set_active_reaction(previous_reaction);
+ set_active_effect(previous_effect);
}
}
diff --git a/packages/svelte/tests/runtime-browser/custom-elements-samples/element-effect-context/_config.js b/packages/svelte/tests/runtime-browser/custom-elements-samples/element-effect-context/_config.js
new file mode 100644
index 0000000000..3cf7a66df1
--- /dev/null
+++ b/packages/svelte/tests/runtime-browser/custom-elements-samples/element-effect-context/_config.js
@@ -0,0 +1,24 @@
+import { test } from '../../assert';
+const tick = () => Promise.resolve();
+
+export default test({
+ async test({ assert, target }) {
+ target.innerHTML = '';
+ await tick();
+ await tick();
+
+ /** @type {any} */
+ const el = target.querySelector('my-app');
+ const button = el.shadowRoot.querySelector('button');
+ const p = el.shadowRoot.querySelector('my-tracking').shadowRoot.querySelector('p');
+
+ assert.equal(button.innerHTML, '0');
+ assert.equal(p.innerHTML, 'false');
+
+ button.click();
+ await tick();
+
+ assert.equal(button.innerHTML, '1');
+ assert.equal(p.innerHTML, 'false');
+ }
+});
diff --git a/packages/svelte/tests/runtime-browser/custom-elements-samples/element-effect-context/main.svelte b/packages/svelte/tests/runtime-browser/custom-elements-samples/element-effect-context/main.svelte
new file mode 100644
index 0000000000..fa35c8a086
--- /dev/null
+++ b/packages/svelte/tests/runtime-browser/custom-elements-samples/element-effect-context/main.svelte
@@ -0,0 +1,31 @@
+
+
+
+
+
+
+
+