diff --git a/packages/svelte/src/compiler/index.js b/packages/svelte/src/compiler/index.js
index d40b8b8935..6ae91a267e 100644
--- a/packages/svelte/src/compiler/index.js
+++ b/packages/svelte/src/compiler/index.js
@@ -37,8 +37,8 @@ export function compile(source, options) {
// resolve the per-component custom renderer, taking ``
// into account. The normalized option is always a function returning `string | null | undefined`
- // (see `validate-options.js`). A string opts in to a specific renderer module, `null`/`false`
- // opts out to plain DOM (while keeping the feature enabled) and `true`/absent inherits whatever
+ // (see `validate-options.js`). A string opts in to a specific renderer module, `null`
+ // opts out to plain DOM (while keeping the feature enabled) and an absent option inherits whatever
// the global option resolves to.
let custom_renderer_option = validated.experimental.customRenderer;
@@ -56,7 +56,7 @@ export function compile(source, options) {
if (typeof custom_renderer === 'string') {
custom_renderer_option = () => custom_renderer;
- } else if (custom_renderer === false || custom_renderer === null) {
+ } else if (custom_renderer === null) {
custom_renderer_option = () => null;
}
}
diff --git a/packages/svelte/src/compiler/phases/1-parse/read/options.js b/packages/svelte/src/compiler/phases/1-parse/read/options.js
index 847d7cf0b9..b8369816f5 100644
--- a/packages/svelte/src/compiler/phases/1-parse/read/options.js
+++ b/packages/svelte/src/compiler/phases/1-parse/read/options.js
@@ -37,7 +37,23 @@ export default function read_options(node) {
break; // eslint doesn't know this is unnecessary
}
case 'customRenderer': {
- component_options.customRenderer = get_static_value(attribute);
+ const { value } = attribute;
+ const chunk = Array.isArray(value) ? value[0] : value;
+
+ if (chunk === true || !chunk || (Array.isArray(value) && value.length !== 1)) {
+ e.svelte_options_invalid_attribute_value(attribute, 'a string or null');
+ }
+
+ if (chunk.type === 'Text') {
+ component_options.customRenderer = chunk.data;
+ } else if (
+ chunk.expression?.type === 'Literal' &&
+ (typeof chunk.expression.value === 'string' || chunk.expression.value === null)
+ ) {
+ component_options.customRenderer = chunk.expression.value;
+ } else {
+ e.svelte_options_invalid_attribute_value(attribute, 'a string or null');
+ }
break;
}
case 'customElement': {
diff --git a/packages/svelte/src/compiler/phases/2-analyze/visitors/RegularElement.js b/packages/svelte/src/compiler/phases/2-analyze/visitors/RegularElement.js
index 6c7f80e37f..03850eb3b7 100644
--- a/packages/svelte/src/compiler/phases/2-analyze/visitors/RegularElement.js
+++ b/packages/svelte/src/compiler/phases/2-analyze/visitors/RegularElement.js
@@ -34,12 +34,10 @@ export function RegularElement(node, context) {
context.state.analysis.elements.push(node);
// Special case: Move the children of