Why Svelte 5 Wants `let` Instead of `const` (And Why Your Linter Is Confused)
If you’ve been working with Svelte 5 and a linter like Biome, you might have run into this sitauation:
use
constinstead oflet
However, Svelte actually needs that let.
Have you ever wondered why this is?
Here is an attempt to explain it to you.
Svelte 5’s reactivity model is different from the rest of the TypeScript world, and some of our tooling hasn’t quite caught up yet.
The const Rule Everyone Knows
In standard TypeScript and React, the prefer-const rule is a solid best practice.
If you declare a variable and never reassign it, use const. It communicates intent clearly: this binding won’t change. You would think
But there is confusion when it comes to objects that are defined as const.
Let’s take a look at a React example:
// React — const makes perfect sense here
const [count, setCount] = useState(0);
const handleClick = () => setCount(count + 1);
count is a number (primitive). setCount is a function.
Neither can modify itself so it makes sense to use const.
Svelte 5 Plays by Different Rules
Svelte 5 introduced runes — reactive primitives like $state(), $derived(), and $props() that bring fine-grained reactivity directly into JavaScript.
Svelte compiler transforms these declarations into getters and setters behind the scenes.
The value does get reassigned, even if your code looks like a simple variable.
<script lang="ts">
let count = $state(0);
</script>
<button onclick={() => count++}>
Clicked {count} times
</button>
Biome Gets This Wrong
But they are trying to make it right. Biome added experimental Svelte support in v2.3.0, but it has a significant limitation: it only analyzes the <script> block in isolation.
It doesn’t see what happens in the template. So when Biome looks at this:
<script lang="ts">
let isOpen = $state(false);
</script>
<button onclick={() => isOpen = !isOpen}>
Toggle
</button>
It only sees let isOpen = $state(false) and thinks: “this variable is never reassigned, use const.”
It completely misses the isOpen = !isOpen happening in the template markup.
If you run biome check --write, it will automatically change let to const and break your app.
The Biome team has acknowledged this as an explicit limitation of their partial Svelte support.
For now, the workaround is to either disable the useConst rule for Svelte files or add biome-ignore comments where needed.
What About ESLint?
The Svelte ESLint plugin community has proposed two new rules to handle this properly: svelte/rune-prefer-let (which recommends let for rune declarations) and a Svelte-aware svelte/prefer-const that understands reactive declarations. These would give you proper linting without the false positives.
My Take
Svelte 5’s runes are special and deserve their own way of handling variable declarations.
React hooks are different.
Svelte’s compiler rewrites your variable.
I like Svelte.
Please fix Biome.
Don’t make me use eslint.
/ Programming / svelte / Typescript / Biome / Linting