diff --git a/.changeset/warm-eyes-protect.md b/.changeset/warm-eyes-protect.md
new file mode 100644
index 0000000000..fbb971f314
--- /dev/null
+++ b/.changeset/warm-eyes-protect.md
@@ -0,0 +1,5 @@
+---
+'svelte': patch
+---
+
+fix: recognize all custom element prop definitions
diff --git a/documentation/docs/07-misc/04-custom-elements.md b/documentation/docs/07-misc/04-custom-elements.md
index 83874f2f5b..71c66f7edc 100644
--- a/documentation/docs/07-misc/04-custom-elements.md
+++ b/documentation/docs/07-misc/04-custom-elements.md
@@ -49,6 +49,8 @@ console.log(el.name);
el.name = 'everybody';
```
+Note that you need to list out all properties explicitly, i.e. doing `let props = $props()` without declaring `props` in the [component options](#Component-options) means that Svelte can't know which props to expose as properties on the DOM element.
+
## Component lifecycle
Custom elements are created from Svelte components using a wrapper approach. This means the inner Svelte component has no knowledge that it is a custom element. The custom element wrapper takes care of handling its lifecycle appropriately.
diff --git a/packages/svelte/src/compiler/phases/3-transform/client/transform-client.js b/packages/svelte/src/compiler/phases/3-transform/client/transform-client.js
index 8369842e13..4daff53efb 100644
--- a/packages/svelte/src/compiler/phases/3-transform/client/transform-client.js
+++ b/packages/svelte/src/compiler/phases/3-transform/client/transform-client.js
@@ -561,17 +561,19 @@ export function client_component(analysis, options) {
if (analysis.custom_element) {
const ce = analysis.custom_element;
+ const ce_props = typeof ce === 'boolean' ? {} : ce.props || {};
/** @type {ESTree.Property[]} */
const props_str = [];
- for (const [name, binding] of properties) {
- const key = binding.prop_alias ?? name;
- const prop_def = typeof ce === 'boolean' ? {} : ce.props?.[key] || {};
+ for (const [name, prop_def] of Object.entries(ce_props)) {
+ const binding = analysis.instance.scope.get(name);
+ const key = binding?.prop_alias ?? name;
+
if (
!prop_def.type &&
- binding.initial?.type === 'Literal' &&
- typeof binding.initial.value === 'boolean'
+ binding?.initial?.type === 'Literal' &&
+ typeof binding?.initial.value === 'boolean'
) {
prop_def.type = 'Boolean';
}
@@ -585,9 +587,17 @@ export function client_component(analysis, options) {
].filter(Boolean)
)
);
+
props_str.push(b.init(key, value));
}
+ for (const [name, binding] of properties) {
+ const key = binding.prop_alias ?? name;
+ if (ce_props[key]) continue;
+
+ props_str.push(b.init(key, b.object([])));
+ }
+
const slots_str = b.array([...analysis.slot_names.keys()].map((name) => b.literal(name)));
const accessors_str = b.array(
analysis.exports.map(({ name, alias }) => b.literal(alias ?? name))
diff --git a/packages/svelte/tests/runtime-browser/custom-elements-samples/props-rune-attributes/_config.js b/packages/svelte/tests/runtime-browser/custom-elements-samples/props-rune-attributes/_config.js
new file mode 100644
index 0000000000..04b49c5c00
--- /dev/null
+++ b/packages/svelte/tests/runtime-browser/custom-elements-samples/props-rune-attributes/_config.js
@@ -0,0 +1,21 @@
+import { test } from '../../assert';
+const tick = () => Promise.resolve();
+
+export default test({
+ async test({ assert, target }) {
+ target.innerHTML = '
1
+2
+3
+ ` + ); + } +}); diff --git a/packages/svelte/tests/runtime-browser/custom-elements-samples/props-rune-attributes/main.svelte b/packages/svelte/tests/runtime-browser/custom-elements-samples/props-rune-attributes/main.svelte new file mode 100644 index 0000000000..bea04b1248 --- /dev/null +++ b/packages/svelte/tests/runtime-browser/custom-elements-samples/props-rune-attributes/main.svelte @@ -0,0 +1,14 @@ +{rest.foo}
+{bar}
+{baz}
diff --git a/packages/svelte/tests/runtime-browser/custom-elements-samples/props-rune-attributes/my-widget.svelte b/packages/svelte/tests/runtime-browser/custom-elements-samples/props-rune-attributes/my-widget.svelte new file mode 100644 index 0000000000..cdba7491f9 --- /dev/null +++ b/packages/svelte/tests/runtime-browser/custom-elements-samples/props-rune-attributes/my-widget.svelte @@ -0,0 +1,14 @@ +{rest.foo}
+{bar}
+{baz}