-
-
Notifications
You must be signed in to change notification settings - Fork 4.7k
Description
Describe the bug
When using bind:textContent on a contenteditable element with a string value containing <script> tags and import statements, the browser attempts to execute the code, resulting in the error:
Uncaught SyntaxError: Cannot use import statement outside a module
Reproduction
<script>
let code_example = $state(`<script>
import { Component } from 'library';
console.log('This should not execute');
<\/script>`);
let editor_element = $state();
</script>
<div
bind:this={editor_element}
contenteditable="true"
bind:textContent={code_example}
></div>Expected Behavior
The <script> tag should be rendered as plain text in the contenteditable div, and no JavaScript should be executed.
Actual Behavior
The browser attempts to parse and execute the script content, throwing:
Uncaught SyntaxError: Cannot use import statement outside a module (at (index):XXXX)
Analysis
According to MDN documentation, setting textContent should be safe:
"Setting
textContenton a node removes all of the node's children and replaces them with a single text node with the given string value."
Text nodes should not be parsed as HTML or executed as JavaScript. However, Svelte's bind:textContent appears to be doing something that triggers script evaluation.
Compiled Output
The compiled code shows:
ho("textContent", E, e)This binding mechanism seems to be causing the issue.
Workaround
Replace bind:textContent with direct assignment in $effect:
<script>
let code_example = $state(`<script>
import { Component } from 'library';
<\/script>`);
let editor_element = $state();
$effect(() => {
if (editor_element) {
editor_element.textContent = code_example;
}
});
</script>
<div
bind:this={editor_element}
contenteditable="true"
oninput={(e) => {
code_example = e.currentTarget.textContent || '';
}}
class="code-editor"
></div>This works correctly and doesn't cause script execution.
Context
This was discovered while building a Svelte syntax highlighter component that displays code examples. The component uses contenteditable with bind:textContent to allow editing while applying CSS highlight API for syntax highlighting:
Logs
System Info
System:
OS: macOS 15.5
Binaries:
Node: 22.17.1 - .../.nvm/versions/node/v22.17.1/bin/node
Yarn: 1.22.22 - .../usr/local/bin/yarn
npm: 11.6.2 - .../.nvm/versions/node/v22.17.1/bin/npm
bun: 0.8.0 - .../.bun/bin/bun
Watchman: 2024.10.21.00 - /usr/local/bin/watchman
Browsers:
Chrome: 142.0.7444.176
Safari: 26.1
npmPackages:
svelte: ^5.41.0 => 5.43.6Severity
annoyance