diff --git a/packages/svelte/src/internal/client/dom/elements/events.js b/packages/svelte/src/internal/client/dom/elements/events.js
index 23022170eb..58217a7b9d 100644
--- a/packages/svelte/src/internal/client/dom/elements/events.js
+++ b/packages/svelte/src/internal/client/dom/elements/events.js
@@ -63,10 +63,21 @@ export function replay_events(dom) {
* @param {AddEventListenerOptions} [options]
*/
export function create_event(event_name, dom, handler, options = {}) {
+ // Capture whether a custom renderer is active at creation time (during mount),
+ // since `renderer` will be null when the event actually fires
+ var is_custom_renderer = renderer != null;
+
/**
* @this {EventTarget}
*/
function target_handler(/** @type {Event} */ event) {
+ if (is_custom_renderer) {
+ // Custom renderers don't use DOM event propagation/delegation,
+ // so just call the handler directly
+ return without_reactive_context(() => {
+ return handler?.call(this, event);
+ });
+ }
if (!options.capture) {
// Only call in the bubble phase, else delegated events would be called before the capturing events
handle_event_propagation.call(dom, event);
diff --git a/packages/svelte/tests/custom-renderers/samples/event-handler-no-propagation/_config.js b/packages/svelte/tests/custom-renderers/samples/event-handler-no-propagation/_config.js
new file mode 100644
index 0000000000..b902b602e6
--- /dev/null
+++ b/packages/svelte/tests/custom-renderers/samples/event-handler-no-propagation/_config.js
@@ -0,0 +1,24 @@
+import { flushSync } from 'svelte';
+import { test } from '../../test';
+
+export default test({
+ test({ assert, target, serialize, logs }) {
+ const button = target.children.find(
+ (/** @type {any} */ n) => n.type === 'element' && n.name === 'button'
+ );
+ assert.ok(button);
+
+ const listeners = button.listeners?.click;
+ assert.ok(listeners, 'button should have click listeners');
+
+ // Call the handler with a plain object that is NOT a DOM Event.
+ // If handle_event_propagation is called, it will fail because
+ // it tries to access DOM-specific properties like composedPath, ownerDocument, etc.
+ for (const { handler } of listeners) {
+ handler.call(button, { type: 'click' });
+ }
+ flushSync();
+
+ assert.deepEqual(logs, [{ type: 'click' }]);
+ }
+});
diff --git a/packages/svelte/tests/custom-renderers/samples/event-handler-no-propagation/main.svelte b/packages/svelte/tests/custom-renderers/samples/event-handler-no-propagation/main.svelte
new file mode 100644
index 0000000000..c5c7626fb2
--- /dev/null
+++ b/packages/svelte/tests/custom-renderers/samples/event-handler-no-propagation/main.svelte
@@ -0,0 +1,7 @@
+
+
+