diff --git a/.changeset/long-foxes-glow.md b/.changeset/long-foxes-glow.md
new file mode 100644
index 000000000000..15216a3b0928
--- /dev/null
+++ b/.changeset/long-foxes-glow.md
@@ -0,0 +1,5 @@
+---
+'svelte': patch
+---
+
+fix: allow setting files binding for ``
diff --git a/documentation/docs/02-template-syntax/05-element-directives.md b/documentation/docs/02-template-syntax/05-element-directives.md
index f9ff1821c813..91c663e19c72 100644
--- a/documentation/docs/02-template-syntax/05-element-directives.md
+++ b/documentation/docs/02-template-syntax/05-element-directives.md
@@ -123,7 +123,7 @@ Numeric input values are coerced; even though `input.value` is a string as far a
```
-On `` elements with `type="file"`, you can use `bind:files` to get the [`FileList` of selected files](https://developer.mozilla.org/en-US/docs/Web/API/FileList). It is readonly.
+On `` elements with `type="file"`, you can use `bind:files` to get the [`FileList` of selected files](https://developer.mozilla.org/en-US/docs/Web/API/FileList).
```svelte
diff --git a/packages/svelte/src/compiler/compile/nodes/Binding.js b/packages/svelte/src/compiler/compile/nodes/Binding.js
index d43ea987660d..53c922f82ce3 100644
--- a/packages/svelte/src/compiler/compile/nodes/Binding.js
+++ b/packages/svelte/src/compiler/compile/nodes/Binding.js
@@ -94,13 +94,12 @@ export default class Binding extends Node {
return;
}
}
- const type = parent.get_static_attribute_value('type');
this.is_readonly =
regex_dimensions.test(this.name) ||
regex_box_size.test(this.name) ||
(is_element(parent) &&
- ((parent.is_media_node() && read_only_media_attributes.has(this.name)) ||
- (parent.name === 'input' && type === 'file'))) /* TODO others? */;
+ parent.is_media_node() &&
+ read_only_media_attributes.has(this.name)) /* TODO others? */;
}
is_readonly_media_attribute() {
return read_only_media_attributes.has(this.name);
diff --git a/packages/svelte/src/compiler/compile/render_ssr/handlers/Element.js b/packages/svelte/src/compiler/compile/render_ssr/handlers/Element.js
index 41f2abfceda8..c79de6137a58 100644
--- a/packages/svelte/src/compiler/compile/render_ssr/handlers/Element.js
+++ b/packages/svelte/src/compiler/compile/render_ssr/handlers/Element.js
@@ -176,6 +176,15 @@ export default function (node, renderer, options) {
node_contents = x`@escape(${snippet} || "")`;
} else if (binding.name === 'value' && node.name === 'select') {
// NOTE: do not add "value" attribute on
+ } else if (
+ binding.name === 'value' &&
+ node.name === 'input' &&
+ node.get_static_attribute_value('type') === 'file'
+ ) {
+ const value = node.get_static_attribute_value('value');
+ if (value !== '') {
+ // NOTE: do not add "value" attribute on
+ }
} else {
const snippet = expression.node;
renderer.add_expression(
diff --git a/packages/svelte/test/js/samples/bindings-readonly-order/expected.js b/packages/svelte/test/js/samples/bindings-readonly-order/expected.js
index 2f2a779d933d..765ab4c004ef 100644
--- a/packages/svelte/test/js/samples/bindings-readonly-order/expected.js
+++ b/packages/svelte/test/js/samples/bindings-readonly-order/expected.js
@@ -30,8 +30,10 @@ function create_fragment(ctx) {
},
m(target, anchor) {
insert(target, input0, anchor);
+ input0.files = /*files*/ ctx[0];
insert(target, t, anchor);
insert(target, input1, anchor);
+ input1.files = /*files*/ ctx[0];
if (!mounted) {
dispose = [
@@ -42,7 +44,15 @@ function create_fragment(ctx) {
mounted = true;
}
},
- p: noop,
+ p(ctx, [dirty]) {
+ if (dirty & /*files*/ 1) {
+ input0.files = /*files*/ ctx[0];
+ }
+
+ if (dirty & /*files*/ 1) {
+ input1.files = /*files*/ ctx[0];
+ }
+ },
i: noop,
o: noop,
d(detaching) {
diff --git a/packages/svelte/test/js/samples/input-files/expected.js b/packages/svelte/test/js/samples/input-files/expected.js
index 7fea84563247..55405571faee 100644
--- a/packages/svelte/test/js/samples/input-files/expected.js
+++ b/packages/svelte/test/js/samples/input-files/expected.js
@@ -24,13 +24,18 @@ function create_fragment(ctx) {
},
m(target, anchor) {
insert(target, input, anchor);
+ input.files = /*files*/ ctx[0];
if (!mounted) {
dispose = listen(input, "change", /*input_change_handler*/ ctx[1]);
mounted = true;
}
},
- p: noop,
+ p(ctx, [dirty]) {
+ if (dirty & /*files*/ 1) {
+ input.files = /*files*/ ctx[0];
+ }
+ },
i: noop,
o: noop,
d(detaching) {