---
title: correctness/unmutated-state · Unmutated $state
description: Use const (or $state.raw) for a $state that is never mutated.
---

**Severity:** info · **Category:** correctness

## What it checks

Flags a `let x = $state(...)` whose value is never written or escaped anywhere in the component: not reassigned, not mutated (`x.a = …`, `x.push()`), not bound (`bind:value={x}`), not passed to a function or component.

State passed to a `use:`/`transition:`/`animate:` directive is not flagged either, since the receiving code holds the reference and may mutate it invisibly.

## Why it matters

A `$state` that is never mutated pays for reactivity it never uses, meaning deep proxying and dependency tracking. `const` is clearer and cheaper; `$state.raw` fits when you only ever reassign the value wholesale (never mutate its properties).

## How to fix

```svelte
<script>
  // Instead of: let title = $state('Dashboard');
  const title = 'Dashboard';

  // Reassigned wholesale but never deeply mutated? Use $state.raw:
  let data = $state.raw(initial);
  data = nextValue;
</script>
```

## Mode differences

None. This rule reads source, the same `.svelte` and `.ts` files, everywhere it runs. The CLI, the Vite plugin's build pass, and the live dashboard's static baseline all report it identically, and the rendered-HTML pass never re-evaluates it. Scoping a run with `--route` skips it: component-scoped rules have no route to attribute a finding to.

## Disabling

Silence a single occurrence with `<!-- svelte-vitals-disable-next-line correctness/unmutated-state -->` on the line above it, or turn the rule off:

```js svelte-vitals.config.js
export default {
  rules: {
    'correctness/unmutated-state': 'off'
  }
};
```
