sveltejs/svelte · warning · MigrationError
$$props is used together with named props in a way that cann
Error message
$$props is used together with named props in a way that cannot be automatically migrated.
What it means
Thrown when a component both consumes `$$props` (the legacy catch-all prop bag) and declares named props that have initializers or are mutated (`binding.updated`). The automigrator cannot decide how to merge a spread props object with explicit prop bindings, so it bails. The condition is `state.analysis.uses_props && (declarator.init || binding.updated)`.
Source
Thrown at packages/svelte/src/compiler/migrate/index.js:636
// const value = path.expression;
// if (binding.kind === 'bindable_prop' || binding.kind === 'rest_prop') {
// state.props.push({
// local: name,
// exported: binding.prop_alias ? binding.prop_alias : name,
// init: value
// });
// state.props_insertion_point = /** @type {number} */(declarator.end);
// } else {
// declarations.push(b.declarator(path.node, value));
// }
// }
}
const name = declarator.id.name;
const binding = /** @type {Binding} */ (state.scope.get(name));
if (state.analysis.uses_props && (declarator.init || binding.updated)) {
throw new MigrationError(
'$$props is used together with named props in a way that cannot be automatically migrated.'
);
}
const prop = state.props.find((prop) => prop.exported === (binding.prop_alias || name));
if (prop) {
next();
// $$Props type was used
prop.init = declarator.init
? state.str
.snip(
/** @type {number} */ (declarator.init.start),
/** @type {number} */ (declarator.init.end)
)
.toString()
: '';
prop.bindable = binding.updated;
prop.exported = binding.prop_alias || name;View on GitHub (pinned to 20b341f100)
Solutions
- Stop reading `$$props` and switch to explicit prop declarations before migrating.
- Use `restProps` via `let { foo, ...rest } = $props()` after migration and spread `rest` instead of `$$props`.
- Migrate the file by hand: split the named props into `$props()` and forward the remainder via a rest binding.
Example fix
// before
<script>
export let foo = 1;
$: foo++;
</script>
<Child {...$$props} />
// after (manual)
<script>
let { foo = $bindable(1), ...rest } = $props();
foo++;
</script>
<Child {...rest} /> Defensive patterns
Strategy: validation
Validate before calling
// Detect the combined $$props + named/mutable prop pattern.
function usesPropsBagAndMutatedProps(source) {
return /\$\$props/.test(source) && /export\s+let\s+\w+\s*=/.test(source);
}
if (usesPropsBagAndMutatedProps(componentSource)) {
// decide between rest-props forwarding and explicit prop declarations
} Try / catch
const { code } = migrate({ filename, source });
if (code.startsWith('<!-- @migration-task') && /\$\$props is used together with named props/i.test(code)) {
// hand-migrate: `let { namedProp, ...rest } = $props()` and spread `rest`
} Prevention
- Pick one strategy for attribute forwarding — explicit props OR `$$props` spread — not both.
- In Svelte 4 wrappers, prefer a rest-props pattern (`export let ...rest` via a helper) over `$$props`.
- Pre-migrate components that mutate their own props to use local state instead.
When it happens
Trigger: Running `migrate()` on a Svelte 4 component that combines `export let foo = ...` (or mutates a prop) with a read of `$$props` (e.g. forwarding unknown attributes: `<Child {...$$props} />`). The two patterns map to incompatible runes-mode translations.
Common situations: Attribute-forwarding components — a Svelte 4 idiom where `$$props` is spread onto a child while specific props are also declared and given defaults or reassigned. Wrapper/facade components around native elements frequently do this.
Related errors
- Encountered an export declaration pattern that is not suppor
- migrating this component would require adding a `$${rune}` r
- Can't migrate code with ${illegal_specifiers.join(' and ')}.
- can't migrate `${...}` to `$${rune}` because there's a varia
- can't migrate `: ${...}` to `$${rune}` because there's a var
AI-assisted analysis of sveltejs/svelte@20b341f100 (2026-08-12).
Data as JSON: /api/errors/e6ba38d9ebf9f7fc.
Report an issue: GitHub.