Repository: sveltejs/eslint-plugin-svelte
Pull request #1061 (2025-02-08 08:23:15)

feat: add `no-unused-props` rule
close: https://github.com/sveltejs/eslint-plugin-svelte/issues/1028

changeset-bot[bot]: ###  🦋  Changeset detected

Latest commit: f70cc81a30e5e27121c72302703f7b85e0c46de9

**The changes in this PR will be included in the next version bump.**

<details><summary>This PR includes changesets to release 1 package</summary>

  | Name                 | Type  |
| -------------------- | ----- |
| eslint-plugin-svelte | Minor |

</details>

Not sure what this means? [Click here  to learn what changesets are](https://github.com/changesets/changesets/blob/main/docs/adding-a-changeset.md).

[Click here if you're a maintainer who wants to add another changeset to this PR](https://github.com/sveltejs/eslint-plugin-svelte/new/feat/no-unused-props?filename=.changeset/eighty-cameras-move.md&value=---%0A%22eslint-plugin-svelte%22%3A%20patch%0A---%0A%0Afeat%3A%20add%20%60no-unused-props%60%20rule%0A)


github-actions[bot]: <!-- posted by pkg.pr.new-comment.mjs -->

## Try the Instant Preview in Online Playground

[ESLint Online Playground](https://eslint-online-playground.netlify.app/?overrideDeps=%7B%22eslint-plugin-svelte%22%3A%22https%3A%2F%2Fpkg.pr.new%2Feslint-plugin-svelte%401061%22%7D#eslint-plugin-svelte%20with%20typescript)

## Install the Instant Preview to Your Local

```
npm i https://pkg.pr.new/eslint-plugin-svelte@1061
```

## Published Instant Preview Packages:

- eslint-plugin-svelte: https://pkg.pr.new/eslint-plugin-svelte@1061

[View Commit](https://github.com/sveltejs/eslint-plugin-svelte/commit/f70cc81a30e5e27121c72302703f7b85e0c46de9)

Patch:
diff --git a/.changeset/blue-panthers-run.md b/.changeset/blue-panthers-run.md
deleted file mode 100644
index afb628d..0000000
--- a/.changeset/blue-panthers-run.md
+++ /dev/null
@@ -1,5 +0,0 @@
----
-'eslint-plugin-svelte': patch
----
-
-fix: support each blocks without an item
diff --git a/.changeset/blue-swans-give.md b/.changeset/blue-swans-give.md
deleted file mode 100644
index 2d84a8c..0000000
--- a/.changeset/blue-swans-give.md
+++ /dev/null
@@ -1,5 +0,0 @@
----
-'eslint-plugin-svelte': minor
----
-
-feat: Implement util to conditionally run lint based on Svelte version and SvelteKit routes etc
diff --git a/.changeset/brown-dots-change.md b/.changeset/brown-dots-change.md
deleted file mode 100644
index dfa797d..0000000
--- a/.changeset/brown-dots-change.md
+++ /dev/null
@@ -1,5 +0,0 @@
----
-'eslint-plugin-svelte': minor
----
-
-feat: add Svelte 5 support to `no-not-function-handler`
diff --git a/.changeset/cold-starfishes-doubt.md b/.changeset/cold-starfishes-doubt.md
deleted file mode 100644
index f85c6b3..0000000
--- a/.changeset/cold-starfishes-doubt.md
+++ /dev/null
@@ -1,5 +0,0 @@
----
-'eslint-plugin-svelte': minor
----
-
-feat: added the no-navigation-without-base rule
diff --git a/.changeset/cool-rockets-allow.md b/.changeset/cool-rockets-allow.md
deleted file mode 100644
index 338906c..0000000
--- a/.changeset/cool-rockets-allow.md
+++ /dev/null
@@ -1,5 +0,0 @@
----
-'eslint-plugin-svelte': major
----
-
-feat!: drop support for old eslint
diff --git a/.changeset/cool-steaks-happen.md b/.changeset/cool-steaks-happen.md
deleted file mode 100644
index 8f6fc6f..0000000
--- a/.changeset/cool-steaks-happen.md
+++ /dev/null
@@ -1,5 +0,0 @@
----
-'eslint-plugin-svelte': patch
----
-
-chore: upgrade `svelte-eslint-parser` to `1.0.0-next.8`
diff --git a/.changeset/cool-tomatoes-fry.md b/.changeset/cool-tomatoes-fry.md
deleted file mode 100644
index f61cd8f..0000000
--- a/.changeset/cool-tomatoes-fry.md
+++ /dev/null
@@ -1,5 +0,0 @@
----
-'eslint-plugin-svelte': patch
----
-
-chore: change the rule category of `valid-prop-names-in-kit-pages` to `SvelteKit`
diff --git a/.changeset/early-trainers-know.md b/.changeset/early-trainers-know.md
deleted file mode 100644
index c379b37..0000000
--- a/.changeset/early-trainers-know.md
+++ /dev/null
@@ -1,5 +0,0 @@
----
-'eslint-plugin-svelte': minor
----
-
-feat: added the `consistent-selector-style` rule
diff --git a/.changeset/eighty-trainers-push.md b/.changeset/eighty-trainers-push.md
deleted file mode 100644
index 8e8690a..0000000
--- a/.changeset/eighty-trainers-push.md
+++ /dev/null
@@ -1,5 +0,0 @@
----
-'eslint-plugin-svelte': patch
----
-
-chore: add `:exit` for each node listener
diff --git a/.changeset/fast-coins-fly.md b/.changeset/fast-coins-fly.md
deleted file mode 100644
index 3169b1e..0000000
--- a/.changeset/fast-coins-fly.md
+++ /dev/null
@@ -1,5 +0,0 @@
----
-'eslint-plugin-svelte': minor
----
-
-feat: Support `<svelte:boundary>`
diff --git a/.changeset/fifty-parents-itch.md b/.changeset/fifty-parents-itch.md
deleted file mode 100644
index c5f1b36..0000000
--- a/.changeset/fifty-parents-itch.md
+++ /dev/null
@@ -1,5 +0,0 @@
----
-'eslint-plugin-svelte': minor
----
-
-feat: Added suggestion to the `block-lang` rule.
diff --git a/.changeset/gold-bottles-learn.md b/.changeset/gold-bottles-learn.md
deleted file mode 100644
index 2eb49d5..0000000
--- a/.changeset/gold-bottles-learn.md
+++ /dev/null
@@ -1,5 +0,0 @@
----
-'eslint-plugin-svelte': patch
----
-
-fix: allow `children` in `valid-prop-names-in-kit-pages` rule
diff --git a/.changeset/gorgeous-cats-exercise.md b/.changeset/gorgeous-cats-exercise.md
deleted file mode 100644
index 2db770b..0000000
--- a/.changeset/gorgeous-cats-exercise.md
+++ /dev/null
@@ -1,31 +0,0 @@
----
-'eslint-plugin-svelte': major
----
-
-**Enabled in recommended config**:
-- `svelte/infinite-reactive-loop`
-- `svelte/no-dom-manipulating`
-- `svelte/no-dupe-on-directives`
-- `svelte/no-dupe-use-directives`
-- `svelte/no-export-load-in-svelte-module-in-kit-pages`
-- `svelte/no-immutable-reactive-statements`
-- `svelte/no-inspect`
-- `svelte/no-raw-special-elements`
-- `svelte/no-reactive-functions`
-- `svelte/no-reactive-literals`
-- `svelte/no-reactive-reassign`
-- `svelte/no-store-async`
-- `svelte/no-svelte-internal`
-- `svelte/no-useless-children-snippet`
-- `svelte/no-useless-mustaches`
-- `svelte/require-each-key`
-- `svelte/require-event-dispatcher-types`
-- `svelte/require-store-reactive-access`
-- `svelte/require-stores-init`
-- `svelte/valid-each-key`
-- `svelte/valid-prop-names-in-kit-pages`
-
-**Removed from recommended config**:
-- `svelte/valid-compile`
-
-This update introduces breaking changes due to newly enabled rules.
diff --git a/.changeset/great-turkeys-hang.md b/.changeset/great-turkeys-hang.md
deleted file mode 100644
index 992e3e8..0000000
--- a/.changeset/great-turkeys-hang.md
+++ /dev/null
@@ -1,5 +0,0 @@
----
-'eslint-plugin-svelte': minor
----
-
-feat: support `warningFilter` in `valid-compile` rule
diff --git a/.changeset/grumpy-deers-film.md b/.changeset/grumpy-deers-film.md
deleted file mode 100644
index 7ac8178..0000000
--- a/.changeset/grumpy-deers-film.md
+++ /dev/null
@@ -1,5 +0,0 @@
----
-'eslint-plugin-svelte': patch
----
-
-chore: change the rule category of `no-export-load-in-svelte-module-in-kit-pages` to `SvelteKit`
diff --git a/.changeset/hip-avocados-call.md b/.changeset/hip-avocados-call.md
deleted file mode 100644
index 850593e..0000000
--- a/.changeset/hip-avocados-call.md
+++ /dev/null
@@ -1,5 +0,0 @@
----
-'eslint-plugin-svelte': patch
----
-
-chore: update `svelte-eslint-parser` to `1.0.0-next.10`
diff --git a/.changeset/hot-oranges-own.md b/.changeset/hot-oranges-own.md
deleted file mode 100644
index 49a4000..0000000
--- a/.changeset/hot-oranges-own.md
+++ /dev/null
@@ -1,5 +0,0 @@
----
-"eslint-plugin-svelte": major
----
-
-feat: add `*.svelte.{js,ts}` config to base config
diff --git a/.changeset/hungry-grapes-shave.md b/.changeset/hungry-grapes-shave.md
deleted file mode 100644
index 67d3457..0000000
--- a/.changeset/hungry-grapes-shave.md
+++ /dev/null
@@ -1,5 +0,0 @@
----
-'eslint-plugin-svelte': patch
----
-
-chore: update `svelte-eslint-parser` to v1.0.0-next.4
diff --git a/.changeset/itchy-dragons-boil.md b/.changeset/itchy-dragons-boil.md
deleted file mode 100644
index b87445b..0000000
--- a/.changeset/itchy-dragons-boil.md
+++ /dev/null
@@ -1,5 +0,0 @@
----
-'eslint-plugin-svelte': patch
----
-
-chore: using svelte-eslint-parser for style selector parsing
diff --git a/.changeset/lazy-eyes-wait.md b/.changeset/lazy-eyes-wait.md
deleted file mode 100644
index 74f2c86..0000000
--- a/.changeset/lazy-eyes-wait.md
+++ /dev/null
@@ -1,5 +0,0 @@
----
-'eslint-plugin-svelte': minor
----
-
-Added new `no-deprecated-raw-special-elements` rule
diff --git a/.changeset/loud-zoos-kiss.md b/.changeset/loud-zoos-kiss.md
deleted file mode 100644
index 74689a9..0000000
--- a/.changeset/loud-zoos-kiss.md
+++ /dev/null
@@ -1,5 +0,0 @@
----
-'eslint-plugin-svelte': minor
----
-
-feat: added the no-useless-children-snippet rule
diff --git a/.changeset/nice-clocks-buy.md b/.changeset/nice-clocks-buy.md
deleted file mode 100644
index e7def69..0000000
--- a/.changeset/nice-clocks-buy.md
+++ /dev/null
@@ -1,5 +0,0 @@
----
-'eslint-plugin-svelte': patch
----
-
-fix: ignore `css_unused_selector` compile error if `<style>` tag has `global` attribute
diff --git a/.changeset/nine-walls-hammer.md b/.changeset/nine-walls-hammer.md
deleted file mode 100644
index 9f45727..0000000
--- a/.changeset/nine-walls-hammer.md
+++ /dev/null
@@ -1,5 +0,0 @@
----
-'eslint-plugin-svelte': patch
----
-
-chore: run `require-event-dispatcher-types` rule on Svelte 3/4 only
diff --git a/.changeset/old-numbers-pull.md b/.changeset/old-numbers-pull.md
deleted file mode 100644
index 9bcf48b..0000000
--- a/.changeset/old-numbers-pull.md
+++ /dev/null
@@ -1,5 +0,0 @@
----
-'eslint-plugin-svelte': patch
----
-
-fix: account for \n and \r in `TemplateLiteral` handling for the `no-useless-mustaches` rule
diff --git a/.changeset/olive-melons-explain.md b/.changeset/olive-melons-explain.md
deleted file mode 100644
index 2b8259c..0000000
--- a/.changeset/olive-melons-explain.md
+++ /dev/null
@@ -1,5 +0,0 @@
----
-'eslint-plugin-svelte': major
----
-
-breaking: deprecated the no-goto-without-base rule
diff --git a/.changeset/olive-spiders-type.md b/.changeset/olive-spiders-type.md
deleted file mode 100644
index 6365268..0000000
--- a/.changeset/olive-spiders-type.md
+++ /dev/null
@@ -1,5 +0,0 @@
----
-'eslint-plugin-svelte': patch
----
-
-chore: update `svelte-eslint-parser` to `1.0.0-next.9`
diff --git a/.changeset/olive-squids-design.md b/.changeset/olive-squids-design.md
deleted file mode 100644
index 0b31d53..0000000
--- a/.changeset/olive-squids-design.md
+++ /dev/null
@@ -1,5 +0,0 @@
----
-'eslint-plugin-svelte': patch
----
-
-chore: do not run `no-reactive-functions` rule on Svelte 5 with runes
diff --git a/.changeset/olive-wolves-travel.md b/.changeset/olive-wolves-travel.md
deleted file mode 100644
index 88f7871..0000000
--- a/.changeset/olive-wolves-travel.md
+++ /dev/null
@@ -1,5 +0,0 @@
----
-"eslint-plugin-svelte": major
----
-
-feat!: drop support for old node versions (<18, 19, 21)
diff --git a/.changeset/perfect-mirrors-doubt.md b/.changeset/perfect-mirrors-doubt.md
deleted file mode 100644
index 413a80f..0000000
--- a/.changeset/perfect-mirrors-doubt.md
+++ /dev/null
@@ -1,5 +0,0 @@
----
-'eslint-plugin-svelte': patch
----
-
-chore: do not run `no-reactive-reassign` rule on Svelte 5 with runes
diff --git a/.changeset/pre.json b/.changeset/pre.json
deleted file mode 100644
index 54865cc..0000000
--- a/.changeset/pre.json
+++ /dev/null
@@ -1,55 +0,0 @@
-{
-  "mode": "pre",
-  "tag": "next",
-  "initialVersions": {
-    "eslint-plugin-svelte": "2.46.0",
-    "docs": "0.0.0"
-  },
-  "changesets": [
-    "blue-panthers-run",
-    "blue-swans-give",
-    "brown-dots-change",
-    "cold-starfishes-doubt",
-    "cool-rockets-allow",
-    "cool-steaks-happen",
-    "cool-tomatoes-fry",
-    "early-trainers-know",
-    "eighty-trainers-push",
-    "fast-coins-fly",
-    "fifty-parents-itch",
-    "gold-bottles-learn",
-    "gorgeous-cats-exercise",
-    "great-turkeys-hang",
-    "green-squids-compete",
-    "grumpy-deers-film",
-    "hip-avocados-call",
-    "hot-oranges-own",
-    "hungry-grapes-shave",
-    "itchy-dragons-boil",
-    "lazy-eyes-wait",
-    "loud-zoos-kiss",
-    "nice-clocks-buy",
-    "nine-walls-hammer",
-    "old-numbers-pull",
-    "olive-melons-explain",
-    "olive-spiders-type",
-    "olive-squids-design",
-    "olive-wolves-travel",
-    "perfect-mirrors-doubt",
-    "quiet-toys-burn",
-    "real-birds-move",
-    "red-jokes-tickle",
-    "sixty-news-look",
-    "small-days-agree",
-    "small-donuts-end",
-    "smart-apes-confess",
-    "smart-brooms-relax",
-    "soft-bears-hear",
-    "stale-bobcats-laugh",
-    "swift-clocks-turn",
-    "thick-items-retire",
-    "thin-dryers-know",
-    "twenty-nails-end",
-    "two-students-pump"
-  ]
-}
diff --git a/.changeset/quiet-toys-burn.md b/.changeset/quiet-toys-burn.md
deleted file mode 100644
index 6c7c311..0000000
--- a/.changeset/quiet-toys-burn.md
+++ /dev/null
@@ -1,5 +0,0 @@
----
-'eslint-plugin-svelte': minor
----
-
-feat: support for typescript-eslint v8 to `svelte/indent` rule
diff --git a/.changeset/real-birds-move.md b/.changeset/real-birds-move.md
deleted file mode 100644
index 82f0200..0000000
--- a/.changeset/real-birds-move.md
+++ /dev/null
@@ -1,5 +0,0 @@
----
-'eslint-plugin-svelte': major
----
-
-breaking: deprecate the `svelte/no-dynamic-slot-name` rule
diff --git a/.changeset/red-jokes-tickle.md b/.changeset/red-jokes-tickle.md
deleted file mode 100644
index ab06d7f..0000000
--- a/.changeset/red-jokes-tickle.md
+++ /dev/null
@@ -1,5 +0,0 @@
----
-'eslint-plugin-svelte': patch
----
-
-chore: update `svelte-eslint-parser` to `v1.0.0-next.12`
diff --git a/.changeset/sixty-news-look.md b/.changeset/sixty-news-look.md
deleted file mode 100644
index e70b9cd..0000000
--- a/.changeset/sixty-news-look.md
+++ /dev/null
@@ -1,5 +0,0 @@
----
-'eslint-plugin-svelte': minor
----
-
-feat: support Svelte5 of `valid-prop-names-in-kit-pages` rule
diff --git a/.changeset/small-days-agree.md b/.changeset/small-days-agree.md
deleted file mode 100644
index febff4e..0000000
--- a/.changeset/small-days-agree.md
+++ /dev/null
@@ -1,5 +0,0 @@
----
-"eslint-plugin-svelte": major
----
-
-feat!: upgrade svelte-eslint-parser to v1
diff --git a/.changeset/small-donuts-end.md b/.changeset/small-donuts-end.md
deleted file mode 100644
index ec0f648..0000000
--- a/.changeset/small-donuts-end.md
+++ /dev/null
@@ -1,5 +0,0 @@
----
-'eslint-plugin-svelte': patch
----
-
-fix: crash with eslint v9.16.0 in `svelte/no-inner-declarations`
diff --git a/.changeset/smart-apes-confess.md b/.changeset/smart-apes-confess.md
deleted file mode 100644
index 922c2c9..0000000
--- a/.changeset/smart-apes-confess.md
+++ /dev/null
@@ -1,5 +0,0 @@
----
-'eslint-plugin-svelte': patch
----
-
-chore: run the `no-inspect` rule only in Svelte 5’s runes mode
diff --git a/.changeset/smart-brooms-relax.md b/.changeset/smart-brooms-relax.md
deleted file mode 100644
index ee9ebe8..0000000
--- a/.changeset/smart-brooms-relax.md
+++ /dev/null
@@ -1,5 +0,0 @@
----
-'eslint-plugin-svelte': patch
----
-
-chore: do not run `infinite-reactive-loop` rule on Svelte 5 with runes
diff --git a/.changeset/soft-bears-hear.md b/.changeset/soft-bears-hear.md
deleted file mode 100644
index d54cb5b..0000000
--- a/.changeset/soft-bears-hear.md
+++ /dev/null
@@ -1,5 +0,0 @@
----
-'eslint-plugin-svelte': major
----
-
-feat!: Updated the `html-self-closing` rule to follow Svelte5
diff --git a/.changeset/stale-bobcats-laugh.md b/.changeset/stale-bobcats-laugh.md
deleted file mode 100644
index 8f6b5f0..0000000
--- a/.changeset/stale-bobcats-laugh.md
+++ /dev/null
@@ -1,5 +0,0 @@
----
-'eslint-plugin-svelte': patch
----
-
-chore: do not run `no-reactive-literals` rule on Svelte 5 with runes
diff --git a/.changeset/swift-clocks-turn.md b/.changeset/swift-clocks-turn.md
deleted file mode 100644
index f91bc65..0000000
--- a/.changeset/swift-clocks-turn.md
+++ /dev/null
@@ -1,5 +0,0 @@
----
-'eslint-plugin-svelte': patch
----
-
-chore: run `no-useless-children-snippet` rule on Svelte 5 only
diff --git a/.changeset/thick-items-retire.md b/.changeset/thick-items-retire.md
deleted file mode 100644
index 534550c..0000000
--- a/.changeset/thick-items-retire.md
+++ /dev/null
@@ -1,5 +0,0 @@
----
-"eslint-plugin-svelte": major
----
-
-feat!: change the plugin to an ESM-only package
diff --git a/.changeset/thin-dryers-know.md b/.changeset/thin-dryers-know.md
deleted file mode 100644
index b98f20f..0000000
--- a/.changeset/thin-dryers-know.md
+++ /dev/null
@@ -1,5 +0,0 @@
----
-'eslint-plugin-svelte': minor
----
-
-fix!: rename `no-deprecated-raw-special-elements` to `no-raw-special-elements`
diff --git a/.changeset/green-squids-compete.md b/.changeset/twelve-beers-talk.md
similarity index 54%
rename from .changeset/green-squids-compete.md
rename to .changeset/twelve-beers-talk.md
index 7666a2a..ed91087 100644
--- a/.changeset/green-squids-compete.md
+++ b/.changeset/twelve-beers-talk.md
@@ -2,4 +2,4 @@
 'eslint-plugin-svelte': minor
 ---
 
-Add `prefer-const` rule
+feat: add `no-unused-props` rule
diff --git a/.changeset/twenty-nails-end.md b/.changeset/twenty-nails-end.md
deleted file mode 100644
index 9efebd7..0000000
--- a/.changeset/twenty-nails-end.md
+++ /dev/null
@@ -1,5 +0,0 @@
----
-'eslint-plugin-svelte': minor
----
-
-feat(no-inline-styles): allowing transitions by default
diff --git a/.changeset/two-students-pump.md b/.changeset/two-students-pump.md
deleted file mode 100644
index c0e36c9..0000000
--- a/.changeset/two-students-pump.md
+++ /dev/null
@@ -1,5 +0,0 @@
----
-'eslint-plugin-svelte': patch
----
-
-fix: update method for extracting major version
diff --git a/.github/workflows/pkg.pr.new-comment.yml b/.github/workflows/pkg.pr.new-comment.yml
index 8ac14fa..277168f 100644
--- a/.github/workflows/pkg.pr.new-comment.yml
+++ b/.github/workflows/pkg.pr.new-comment.yml
@@ -6,6 +6,10 @@ on:
     types:
       - completed
 
+permissions:
+  issues: write
+  pull-requests: write
+
 jobs:
   build:
     if: github.repository == 'sveltejs/eslint-plugin-svelte'
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 4c273be..1f1d954 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -1,6 +1,6 @@
 # Contributing
 
-Thanks for contributing!
+Thank you for contributing!
 
 ## Installation
 
@@ -10,62 +10,60 @@ cd eslint-plugin-svelte
 pnpm install
 ```
 
-## Running the tests
+## Running Tests
 
 ```sh
+cd packages/eslint-plugin-svelte
 pnpm run test
 ```
 
-To run the tests with debugging log, you can use `pnpm run test:debug`.
+This is an [ESLint](http://eslint.org) plugin. See ESLint's [Working with Plugins](http://eslint.org/docs/developer-guide/working-with-plugins) for API details.
 
-This is an [ESLint](http://eslint.org) plugin. Documentation for the APIs that it uses can be found on ESLint's [Working with Plugins](http://eslint.org/docs/developer-guide/working-with-plugins) page.
-
-This plugin is used to lint itself. The style is checked when `pnpm run lint` is run, and the build will fail if there are any linting errors. You can use `pnpm run lint-fix` to fix some linting errors.
+The plugin lints itself. Running `pnpm run lint` checks the style and will fail the build if there are lint errors. Use `pnpm run lint-fix` to automatically fix some issues.
 
 ## Other Development Tools
 
-- `pnpm run test` runs tests.
-- `pnpm run cover` runs tests and measures coverage.
-- `pnpm run new -- [new-rule-name]` generate the files needed to implement the new rule.
-- `pnpm run update` runs in order to update readme and recommended configuration.
-- `pnpm run docs:watch` launch the document site in development mode.
+- `pnpm run test` – runs tests.
+- `pnpm run new -- [new-rule-name]` – generates files for a new rule.
+- `pnpm run update` – updates the README and recommended configuration.
 
-## Test the Rule
+## Rule Testing
 
-Rule testing almost always uses fixtures.  
-For example, for an `indent` rule, the `.ts` file that runs the test is `tests/src/rules/indent.ts` and the fixture is in `tests/fixtures/rules/indent`.  
-The fixture directory has an `invalid` directory and a `valid` directory.
+Rule tests typically use fixtures. For example, for the `indent` rule, the test file is `tests/src/rules/indent.ts` and the fixtures are in `tests/fixtures/rules/indent`, which contains `invalid` and `valid` directories.
 
-- The `invalid` directory contains test cases where the rule reports problems.
-- The `valid` directory contains test cases where the rule does not report a problem.
+- The `invalid` directory contains test cases where the rule should report errors.
+- The `valid` directory contains test cases where no errors are reported.
 
-The fixture input file should be named `*-input.svelte`. It is automatically collected and tested.  
-If your test requires configuration, you need to add a json file with the configuration.
+Fixture input files should be named `*-input.svelte` and are automatically collected.  
+If configuration is needed, include a JSON file:
 
-- If you want to apply a configuration to `my-test-input.svelte`, add `my-test-config.json`.
-- If you want to apply the same configuration to all the fixtures in that directory, add `_config.json`.
+- For a specific test file (e.g., `my-test-input.svelte`), add `my-test-config.json`.
+- For all fixtures in a directory, add `_config.json`.
 
-To verify the output of invalid test cases requires `*-errors.json`, and `*-output.svelte` (for auto-fix). However, you don't have to add them yourself. If they do not exist, they will be automatically generated when you run the test. In other words, delete them manually when you want to recreate them.
+Verifying output for invalid tests requires `*-errors.json` and `*-output.svelte` (for auto-fix). These files are auto-generated if missing—delete them to recreate.
 
 **Tips**:
 
-If you want to test only one rule, run the following command (for `indent` rule):
+To test only one rule (e.g., `indent`), run:
 
 ```sh
 pnpm run test -- -g indent
 ```
 
-Take <https://stackoverflow.com/questions/10832031/how-to-run-a-single-test-with-mocha> as reference for details.
+Refer to [this Stack Overflow post](https://stackoverflow.com/questions/10832031/how-to-run-a-single-test-with-mocha) for details.
 
-If you want to test only `my-test-input.svelte`, add `my-test-config.json` and save `{"only": true}`.  
-(Note that `{"only": true}` must be removed before making a pull request.)
+To test a single file (e.g., `my-test-input.svelte`), add a `my-test-config.json` with `{"only": true}`.  
+(Remember to remove `{"only": true}` before submitting a pull request.)
 
-## Commit messages
+## Preview Docs
 
-Please view [commitlint](https://commitlint.js.org) and [@commitlint/config-conventional](https://github.com/conventional-changelog/commitlint/tree/master/%40commitlint/config-conventional) for more details.
+```sh
+cd docs-svelte-kit
+pnpm run build && pnpm run preview
+```
 
 ## Publishing
 
-We're using [changesets](https://github.com/changesets/changesets) for version management, CHANGELOG and releasing automatically.
+We use [changesets](https://github.com/changesets/changesets) for version management, changelog generation, and automated releases.
 
-Please view [changesets-bot](https://github.com/apps/changeset-bot) and its [action](https://github.com/changesets/action) for more details.
+For more details, see [changesets-bot](https://github.com/apps/changeset-bot) and its [action](https://github.com/changesets/action).
diff --git a/README.md b/README.md
index 01eb6bd..a097287 100644
--- a/README.md
+++ b/README.md
@@ -1,14 +1,4 @@
-# Introduction
-
-`eslint-plugin-svelte` is the official [ESLint] plugin for [Svelte].  
-It provides many unique check rules by using the template AST.  
-You can check on the [Online DEMO](https://eslint-online-playground.netlify.app/#eslint-plugin-svelte%20with%20typescript).
-
-> [!NOTE]
-> This document is in development.\
-> Please refer to the document for the version you are using.\
-> For example, <https://github.com/sveltejs/eslint-plugin-svelte/blob/eslint-plugin-svelte%402.46.0/README.md>
-> and <https://github.com/sveltejs/eslint-plugin-svelte/blob/eslint-plugin-svelte%402.46.0/docs>
+<!--DOCS_IGNORE_START-->
 
 [![NPM license](https://img.shields.io/npm/l/eslint-plugin-svelte.svg)](https://www.npmjs.com/package/eslint-plugin-svelte)
 [![NPM version](https://img.shields.io/npm/v/eslint-plugin-svelte.svg)](https://www.npmjs.com/package/eslint-plugin-svelte)
@@ -24,209 +14,197 @@ You can check on the [Online DEMO](https://eslint-online-playground.netlify.app/
 [![Code Style: Prettier](https://img.shields.io/badge/code_style-prettier-ff69b4.svg)](https://github.com/prettier/prettier)
 [![changesets](https://img.shields.io/badge/maintained%20with-changesets-176de3.svg)](https://github.com/atlassian/changesets)
 
-## :name_badge: What is this plugin?
+<div align="center">
 
-[ESLint] plugin for [Svelte].  
-It provides many unique check rules using the AST generated by [svelte-eslint-parser].
+# eslint-plugin-svelte
 
-### ❗ Attention
+## ESLint plugin for Svelte using AST
 
-#### Cannot be used with eslint-plugin-svelte3
+[Live Demo](https://eslint-online-playground.netlify.app/#eslint-plugin-svelte%20with%20typescript) •
+[Documentation](https://sveltejs.github.io/eslint-plugin-svelte/) •
+[Discord](https://svelte.dev/chat)
 
-The [svelte-eslint-parser] and the `eslint-plugin-svelte` can not be used with the [eslint-plugin-svelte3].
+</div>
 
-[svelte-eslint-parser]: https://github.com/sveltejs/svelte-eslint-parser
-[eslint-plugin-svelte3]: https://github.com/sveltejs/eslint-plugin-svelte3
-
-#### Experimental support for Svelte v5
-
-We are working on support for Svelte v5, but it is still an experimental feature. Please note that rules and features related to Svelte v5 may be changed or removed in minor versions without notice.
-
-<!--DOCS_IGNORE_START-->
-
-## Versioning policy
-
-This plugin follows [Semantic Versioning](https://semver.org/).  
-However, unlike [ESLint’s Semantic Versioning Policy](https://github.com/eslint/eslint#semantic-versioning-policy), this plugin adds new rules to its configs even in minor releases. For example, if you are using the recommended config, a minor update may add new rules, which could cause new lint errors in your project.  
-While [ESLint’s Semantic Versioning Policy](https://github.com/eslint/eslint#semantic-versioning-policy) only adds new rules to configs in major releases, most users (myself included) don’t regularly monitor new rules. This makes it challenging to manually add them to projects whenever they are introduced.  
-By adding new rules to configs in minor releases, this plugin ensures users can adopt them more easily. If any new rules cause issues, you can simply disable them. I believe this approach helps maintain and improve code quality with minimal effort.
-
-## Migration Guide
+<!--DOCS_IGNORE_END-->
 
-To migrate from `eslint-plugin-svelte` v1, or [`@ota-meshi/eslint-plugin-svelte`](https://www.npmjs.com/package/@ota-meshi/eslint-plugin-svelte), please refer to the [migration guide](https://sveltejs.github.io/eslint-plugin-svelte/migration/).
+## Introduction
 
-## :book: Documentation
+`eslint-plugin-svelte` is the official [ESLint](https://eslint.org/) plugin for [Svelte](https://svelte.dev/).  
+It leverages the AST generated by [svelte-eslint-parser](https://github.com/sveltejs/svelte-eslint-parser) to provide custom linting for Svelte.  
+Note that `eslint-plugin-svelte` and `svelte-eslint-parser` cannot be used alongside [eslint-plugin-svelte3](https://github.com/sveltejs/eslint-plugin-svelte3).
 
-See [documents](https://sveltejs.github.io/eslint-plugin-svelte/).
+<!--USAGE_SECTION_START-->
+<!--USAGE_GUIDE_START-->
 
-## :cd: Installation
+## Installation
 
 ```bash
-npm install --save-dev eslint eslint-plugin-svelte svelte
+npm install --save-dev svelte eslint eslint-plugin-svelte globals
 ```
 
-> **Requirements**
+> [!NOTE]
 >
-> - ESLint v8.57.1, v9.0.0 and above
-> - Node.js v18.20.4, v20.18.0, v22.10.0 and above
-
-<!--DOCS_IGNORE_END-->
+> **Requirements:**
+>
+> - ESLint v8.57.1, v9.0.0, and above
+> - Node.js v18.20.4, v20.18.0, v22.10.0, and above
 
-## :book: Usage
+## Usage
 
-<!--USAGE_SECTION_START-->
-<!--USAGE_GUIDE_START-->
+Use the `eslint.config.js` file to configure rules. For more details, see the [ESLint documentation](https://eslint.org/docs/latest/use/configure/configuration-files-new).
 
 ### Configuration
 
-Use `eslint.config.js` file to configure rules. See also: <https://eslint.org/docs/latest/use/configure/configuration-files-new>.
-
-Example **eslint.config.js**:
+#### JavaScript project
 
 ```js
-import eslintPluginSvelte from 'eslint-plugin-svelte';
-export default [
-  // add more generic rule sets here, such as:
-  // js.configs.recommended,
-  ...eslintPluginSvelte.configs.recommended,
-  {
-    rules: {
-      // override/add rules settings here, such as:
-      // 'svelte/rule-name': 'error'
-    }
-  }
-];
-```
-
-This plugin provides configs:
-
-- `eslintPluginSvelte.configs.base` ... Configuration to enable correct Svelte parsing.
-- `eslintPluginSvelte.configs.recommended` ... Above, plus rules to prevent errors or unintended behavior.
-- `eslintPluginSvelte.configs.prettier` ... Turns off rules that may conflict with [Prettier](https://prettier.io/) (You still need to configure prettier to work with svelte yourself, for example by using [prettier-plugin-svelte](https://github.com/sveltejs/prettier-plugin-svelte).).
-- `eslintPluginSvelte.configs.all` ... All rules. This configuration is not recommended for production use because it changes with every minor and major version of `eslint-plugin-svelte`. Use it at your own risk.
-
-See [the rule list](https://sveltejs.github.io/eslint-plugin-svelte/rules/) to get the `rules` that this plugin provides.
-
-#### Parser Configuration
-
-If you have specified a parser, you need to configure a parser for `.svelte`.
-
-For example, if you are using the `"@typescript-eslint/parser"`, and if you want to use TypeScript in `<script>` of `.svelte`, you need to add more `parserOptions` configuration.
+// eslint.config.js
+import js from '@eslint/js';
+import svelte from 'eslint-plugin-svelte';
+import globals from 'globals';
+import svelteConfig from './svelte.config.js';
 
-```js
-import eslintPluginSvelte from 'eslint-plugin-svelte';
-import * as svelteParser from 'svelte-eslint-parser';
-import * as typescriptParser from '@typescript-eslint/parser';
+/** @type {import('eslint').Linter.Config[]} */
 export default [
-  ...js.configs.recommended,
-  ...eslintPluginSvelte.configs.recommended,
+  js.configs.recommended,
+  ...svelte.configs.recommended,
   {
-    files: ['**/*.svelte'],
     languageOptions: {
-      parser: svelteParser,
-      parserOptions: {
-        parser: typescriptParser,
-        project: './path/to/your/tsconfig.json',
-        extraFileExtensions: ['.svelte']
+      globals: {
+        ...globals.browser,
+        ...globals.node // Add this if you are using SvelteKit in non-SPA mode
       }
     }
-  }
-];
-```
-
-If you have a mix of TypeScript and JavaScript in your project, use a multiple parser configuration.
-
-```js
-import eslintPluginSvelte from 'eslint-plugin-svelte';
-import * as svelteParser from 'svelte-eslint-parser';
-import * as typescriptParser from '@typescript-eslint/parser';
-import espree from 'espree';
-export default [
-  ...js.configs.recommended,
-  ...eslintPluginSvelte.configs.recommended,
+  },
   {
-    files: ['**/*.svelte'],
+    files: ['**/*.svelte', '**/*.svelte.js'],
     languageOptions: {
-      parser: svelteParser,
       parserOptions: {
-        parser: {
-          // Specify a parser for each lang.
-          ts: typescriptParser,
-          js: espree,
-          typescript: typescriptParser
-        },
-        project: './path/to/your/tsconfig.json',
-        extraFileExtensions: ['.svelte']
+        // We recommend importing and specifying svelte.config.js.
+        // By doing so, some rules in eslint-plugin-svelte will automatically read the configuration and adjust their behavior accordingly.
+        // While certain Svelte settings may be statically loaded from svelte.config.js even if you don’t specify it,
+        // explicitly specifying it ensures better compatibility and functionality.
+        svelteConfig
       }
     }
+  },
+  {
+    rules: {
+      // Override or add rule settings here, such as:
+      // 'svelte/rule-name': 'error'
+    }
   }
 ];
 ```
 
-See also <https://github.com/sveltejs/svelte-eslint-parser#readme>.
+#### TypeScript project
 
-::: warning ❗ Attention
-
-The TypeScript parser uses a singleton internally and it will only use the
-options given to it when it was first initialized. If trying to change the
-options for a different file or override, the parser will simply ignore the new
-options (which may result in an error). See
-[typescript-eslint/typescript-eslint#6778](https://github.com/typescript-eslint/typescript-eslint/issues/6778)
-for some context.
-
-:::
-
-#### Specify `svelte.config.js`
-
-If you are using `eslint.config.js`, we recommend that you import and specify `svelte.config.js`.
-By specifying it, some rules of `eslint-plugin-svelte` will read it and try to behave well for you by default.
-Some Svelte configurations will be statically loaded from `svelte.config.js` even if you don't specify it, but you need to specify it to make it work better.
-
-Example **eslint.config.js**:
+```shell
+npm install --save-dev typescript-eslint
+```
 
 ```js
-import eslintPluginSvelte from 'eslint-plugin-svelte';
+// eslint.config.js
+import js from '@eslint/js';
+import svelte from 'eslint-plugin-svelte';
+import globals from 'globals';
+import ts from 'typescript-eslint';
 import svelteConfig from './svelte.config.js';
-export default [
-  ...eslintPluginSvelte.configs.recommended,
+
+export default ts.config(
+  js.configs.recommended,
+  ...ts.configs.recommended,
+  ...svelte.configs.recommended,
+  {
+    languageOptions: {
+      globals: {
+        ...globals.browser,
+        ...globals.node
+      }
+    }
+  },
   {
-    files: [
-      '**/*.svelte',
-      '*.svelte'
-      // Add more files if you need.
-      // '**/*.svelte.ts', '*.svelte.ts', '**/*.svelte.js', '*.svelte.js',
-    ],
+    files: ['**/*.svelte', '**/*.svelte.ts', '**/*.svelte.js'],
+    // See more details at: https://typescript-eslint.io/packages/parser/
     languageOptions: {
       parserOptions: {
-        // Specify the `svelte.config.js`.
+        projectService: true,
+        extraFileExtensions: ['.svelte'], // Add support for additional file extensions, such as .svelte
+        parser: ts.parser,
+        // Specify a parser for each language, if needed:
+        // parser: {
+        //   ts: ts.parser,
+        //   js: espree,    // Use espree for .js files (add: import espree from 'espree')
+        //   typescript: ts.parser
+        // },
+
+        // We recommend importing and specifying svelte.config.js.
+        // By doing so, some rules in eslint-plugin-svelte will automatically read the configuration and adjust their behavior accordingly.
+        // While certain Svelte settings may be statically loaded from svelte.config.js even if you don’t specify it,
+        // explicitly specifying it ensures better compatibility and functionality.
         svelteConfig
       }
     }
+  },
+  {
+    rules: {
+      // Override or add rule settings here, such as:
+      // 'svelte/rule-name': 'error'
+    }
   }
-];
+);
 ```
 
-#### settings.svelte
+> [!WARNING]
+> The TypeScript parser uses a singleton internally, meaning it only respects the options provided during its initial initialization.
+> If you try to change the options for a different file or override them later, the parser will ignore the new options, which may lead to errors.
+> For more context, see [typescript-eslint/typescript-eslint#6778](https://github.com/typescript-eslint/typescript-eslint/issues/6778).
+
+### Available Configurations
+
+This plugin provides the following configurations:
 
-You can change the behavior of this plugin with some settings.
+- **`eslintPluginSvelte.configs.base`** ... Enables correct Svelte parsing.
+- **`eslintPluginSvelte.configs.recommended`** ... Includes `base` configuration, plus rules to prevent errors or unintended behavior.
+- **`eslintPluginSvelte.configs.prettier`** ... Disables rules that may conflict with [Prettier](https://prettier.io/). You still need to configure Prettier to work with Svelte manually, for example, by using [prettier-plugin-svelte](https://github.com/sveltejs/prettier-plugin-svelte).
+- **`eslintPluginSvelte.configs.all`** ... Includes all available rules. **Note:** This configuration is not recommended for production use, as it changes with every minor and major version of `eslint-plugin-svelte`. Use at your own risk.
 
-e.g.
+For more details, see [the rule list](https://sveltejs.github.io/eslint-plugin-svelte/rules/) to explore the rules provided by this plugin.
+
+### settings.svelte
+
+You can customize the behavior of this plugin using specific settings.
 
 ```js
+// eslint.config.js
 export default [
   // ...
   {
     settings: {
       svelte: {
+        // Specifies an array of rules to ignore reports within the template.
+        // For example, use this to disable rules in the template that may produce unavoidable false positives.
         ignoreWarnings: [
           '@typescript-eslint/no-unsafe-assignment',
           '@typescript-eslint/no-unsafe-member-access'
         ],
+        // Specifies options for Svelte compilation.
+        // This affects rules that rely on Svelte compilation,
+        // such as svelte/valid-compile and svelte/no-unused-svelte-ignore.
+        // Note that this setting does not impact ESLint’s custom parser.
         compileOptions: {
+          // Specifies options related to PostCSS. You can disable the PostCSS processing by setting it to false.
           postcss: {
+            // Specifies the path to the directory that contains the PostCSS configuration.
             configFilePath: './path/to/my/postcss.config.js'
           }
         },
+        // Even if settings.svelte.kit is not specified, the rules will attempt to load information from svelte.config.js.
+        // However, if the default behavior does not work as expected, you should specify settings.svelte.kit explicitly.
+        // If you are using SvelteKit with a non-default configuration, you need to set the following options.
+        // The schema is a subset of SvelteKit’s configuration, so refer to the SvelteKit documentation for more details.
+        // https://svelte.dev/docs/kit/configuration
         kit: {
           files: {
             routes: 'src/routes'
@@ -239,59 +217,11 @@ export default [
 ];
 ```
 
-#### settings.svelte.ignoreWarnings
-
-Specifies an array of rules that ignore reports in the template.  
-For example, set rules on the template that cannot avoid false positives.
-
-#### settings.svelte.compileOptions
-
-Specifies options for Svelte compile. Effects rules that use Svelte compile. The target rules are [svelte/valid-compile](https://sveltejs.github.io/eslint-plugin-svelte/rules/valid-compile/) and [svelte/no-unused-svelte-ignore](https://sveltejs.github.io/eslint-plugin-svelte/rules/no-unused-svelte-ignore/). **Note that it has no effect on ESLint's custom parser**.
-
-- `postcss` ... Specifies options related to PostCSS. You can disable the PostCSS process by specifying `false`.
-  - `configFilePath` ... Specifies the path of the directory containing the PostCSS configuration.
-
-#### settings.svelte.kit
-
-::: warning
-
-Even if you don't specify `settings.svelte.kit`, the rules will try to load information from `svelte.config.js`, so specify `settings.svelte.kit` if the default doesn't work.
-
-:::
-
-If you use SvelteKit with not default configuration, you need to set below configurations.
-The schema is subset of SvelteKit's configuration.
-Therefore please check [SvelteKit docs](https://kit.svelte.dev/docs/configuration) for more details.
-
-e.g.
-
-```js
-export default [
-  // ...
-  {
-    settings: {
-      svelte: {
-        kit: {
-          files: {
-            routes: 'src/routes'
-          }
-        }
-      }
-    }
-  }
-  // ...
-];
-```
-
-## :computer: Editor Integrations
-
-### Visual Studio Code
+## Editor Integrations
 
-Use the [dbaeumer.vscode-eslint](https://marketplace.visualstudio.com/items?itemName=dbaeumer.vscode-eslint) extension that Microsoft provides officially.
-
-You have to configure the `eslint.validate` option of the extension to check `.svelte` files, because the extension targets only `*.js` or `*.jsx` files by default.
-
-Example **.vscode/settings.json**:
+**Visual Studio Code**  
+Install [dbaeumer.vscode-eslint](https://marketplace.visualstudio.com/items?itemName=dbaeumer.vscode-eslint).  
+Configure `.svelte` files in `.vscode/settings.json`:
 
 ```json
 {
@@ -302,7 +232,17 @@ Example **.vscode/settings.json**:
 <!--USAGE_GUIDE_END-->
 <!--USAGE_SECTION_END-->
 
-## :white_check_mark: Rules
+## Migration Guide
+
+If you’re migrating from `eslint-plugin-svelte` v1 or [`@ota-meshi/eslint-plugin-svelte`](https://www.npmjs.com/package/@ota-meshi/eslint-plugin-svelte), see the [migration guide](https://sveltejs.github.io/eslint-plugin-svelte/migration/).
+
+## Versioning Policy
+
+This project follows [Semantic Versioning](https://semver.org/). Unlike [ESLint’s versioning policy](https://github.com/eslint/eslint#semantic-versioning-policy), new rules may be added to the recommended config in minor releases. If these rules cause unwanted warnings, you can disable them.
+
+<!--DOCS_IGNORE_END-->
+
+## Rules
 
 <!-- prettier-ignore-start -->
 <!--RULES_SECTION_START-->
@@ -335,6 +275,7 @@ These rules relate to possible syntax or logic errors in Svelte code:
 | [svelte/require-store-callbacks-use-set-param](https://sveltejs.github.io/eslint-plugin-svelte/rules/require-store-callbacks-use-set-param/) | store callbacks must use `set` param |  |
 | [svelte/require-store-reactive-access](https://sveltejs.github.io/eslint-plugin-svelte/rules/require-store-reactive-access/) | disallow to use of the store itself as an operand. Need to use $ prefix or get function. | :star::wrench: |
 | [svelte/valid-compile](https://sveltejs.github.io/eslint-plugin-svelte/rules/valid-compile/) | disallow warnings when compiling. |  |
+| [svelte/valid-style-parse](https://sveltejs.github.io/eslint-plugin-svelte/rules/valid-style-parse/) | require valid style element parsing |  |
 
 ## Security Vulnerability
 
@@ -362,6 +303,7 @@ These rules relate to better ways of doing things to help you avoid problems:
 | [svelte/no-reactive-literals](https://sveltejs.github.io/eslint-plugin-svelte/rules/no-reactive-literals/) | don't assign literal values in reactive statements | :star::bulb: |
 | [svelte/no-svelte-internal](https://sveltejs.github.io/eslint-plugin-svelte/rules/no-svelte-internal/) | svelte/internal will be removed in Svelte 6. | :star: |
 | [svelte/no-unused-class-name](https://sveltejs.github.io/eslint-plugin-svelte/rules/no-unused-class-name/) | disallow the use of a class in the template without a corresponding style |  |
+| [svelte/no-unused-props](https://sveltejs.github.io/eslint-plugin-svelte/rules/no-unused-props/) | Warns about defined Props properties that are unused | :star: |
 | [svelte/no-unused-svelte-ignore](https://sveltejs.github.io/eslint-plugin-svelte/rules/no-unused-svelte-ignore/) | disallow unused svelte-ignore comments | :star: |
 | [svelte/no-useless-children-snippet](https://sveltejs.github.io/eslint-plugin-svelte/rules/no-useless-children-snippet/) | disallow explicit children snippet where it's not needed | :star: |
 | [svelte/no-useless-mustaches](https://sveltejs.github.io/eslint-plugin-svelte/rules/no-useless-mustaches/) | disallow unnecessary mustache interpolations | :star::wrench: |
@@ -370,7 +312,7 @@ These rules relate to better ways of doing things to help you avoid problems:
 | [svelte/require-each-key](https://sveltejs.github.io/eslint-plugin-svelte/rules/require-each-key/) | require keyed `{#each}` block | :star: |
 | [svelte/require-event-dispatcher-types](https://sveltejs.github.io/eslint-plugin-svelte/rules/require-event-dispatcher-types/) | require type parameters for `createEventDispatcher` | :star: |
 | [svelte/require-optimized-style-attribute](https://sveltejs.github.io/eslint-plugin-svelte/rules/require-optimized-style-attribute/) | require style attributes that can be optimized |  |
-| [svelte/require-stores-init](https://sveltejs.github.io/eslint-plugin-svelte/rules/require-stores-init/) | require initial value in store | :star: |
+| [svelte/require-stores-init](https://sveltejs.github.io/eslint-plugin-svelte/rules/require-stores-init/) | require initial value in store |  |
 | [svelte/valid-each-key](https://sveltejs.github.io/eslint-plugin-svelte/rules/valid-each-key/) | enforce keys to use variables defined in the `{#each}` block | :star: |
 
 ## Stylistic Issues
@@ -453,23 +395,13 @@ These rules relate to this plugin works:
 
 <!--DOCS_IGNORE_START-->
 
-## :beers: Contributing
-
-Welcome contributing!
-
-Please use GitHub's Issues/PRs.
+## Contributing
 
-See also [CONTRIBUTING.md](./CONTRIBUTING.md)
-
-### Working With Rules
-
-This plugin uses [svelte-eslint-parser](https://github.com/sveltejs/svelte-eslint-parser) for the parser. Check [here](https://sveltejs.github.io/svelte-eslint-parser/) to find out about AST.
+Contributions are welcome! Please open an issue or submit a PR. For more details, see [CONTRIBUTING.md](./CONTRIBUTING.md).  
+Refer to [svelte-eslint-parser](https://github.com/sveltejs/svelte-eslint-parser) for AST details.
 
 <!--DOCS_IGNORE_END-->
 
-## :lock: License
-
-See the [LICENSE](LICENSE) file for license rights and limitations (MIT).
+## License
 
-[svelte]: https://svelte.dev/
-[eslint]: https://eslint.org/
+See [LICENSE](LICENSE) (MIT).
diff --git a/docs-svelte-kit/package.json b/docs-svelte-kit/package.json
index e6fb687..6ea86fb 100644
--- a/docs-svelte-kit/package.json
+++ b/docs-svelte-kit/package.json
@@ -16,8 +16,8 @@
     "@babel/types": "^7.26.0",
     "@fontsource/fira-mono": "^5.1.0",
     "@ota-meshi/eslint-plugin": "^0.17.6",
-    "@shikijs/markdown-it": "^2.0.0",
-    "@shikijs/twoslash": "^2.0.0",
+    "@shikijs/markdown-it": "^3.0.0",
+    "@shikijs/twoslash": "^3.0.0",
     "@sindresorhus/slugify": "^2.2.1",
     "@sveltejs/adapter-static": "^3.0.6",
     "@sveltejs/kit": "^2.8.5",
@@ -45,7 +45,7 @@
     "eslint-scope": "^8.2.0",
     "eslint-visitor-keys": "^4.2.0",
     "espree": "^10.3.0",
-    "globals": "^15.12.0",
+    "globals": "^16.0.0",
     "markdown-it-anchor": "^9.2.0",
     "markdown-it-container": "^4.0.0",
     "markdown-it-emoji": "^3.0.0",
@@ -58,8 +58,8 @@
     "stylus": "^0.64.0",
     "svelte": "^5.2.10",
     "svelte-adapter-ghpages": "0.2.2",
-    "twoslash-eslint": "^0.2.12",
-    "twoslash-protocol": "^0.2.12",
+    "twoslash-eslint": "^0.3.0",
+    "twoslash-protocol": "^0.3.0",
     "util": "^0.12.5",
     "vite": "^6.0.1",
     "vite-plugin-svelte-md": "^0.1.7"
diff --git a/docs-svelte-kit/src/app.css b/docs-svelte-kit/src/app.css
index 7ab76d3..f8a94c4 100644
--- a/docs-svelte-kit/src/app.css
+++ b/docs-svelte-kit/src/app.css
@@ -1,105 +1,226 @@
-@import "@fontsource/fira-mono";
+@import '@fontsource/fira-mono';
+@import 'prismjs/themes/prism-tomorrow';
+@import '@shikijs/twoslash/style-rich.css';
 
 :root {
-  font-family: Arial, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
-    Oxygen, Ubuntu, Cantarell, "Open Sans", "Helvetica Neue", sans-serif;
-  --font-mono: "Fira Mono", monospace;
-  --pure-white: #ffffff;
-  --primary-color: #b9c6d2;
-  --secondary-color: #676778;
-  --tertiary-color: #edf0f8;
-  --accent-color: #ff3e00;
-  --heading-color: rgba(0, 0, 0, 0.7);
-  --text-color: #444444;
-  --background-without-opacity: rgba(255, 255, 255, 0.7);
-  --column-width: 42rem;
-  --column-margin-top: 4rem;
+	font-family: 'Inter', 'Helvetica Neue', 'Arial', sans-serif;
+	--font-mono: 'Fira Mono', monospace;
+	--pure-white: #ffffff;
+	--primary-color: #ffffff;
+	--secondary-color: #676778;
+	--tertiary-color: #edf0f8;
+	--accent-color: #ff3e00;
+	--heading-color: rgba(0, 0, 0, 0.7);
+	--text-color: #444444;
+	--background-without-opacity: rgba(255, 255, 255, 0.95);
+	--column-width: 42rem;
+	--column-margin-top: 4rem;
 }
 
 body {
-  min-height: 100vh;
-  margin: 0;
-  background-color: var(--primary-color);
-}
-
-body::before {
-  content: "";
-  width: 80vw;
-  height: 100vh;
-  position: absolute;
-  top: 0;
-  left: 10vw;
-  z-index: -1;
-  background: radial-gradient(
-    50% 50% at 50% 50%,
-    var(--pure-white) 0%,
-    rgba(255, 255, 255, 0) 100%
-  );
-  opacity: 0.05;
+	min-height: 100vh;
+	margin: 0;
+	background-color: var(--primary-color);
+	color: var(--text-color);
 }
 
 #svelte {
-  min-height: 100vh;
-  display: flex;
-  flex-direction: column;
+	min-height: 100vh;
+	display: flex;
+	flex-direction: column;
 }
 
 h1,
 h2,
 p {
-  font-weight: 400;
-  color: var(--heading-color);
+	font-weight: 400;
+	color: var(--heading-color);
 }
 
 p {
-  line-height: 1.5;
+	line-height: 1.5;
 }
 
 a {
-  color: var(--accent-color);
-  text-decoration: none;
+	color: var(--accent-color);
+	text-decoration: none;
 }
-
 a:hover {
-  text-decoration: underline;
+	text-decoration: underline;
 }
 
 h1 {
-  font-size: 2rem;
-  text-align: center;
+	font-size: 2rem;
+	margin: 1rem 0;
+}
+@media (min-width: 720px) {
+	h1 {
+		font-size: 2.4rem;
+	}
 }
 
 h2 {
-  font-size: 1rem;
+	font-size: 1.6rem;
+	margin: 1rem 0;
+}
+
+h3,
+h4,
+h5,
+h6 {
+	margin: 0.5rem 0;
+	font-weight: 400;
+	color: var(--heading-color);
+	font-size: 1rem;
+}
+
+h1 + h2,
+h2 + h3,
+h3 + h4,
+h4 + h5,
+h5 + h6 {
+	margin-top: 0;
+}
+
+ul {
+	margin: 1rem 0;
 }
 
 pre {
-  font-size: 16px;
-  font-family: var(--font-mono);
-  background-color: rgba(255, 255, 255, 0.45);
-  border-radius: 3px;
-  box-shadow: 2px 2px 6px rgb(255 255 255 / 25%);
-  padding: 0.5em;
-  overflow-x: auto;
-  color: var(--text-color);
+	font-size: 16px;
+	font-family: var(--font-mono);
+	background-color: rgba(255, 255, 255, 0.45);
+	border-radius: 3px;
+	box-shadow: 2px 2px 6px rgb(255 255 255 / 25%);
+	padding: 0.5em;
+	overflow-x: auto;
+	color: var(--text-color);
+	margin: 0.5rem 0;
 }
 
 input,
 button {
-  font-size: inherit;
-  font-family: inherit;
+	font-size: inherit;
+	font-family: inherit;
 }
 
 button:focus:not(:focus-visible) {
-  outline: none;
+	outline: none;
 }
 
-@media (min-width: 720px) {
-  h1 {
-    font-size: 2.4rem;
-  }
+:target {
+	scroll-margin-top: 80px;
 }
 
-:target {
-  scroll-margin-top: 80px;
+/* twoslash */
+.twoslash .twoslash-popup-container {
+	transform: translateY(2em);
+	white-space: pre-wrap;
+	margin-right: 32px;
+}
+.twoslash .twoslash-error > span:not(.twoslash-popup-container):not(:has(*)) {
+	min-width: 6px;
+	display: inline-block;
+}
+
+/*** markdown ***/
+:not(pre) > code {
+	padding: 0.1rem 0.4rem;
+	margin: 0 0.2rem;
+	background: #e5eef5;
+	position: relative;
+	border-radius: 0.3em;
+	white-space: nowrap;
+	color: #444;
+	-webkit-font-smoothing: initial;
+	transform: translateY(-2px);
+	display: inline-block;
+}
+
+blockquote {
+	padding: 0.5rem 2.4rem;
+	color: #09f;
+	border: 1px solid #40b3ff;
+	margin: 1.6rem 2.4rem 2.4rem;
+	border-radius: 0.4rem;
+}
+blockquote p {
+	color: #09f;
+}
+
+table {
+	margin: 0 0 2em;
+	width: 100%;
+	font-size: 1rem; /* var(--h5)は未定義なので1remに */
+}
+td,
+th {
+	text-align: left;
+	border-bottom: 1px solid rgba(0 0 0 / 0.1);
+	padding: 0.4rem 0.8rem 0.4rem 0;
+}
+
+/* custom container */
+.custom-block .custom-block-title {
+	font-weight: 600;
+	margin-bottom: -0.4rem;
+}
+
+.custom-block.danger,
+.custom-block.tip,
+.custom-block.warning {
+	padding: 0.1rem 1.5rem;
+	border-left-width: 4px;
+	border-left-style: solid;
+	margin: 1rem 0;
+}
+
+.custom-block.tip {
+	background-color: #f3f5f7;
+	border-color: #42b983;
+}
+
+.custom-block.warning {
+	background-color: rgba(255, 229, 100, 0.3);
+	border-color: #e7c000;
+	color: #6b5900;
+}
+.custom-block.warning .custom-block-title {
+	color: #b29400;
+}
+.custom-block.warning a {
+	color: #2c3e50;
+}
+
+.custom-block.danger {
+	background-color: #ffe6e6;
+	border-color: #c00;
+	color: #4d0000;
+}
+.custom-block.danger .custom-block-title {
+	color: #900;
+}
+.custom-block.danger a {
+	color: #2c3e50;
+}
+
+.custom-block.details {
+	display: block;
+	position: relative;
+	border-radius: 2px;
+	margin: 1.6em 0;
+	padding: 1.6em;
+	background-color: #eee;
+}
+.custom-block.details h4 {
+	margin-top: 0;
+}
+.custom-block.details figure:last-child,
+.custom-block.details p:last-child {
+	margin-bottom: 0;
+	padding-bottom: 0;
+}
+.custom-block.details summary {
+	outline: none;
+	cursor: pointer;
 }
diff --git a/docs-svelte-kit/src/lib/footer/Footer.svelte b/docs-svelte-kit/src/lib/footer/Footer.svelte
index 01b6ed2..6a7fb82 100644
--- a/docs-svelte-kit/src/lib/footer/Footer.svelte
+++ b/docs-svelte-kit/src/lib/footer/Footer.svelte
@@ -47,27 +47,25 @@
 				target="_blank"
 				rel="noopener noreferrer">Edit this page</a
 			>
-			<span>
-				<svg
-					xmlns="http://www.w3.org/2000/svg"
-					aria-hidden="true"
-					focusable="false"
-					x="0px"
-					y="0px"
-					viewBox="0 0 100 100"
-					width="15"
-					height="15"
-					class="icon outbound"
-					><path
-						fill="currentColor"
-						d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"
-					/>
-					<polygon
-						fill="currentColor"
-						points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"
-					/>
-				</svg>
-			</span>
+			<svg
+				xmlns="http://www.w3.org/2000/svg"
+				aria-hidden="true"
+				focusable="false"
+				x="0px"
+				y="0px"
+				viewBox="0 0 100 100"
+				width="15"
+				height="15"
+				class="icon outbound"
+				><path
+					fill="currentColor"
+					d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"
+				/>
+				<polygon
+					fill="currentColor"
+					points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"
+				/>
+			</svg>
 		</div>
 		{#if fileInfo.lastUpdated}
 			<div class="last-updated">
@@ -85,29 +83,34 @@
 		{/if}
 	</div>
 	<div class="footer-text">
-		<span
-			>This site was built with <a href="https://kit.svelte.dev/" target="_brank">SvelteKit</a
-			>.</span
-		>
+		<span>
+			This site was built with <a href="https://kit.svelte.dev/" target="_brank">SvelteKit</a>.
+		</span>
 	</div>
 </footer>
 
 <style>
 	.footer-tools {
 		width: 100%;
-		padding: 1rem;
+		padding: 0 1rem;
 		box-sizing: border-box;
 		display: flex;
 	}
+
+	.edit-link {
+		display: flex;
+		gap: 4px;
+	}
+
 	.footer-move {
 		border-top: 1px solid var(--background-without-opacity);
 		width: 100%;
-		padding: 1rem;
+		padding: 0 1rem;
 		box-sizing: border-box;
 		display: flex;
 	}
 	.footer-text {
-		padding: 1rem;
+		padding: 0 1rem 1rem 1rem;
 		display: flex;
 		flex-direction: column;
 		justify-content: center;
@@ -139,7 +142,7 @@
 		flex-direction: column;
 		justify-content: center;
 		align-items: center;
-		padding: 40px;
+		padding: 16px 0 0 0;
 	}
 
 	footer a {
@@ -148,7 +151,7 @@
 
 	@media (min-width: 480px) {
 		footer {
-			padding: 40px 0;
+			padding: 24px 0 0 0;
 		}
 	}
 </style>
diff --git a/docs-svelte-kit/src/lib/header/Header.svelte b/docs-svelte-kit/src/lib/header/Header.svelte
index 78af595..4e8d2a2 100644
--- a/docs-svelte-kit/src/lib/header/Header.svelte
+++ b/docs-svelte-kit/src/lib/header/Header.svelte
@@ -103,9 +103,9 @@
 		padding: 0.5em 2em;
 		position: fixed;
 		top: 0;
-		width: calc(100% - 4em);
+		width: 100vw;
 		z-index: 100;
-		background-color: white;
+		background-color: var(--primary-color);
 		box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
 	}
 
diff --git a/docs-svelte-kit/src/lib/sidemenu/UlMenu.svelte b/docs-svelte-kit/src/lib/sidemenu/UlMenu.svelte
index 3248bb5..c71888e 100644
--- a/docs-svelte-kit/src/lib/sidemenu/UlMenu.svelte
+++ b/docs-svelte-kit/src/lib/sidemenu/UlMenu.svelte
@@ -7,7 +7,7 @@
 </script>
 
 <ul class="sidebar-menu sidebar-menu--level{level}">
-	{#each children as item (item.id)}
+	{#each children as item (item.path || item.id || item.title)}
 		<li
 			class="sidebar-menu-item"
 			class:active={item.active || (item.path && isActive(item.path, $page))}
@@ -17,8 +17,9 @@
 					class="sidebar-menu-item-title"
 					class:active={item.active || (item.path && isActive(item.path, $page))}
 					href="{baseUrl}{item.path || `${stripBaseUrl($page.url.pathname)}#${item.id}`}"
-					>{item.title}</a
 				>
+					{item.title}
+				</a>
 			{:else}
 				<span class="sidebar-menu-item-title">{item.title}</span>
 			{/if}
diff --git a/docs-svelte-kit/src/reset.css b/docs-svelte-kit/src/reset.css
new file mode 100644
index 0000000..e235875
--- /dev/null
+++ b/docs-svelte-kit/src/reset.css
@@ -0,0 +1,104 @@
+*,
+*::before,
+*::after {
+	margin: 0;
+	padding: 0;
+	box-sizing: border-box;
+}
+:where([hidden]:not([hidden='until-found'])) {
+	display: none !important;
+}
+:where(html) {
+	-webkit-text-size-adjust: none;
+	color-scheme: dark light;
+	tab-size: 2;
+	scrollbar-gutter: stable;
+}
+:where(html:has(dialog:modal[open])) {
+	overflow: clip;
+}
+@media (prefers-reduced-motion: no-preference) {
+	:where(html:focus-within) {
+		scroll-behavior: smooth;
+	}
+}
+:where(body) {
+	line-height: 1.5;
+	font-family: system-ui, sans-serif;
+	-webkit-font-smoothing: antialiased;
+}
+:where(button) {
+	all: unset;
+}
+:where(input, button, textarea, select) {
+	font: inherit;
+	color: inherit;
+}
+:where(textarea) {
+	resize: vertical;
+	resize: block;
+}
+:where(button, label, select, summary, [role='button'], [role='option']) {
+	cursor: pointer;
+}
+:where(:disabled) {
+	cursor: not-allowed;
+}
+:where(label:has(> input:disabled), label:has(+ input:disabled)) {
+	cursor: not-allowed;
+}
+:where(a) {
+	color: inherit;
+	text-underline-offset: 0.2ex;
+}
+:where(ul, ol) {
+	list-style: none;
+}
+:where(img, svg, video, canvas, audio, iframe, embed, object) {
+	display: block;
+}
+:where(img, picture, svg, video) {
+	max-inline-size: 100%;
+	block-size: auto;
+}
+:where(p, h1, h2, h3, h4, h5, h6) {
+	overflow-wrap: break-word;
+}
+:where(h1, h2, h3) {
+	line-height: calc(1em + 0.5rem);
+	text-wrap: balance;
+}
+:where(hr) {
+	border: none;
+	border-block-start: 1px solid;
+	color: inherit;
+	block-size: 0;
+	overflow: visible;
+}
+:where(dialog) {
+	border: none;
+	background: none;
+	inset: unset;
+	max-width: unset;
+	max-height: unset;
+}
+:where(dialog:not([open])) {
+	display: none !important;
+}
+:where(:focus-visible) {
+	outline: 3px solid CanvasText;
+	box-shadow: 0 0 0 5px Canvas;
+	outline-offset: 1px;
+}
+:where(:focus-visible, :target) {
+	scroll-margin-block: 8vh;
+}
+:where(.visually-hidden:not(:focus-within, :active)) {
+	clip-path: inset(50%) !important;
+	height: 1px !important;
+	width: 1px !important;
+	overflow: hidden !important;
+	position: absolute !important;
+	white-space: nowrap !important;
+	border: 0 !important;
+}
diff --git a/docs-svelte-kit/src/routes/+layout.svelte b/docs-svelte-kit/src/routes/+layout.svelte
index 14f2f3e..eaa30bc 100644
--- a/docs-svelte-kit/src/routes/+layout.svelte
+++ b/docs-svelte-kit/src/routes/+layout.svelte
@@ -3,9 +3,9 @@
 	import SideMenu from '$lib/sidemenu/SideMenu.svelte';
 	import Footer from '$lib/footer/Footer.svelte';
 
+	import '../reset.css';
 	import '../app.css';
-	import '../site.css';
-	import { tocStore } from '$lib/utils';
+	import { tocStore } from '$lib/utils.js';
 
 	/** @type {import('./$types').PageData */
 	export let data;
@@ -49,6 +49,7 @@
 <style>
 	main {
 		margin-top: 64px;
+		min-height: calc(100vh - 180px);
 	}
 	main:not(.hidden-menu) {
 		padding-left: 20rem;
@@ -69,7 +70,7 @@
 		flex: 1;
 		display: flex;
 		flex-direction: column;
-		padding: 1rem;
+		padding: 1rem calc(1rem + 16px);
 		width: 100%;
 		max-width: 1024px;
 		margin: 0 auto;
diff --git a/docs-svelte-kit/src/site.css b/docs-svelte-kit/src/site.css
deleted file mode 100644
index d92d253..0000000
--- a/docs-svelte-kit/src/site.css
+++ /dev/null
@@ -1,157 +0,0 @@
-/* @import "prismjs/themes/prism"; */
-@import 'prismjs/themes/prism-tomorrow';
-@import '@shikijs/twoslash/style-rich.css';
-
-.twoslash .twoslash-popup-container {
-	transform: translateY(2em);
-	white-space: pre-wrap;
-	margin-right: 32px;
-}
-
-.twoslash .twoslash-error > span:not(.twoslash-popup-container):not(:has(*)) {
-	/*
-	Since elements with no content have a width of zero and no wavy line is displayed,
-	we specify a minimum width to display the wavy line.
-	e.g. https://sveltejs.github.io/eslint-plugin-svelte/rules/html-closing-bracket-new-line/
-	*/
-	min-width: 6px;
-	display: inline-block;
-}
-
-/* theme */
-:root {
-	--primary-color: #ffffff;
-	--secondary-color: #676778;
-	--tertiary-color: #edf0f8;
-	--background-without-opacity: rgba(255, 255, 255, 0.95);
-}
-
-/*** markdown ***/
-
-:not(pre) > code {
-	padding: 0.1rem 0.4rem 0;
-	margin: 0 0.2rem;
-	background: #e5eef5;
-	position: relative;
-	border-radius: 0.3em;
-	white-space: nowrap;
-	color: #444;
-	-webkit-font-smoothing: initial;
-}
-
-blockquote {
-	padding: 1rem;
-	color: #09f;
-	border: 1px solid #40b3ff;
-	margin: 1.6rem 2.4rem 2.4rem;
-	padding: 0.5rem 2.4rem;
-	border-radius: 0.4rem;
-}
-blockquote p {
-	color: #09f;
-}
-
-table {
-	margin: 0 0 2em;
-	width: 100%;
-	font-size: var(--h5);
-}
-td,
-th {
-	text-align: left;
-	border-bottom: 1px solid rgba(0 0 0 / 0.1);
-	padding: 0.4rem 0.8rem 0.4rem 0;
-}
-
-/* header */
-
-h3,
-h4,
-h5,
-h6 {
-	font-weight: 400;
-	color: var(--heading-color);
-}
-
-h2 {
-	font-size: 1.2rem;
-}
-h3,
-h4,
-h5,
-h6 {
-	font-size: 1rem;
-}
-
-/*** custom container ***/
-
-.custom-block .custom-block-title {
-	font-weight: 600;
-	margin-bottom: -0.4rem;
-}
-
-.custom-block.danger,
-.custom-block.tip,
-.custom-block.warning {
-	padding: 0.1rem 1.5rem;
-	border-left-width: 4px;
-	border-left-style: solid;
-	margin: 1rem 0;
-}
-
-.custom-block.tip {
-	background-color: #f3f5f7;
-	border-color: #42b983;
-}
-
-.custom-block.warning {
-	background-color: rgba(255, 229, 100, 0.3);
-	border-color: #e7c000;
-	color: #6b5900;
-}
-
-.custom-block.warning .custom-block-title {
-	color: #b29400;
-}
-
-.custom-block.warning a {
-	color: #2c3e50;
-}
-
-.custom-block.danger {
-	background-color: #ffe6e6;
-	border-color: #c00;
-	color: #4d0000;
-}
-
-.custom-block.danger .custom-block-title {
-	color: #900;
-}
-
-.custom-block.danger a {
-	color: #2c3e50;
-}
-
-.custom-block.details {
-	display: block;
-	position: relative;
-	border-radius: 2px;
-	margin: 1.6em 0;
-	padding: 1.6em;
-	background-color: #eee;
-}
-
-.custom-block.details h4 {
-	margin-top: 0;
-}
-
-.custom-block.details figure:last-child,
-.custom-block.details p:last-child {
-	margin-bottom: 0;
-	padding-bottom: 0;
-}
-
-.custom-block.details summary {
-	outline: none;
-	cursor: pointer;
-}
diff --git a/docs-svelte-kit/vite.config.mts b/docs-svelte-kit/vite.config.ts
similarity index 100%
rename from docs-svelte-kit/vite.config.mts
rename to docs-svelte-kit/vite.config.ts
diff --git a/docs/README.md b/docs/README.md
index e7bea45..9f765d5 100644
--- a/docs/README.md
+++ b/docs/README.md
@@ -2,63 +2,30 @@
 title: 'eslint-plugin-svelte'
 ---
 
-# Introduction
+## Introduction
 
-`eslint-plugin-svelte` is the official [ESLint] plugin for [Svelte].  
-It provides many unique check rules by using the template AST.  
-You can check on the [Online DEMO](https://eslint-online-playground.netlify.app/#eslint-plugin-svelte%20with%20typescript).
+`eslint-plugin-svelte` is the official [ESLint](https://eslint.org/) plugin for [Svelte](https://svelte.dev/).  
+It leverages the AST generated by [svelte-eslint-parser](https://github.com/sveltejs/svelte-eslint-parser) to provide custom linting for Svelte.  
+Note that `eslint-plugin-svelte` and `svelte-eslint-parser` cannot be used alongside [eslint-plugin-svelte3](https://github.com/sveltejs/eslint-plugin-svelte3).
 
-> [!NOTE]
-> This document is in development.\
-> Please refer to the document for the version you are using.\
-> For example, <https://github.com/sveltejs/eslint-plugin-svelte/blob/eslint-plugin-svelte%402.46.0/README.md>
-> and <https://github.com/sveltejs/eslint-plugin-svelte/blob/eslint-plugin-svelte%402.46.0/docs>
-
-[![NPM license](https://img.shields.io/npm/l/eslint-plugin-svelte.svg)](https://www.npmjs.com/package/eslint-plugin-svelte)
-[![NPM version](https://img.shields.io/npm/v/eslint-plugin-svelte.svg)](https://www.npmjs.com/package/eslint-plugin-svelte)
-[![NPM downloads](https://img.shields.io/badge/dynamic/json.svg?label=downloads&colorB=green&suffix=/day&query=$.downloads&uri=https://api.npmjs.org//downloads/point/last-day/eslint-plugin-svelte&maxAge=3600)](http://www.npmtrends.com/eslint-plugin-svelte)
-[![NPM downloads](https://img.shields.io/npm/dw/eslint-plugin-svelte.svg)](http://www.npmtrends.com/eslint-plugin-svelte)
-[![NPM downloads](https://img.shields.io/npm/dm/eslint-plugin-svelte.svg)](http://www.npmtrends.com/eslint-plugin-svelte)
-[![NPM downloads](https://img.shields.io/npm/dy/eslint-plugin-svelte.svg)](http://www.npmtrends.com/eslint-plugin-svelte)
-[![NPM downloads](https://img.shields.io/npm/dt/eslint-plugin-svelte.svg)](http://www.npmtrends.com/eslint-plugin-svelte)
-[![Build Status](https://github.com/sveltejs/eslint-plugin-svelte/workflows/CI/badge.svg?branch=main)](https://github.com/sveltejs/eslint-plugin-svelte/actions?query=workflow%3ACI)
-
-[![type-coverage](https://img.shields.io/badge/dynamic/json.svg?label=type-coverage&prefix=%E2%89%A5&suffix=%&query=$.typeCoverage.atLeast&uri=https%3A%2F%2Fraw.githubusercontent.com%2Fsveltejs%2Feslint-plugin-svelte%2Fmain%2Fpackage.json)](https://github.com/plantain-00/type-coverage)
-[![Conventional Commits](https://img.shields.io/badge/conventional%20commits-1.0.0-yellow.svg)](https://conventionalcommits.org)
-[![Code Style: Prettier](https://img.shields.io/badge/code_style-prettier-ff69b4.svg)](https://github.com/prettier/prettier)
-[![changesets](https://img.shields.io/badge/maintained%20with-changesets-176de3.svg)](https://github.com/atlassian/changesets)
-
-## :name_badge: What is this plugin?
-
-[ESLint] plugin for [Svelte].  
-It provides many unique check rules using the AST generated by [svelte-eslint-parser].
-
-### ❗ Attention
-
-#### Cannot be used with eslint-plugin-svelte3
+See [User Guide](./user-guide.md).
 
-The [svelte-eslint-parser] and the `eslint-plugin-svelte` can not be used with the [eslint-plugin-svelte3].
+## Migration Guide
 
-[svelte-eslint-parser]: https://github.com/sveltejs/svelte-eslint-parser
-[eslint-plugin-svelte3]: https://github.com/sveltejs/eslint-plugin-svelte3
+If you’re migrating from `eslint-plugin-svelte` v1 or [`@ota-meshi/eslint-plugin-svelte`](https://www.npmjs.com/package/@ota-meshi/eslint-plugin-svelte), see the [migration guide](./migration.md).
 
-#### Experimental support for Svelte v5
+## Versioning Policy
 
-We are working on support for Svelte v5, but it is still an experimental feature. Please note that rules and features related to Svelte v5 may be changed or removed in minor versions without notice.
+This project follows [Semantic Versioning](https://semver.org/). Unlike [ESLint’s versioning policy](https://github.com/eslint/eslint#semantic-versioning-policy), new rules may be added to the recommended config in minor releases. If these rules cause unwanted warnings, you can disable them.
 
-## :book: Usage
+<!--DOCS_IGNORE_END-->
 
-See [User Guide](./user-guide.md).
-
-## :white_check_mark: Rules
+## Rules
 
 <!-- prettier-ignore-start -->
 See [Available Rules](./rules.md).
 <!-- prettier-ignore-end -->
 
-## :lock: License
-
-See the [LICENSE](https://github.com/sveltejs/eslint-plugin-svelte/blob/main/LICENSE) file for license rights and limitations (MIT).
+## License
 
-[svelte]: https://svelte.dev/
-[eslint]: https://eslint.org/
+See [LICENSE](https://github.com/sveltejs/eslint-plugin-svelte/blob/main/LICENSE) (MIT).
diff --git a/docs/migration.md b/docs/migration.md
index 10cd9b6..0acc972 100644
--- a/docs/migration.md
+++ b/docs/migration.md
@@ -62,3 +62,99 @@ Therefore, you need to replace the package name, and the presets, rules, and set
     +    "svelte": { ... }
       },
     ```
+
+## From `eslint-plugin-svelte` v2 To v3
+
+This section explains the necessary changes when upgrading from `eslint-plugin-svelte` v2 to v3.  
+v3 includes **support for ESLint Flat Config only**, **changes to the recommended rule set**, and other breaking changes.
+
+---
+
+## Breaking Changes
+
+### 1. **Minimum Node.js Version Requirement**
+
+v3 requires **one of the following Node.js versions**:
+
+- `^18.20.4`
+- `^20.18.0`
+- `>=22.10.0`
+
+### 2. **ESLint Flat Config Only**
+
+- `.eslintrc.js` and `.eslintrc.json` are **no longer supported**.
+- You must use **Flat Config (`eslint.config.js`)**.
+- See [README](README.md) for more details.
+
+### 3. **ESLint Version Requirement**
+
+- v3 requires **ESLint 8.57.1 or later** (including v9.x).
+
+### 4. **Changes to Recommended Rule Set**
+
+- The following rules are now included in `recommended`:
+  - `svelte/infinite-reactive-loop`
+  - `svelte/no-dom-manipulating`
+  - `svelte/no-dupe-on-directives`
+  - `svelte/no-reactive-reassign`
+  - `svelte/require-event-dispatcher-types`
+  - Many others (See [Changelog](https://github.com/sveltejs/eslint-plugin-svelte/releases/tag/eslint-plugin-svelte%403.0.0-next.16) for more details)
+- `svelte/valid-compile` has been **removed** from `recommended`.
+
+### 5. **Deprecated Rules**
+
+- `svelte/no-dynamic-slot-name` is **deprecated**.
+- `svelte/no-goto-without-base` is **deprecated** and replaced with `svelte/no-navigation-without-base`.
+
+---
+
+## New Features & Improvements
+
+### 1. **Support for Svelte 5**
+
+- Rules such as `no-not-function-handler` and `valid-prop-names-in-kit-pages` now support **Svelte 5**.
+
+### 2. **New Rules**
+
+- `consistent-selector-style`: Enforce consistent selector styles.
+- `no-useless-children-snippet`: Prevent unnecessary `{@children}` usage.
+- `prefer-const`: Prefer `const` over `let`.
+- `no-navigation-without-base`: Ensure proper usage of `goto()`.
+
+---
+
+## Migration Steps
+
+### 1. **Upgrade Package**
+
+```sh
+npm install eslint-plugin-svelte@3
+```
+
+### 2. **Check Node.js Version**
+
+```sh
+node -v
+```
+
+- Ensure it is `v18.20.4` or later.
+
+### 3. **Check ESLint Version**
+
+```sh
+npx eslint -v
+```
+
+- Ensure it is `8.57.1` or later (or `9.x`).
+
+### 4. **Use Flat Config (`eslint.config.js`)**
+
+- Remove `.eslintrc.js` and migrate to `eslint.config.js`.
+
+### 5. **Apply the Updated Recommended Rules**
+
+- If using `recommended`, check the impact of newly added rules.
+
+### 6. **Replace Deprecated Rules**
+
+- Replace `no-goto-without-base` with `no-navigation-without-base`.
diff --git a/docs/rules.md b/docs/rules.md
index 5e509b0..ac26d5e 100644
--- a/docs/rules.md
+++ b/docs/rules.md
@@ -32,6 +32,7 @@ These rules relate to possible syntax or logic errors in Svelte code:
 | [svelte/require-store-callbacks-use-set-param](./rules/require-store-callbacks-use-set-param.md) | store callbacks must use `set` param                                                                                                   |                |
 | [svelte/require-store-reactive-access](./rules/require-store-reactive-access.md)                 | disallow to use of the store itself as an operand. Need to use $ prefix or get function.                                               | :star::wrench: |
 | [svelte/valid-compile](./rules/valid-compile.md)                                                 | disallow warnings when compiling.                                                                                                      |                |
+| [svelte/valid-style-parse](./rules/valid-style-parse.md)                                         | require valid style element parsing                                                                                                    |                |
 
 ## Security Vulnerability
 
@@ -59,6 +60,7 @@ These rules relate to better ways of doing things to help you avoid problems:
 | [svelte/no-reactive-literals](./rules/no-reactive-literals.md)                           | don't assign literal values in reactive statements                                                                                        | :star::bulb:   |
 | [svelte/no-svelte-internal](./rules/no-svelte-internal.md)                               | svelte/internal will be removed in Svelte 6.                                                                                              | :star:         |
 | [svelte/no-unused-class-name](./rules/no-unused-class-name.md)                           | disallow the use of a class in the template without a corresponding style                                                                 |                |
+| [svelte/no-unused-props](./rules/no-unused-props.md)                                     | Warns about defined Props properties that are unused                                                                                      | :star:         |
 | [svelte/no-unused-svelte-ignore](./rules/no-unused-svelte-ignore.md)                     | disallow unused svelte-ignore comments                                                                                                    | :star:         |
 | [svelte/no-useless-children-snippet](./rules/no-useless-children-snippet.md)             | disallow explicit children snippet where it's not needed                                                                                  | :star:         |
 | [svelte/no-useless-mustaches](./rules/no-useless-mustaches.md)                           | disallow unnecessary mustache interpolations                                                                                              | :star::wrench: |
@@ -67,7 +69,7 @@ These rules relate to better ways of doing things to help you avoid problems:
 | [svelte/require-each-key](./rules/require-each-key.md)                                   | require keyed `{#each}` block                                                                                                             | :star:         |
 | [svelte/require-event-dispatcher-types](./rules/require-event-dispatcher-types.md)       | require type parameters for `createEventDispatcher`                                                                                       | :star:         |
 | [svelte/require-optimized-style-attribute](./rules/require-optimized-style-attribute.md) | require style attributes that can be optimized                                                                                            |                |
-| [svelte/require-stores-init](./rules/require-stores-init.md)                             | require initial value in store                                                                                                            | :star:         |
+| [svelte/require-stores-init](./rules/require-stores-init.md)                             | require initial value in store                                                                                                            |                |
 | [svelte/valid-each-key](./rules/valid-each-key.md)                                       | enforce keys to use variables defined in the `{#each}` block                                                                              | :star:         |
 
 ## Stylistic Issues
diff --git a/docs/rules/no-export-load-in-svelte-module-in-kit-pages.md b/docs/rules/no-export-load-in-svelte-module-in-kit-pages.md
index 5c6ccab..fc13f85 100644
--- a/docs/rules/no-export-load-in-svelte-module-in-kit-pages.md
+++ b/docs/rules/no-export-load-in-svelte-module-in-kit-pages.md
@@ -34,7 +34,7 @@ And the API has changed.
 
 ## :wrench: Options
 
-Nothing. But if you are not using the default routes folder, please set configuration according to the [user guide](../user-guide.md#settings-svelte-kit).
+Nothing. But if you are not using the default routes folder, please set configuration according to the [user guide](../user-guide.md#settings-svelte).
 
 ## :books: Further Reading
 
diff --git a/docs/rules/no-inner-declarations.md b/docs/rules/no-inner-declarations.md
index 43d69f2..50cff3e 100644
--- a/docs/rules/no-inner-declarations.md
+++ b/docs/rules/no-inner-declarations.md
@@ -48,7 +48,9 @@ This rule supports [svelte-eslint-parser]'s AST.
   "svelte/no-inner-declarations": [
     "error",
     "functions", // or "both"
-    { "blockScopedFunctions": "allow" }
+    {
+      "blockScopedFunctions": "allow" // or "disallow"
+    }
   ]
 }
 ```
diff --git a/docs/rules/no-unused-props.md b/docs/rules/no-unused-props.md
new file mode 100644
index 0000000..ad9e592
--- /dev/null
+++ b/docs/rules/no-unused-props.md
@@ -0,0 +1,208 @@
+---
+pageClass: 'rule-details'
+sidebarDepth: 0
+title: 'svelte/no-unused-props'
+description: 'Warns about defined Props properties that are unused'
+---
+
+# svelte/no-unused-props
+
+> Warns about defined Props properties that are unused
+
+- :exclamation: <badge text="This rule has not been released yet." vertical="middle" type="error"> **_This rule has not been released yet._** </badge>
+- :gear: This rule is included in `"plugin:svelte/recommended"`.
+
+## :book: Rule Details
+
+This rule reports properties that are defined in Props but never used in the component code.  
+It helps to detect dead code and improve component clarity by ensuring that every declared prop is utilized.
+
+This rule checks various usage patterns of props:
+
+- Direct property access
+- Destructuring assignment
+- Method calls
+- Computed property access
+- Object spread
+- Constructor calls (new expressions)
+- Assignment to other variables
+- Index signatures (e.g. `[key: string]: unknown`)
+
+Additionally, this rule checks if index signatures are properly used. When an index signature is defined but not captured using the rest operator (`...`), the rule will suggest using it.
+
+Note: Properties of class types are not checked for usage, as they might be used in other parts of the application.
+
+:warning: This rule requires `@typescript-eslint/parser` to work. Make sure you have installed `@typescript-eslint/parser` and configured it in your ESLint configuration. Therefore, the rule violations cannot be seen in the examples on this page because this documentation does not use `@typescript-eslint/parser`.
+
+<!--eslint-skip-->
+
+```svelte
+<!-- ✓ Good Examples -->
+<script lang="ts">
+  /* eslint svelte/no-unused-props: "error" */
+  // Direct property access
+  const props: { value: string } = $props();
+  console.log(props.value);
+</script>
+```
+
+```svelte
+<!-- ✓ Good Examples -->
+<script lang="ts">
+  /* eslint svelte/no-unused-props: "error" */
+  // Destructuring assignment
+  const { width, height }: { width: number; height: number } = $props();
+  console.log(width, height);
+</script>
+```
+
+```svelte
+<!-- ✓ Good Examples -->
+<script lang="ts">
+  /* eslint svelte/no-unused-props: "error" */
+  // Class properties are not checked
+  class User {
+    constructor(
+      public name: string,
+      public age: number
+    ) {}
+  }
+  type Props = {
+    user: User;
+  };
+  const props: Props = $props();
+  console.log(props.user.name); // age is not reported as unused
+</script>
+```
+
+```svelte
+<!-- ✓ Good Examples -->
+<script lang="ts">
+  /* eslint svelte/no-unused-props: "error" */
+  // Method calls
+  const props2: { callback: () => void } = $props();
+  props2.callback();
+</script>
+```
+
+```svelte
+<!-- ✓ Good Examples -->
+<script lang="ts">
+  /* eslint svelte/no-unused-props: "error" */
+  // Computed property access
+  const props3: { 'data-value': string } = $props();
+  const value = props3['data-value'];
+</script>
+```
+
+```svelte
+<!-- ✓ Good Examples -->
+<script lang="ts">
+  /* eslint svelte/no-unused-props: "error" */
+  // Constructor calls
+  const props4: { config: { new (): any } } = $props();
+  new props4.config();
+</script>
+```
+
+```svelte
+<!-- ✓ Good Examples -->
+<script lang="ts">
+  /* eslint svelte/no-unused-props: "error" */
+  // Using index signature with rest operator
+  interface Props {
+    a: number;
+    [key: string]: unknown;
+  }
+  let { a, ...rest }: Props = $props();
+  console.log(rest);
+</script>
+```
+
+```svelte
+<!-- ✗ Bad Examples -->
+<script lang="ts">
+  /* eslint svelte/no-unused-props: "error" */
+  // Unused property 'b'
+  const props: { a: string; b: number } = $props();
+  console.log(props.a);
+</script>
+```
+
+```svelte
+<!-- ✗ Bad Examples -->
+<script lang="ts">
+  /* eslint svelte/no-unused-props: "error" */
+  // Unused property in destructuring
+  const { x }: { x: number; y: number } = $props();
+  console.log(x);
+</script>
+```
+
+```svelte
+<!-- ✗ Bad Examples -->
+<script lang="ts">
+  /* eslint svelte/no-unused-props: "error" */
+  // Unused index signature
+  interface Props {
+    a: number;
+    [key: string]: unknown; // This will be reported
+  }
+  let { a }: Props = $props();
+</script>
+```
+
+## :wrench: Options
+
+```js
+{
+  "svelte/no-unused-props": ["error", {
+    // Whether to check properties from imported types
+    "checkImportedTypes": false,
+    // Patterns to ignore when checking for unused props
+    "ignorePatterns": []
+  }]
+}
+```
+
+- `checkImportedTypes` ... Controls whether to check properties from imported types. Default is `false`.
+- `ignorePatterns` ... Patterns to ignore when checking for unused props. Default is an empty array.
+
+Examples:
+
+```svelte
+<!-- ✓ Good Examples -->
+<script lang="ts">
+  /* eslint svelte/no-unused-props: ["error", { "checkImportedTypes": true }] */
+  // Check properties from imported types
+  import type { BaseProps } from './types';
+  interface Props extends BaseProps {
+    age: number;
+  }
+  let { name, age }: Props = $props();
+  console.log(name, age);
+</script>
+```
+
+```svelte
+<!-- ✓ Good Examples -->
+<script lang="ts">
+  /* eslint svelte/no-unused-props: ["error", { "ignorePatterns": ["^_"] }] */
+  // Ignore properties starting with underscore
+  interface Props {
+    _internal: string;
+    value: number;
+  }
+  let { value }: Props = $props();
+  console.log(value);
+</script>
+```
+
+## :gear: Required Configuration
+
+This rule requires `@typescript-eslint/parser` to work. Please refer to the [User Guide](../user-guide.md) for more information.
+
+## :mag: Implementation
+
+- [Rule source](https://github.com/sveltejs/eslint-plugin-svelte/blob/main/packages/eslint-plugin-svelte/src/rules/no-unused-props.ts)
+- [Test source](https://github.com/sveltejs/eslint-plugin-svelte/blob/main/packages/eslint-plugin-svelte/tests/src/rules/no-unused-props.ts)
diff --git a/docs/rules/prefer-class-directive.md b/docs/rules/prefer-class-directive.md
index ab68bb5..5b5c6a1 100644
--- a/docs/rules/prefer-class-directive.md
+++ b/docs/rules/prefer-class-directive.md
@@ -41,7 +41,7 @@ You cannot enforce this style by using [prettier-plugin-svelte]. That is, this r
 
 ```json
 {
-  "svelte/html-quotes": [
+  "svelte/prefer-class-directive": [
     "error",
     {
       "prefer": "empty" // or "always"
diff --git a/docs/rules/prefer-const.md b/docs/rules/prefer-const.md
index a6ad021..8029ea8 100644
--- a/docs/rules/prefer-const.md
+++ b/docs/rules/prefer-const.md
@@ -14,7 +14,7 @@ since: 'v3.0.0-next.6'
 
 ## :book: Rule Details
 
-This rule reports the same as the base ESLint `prefer-const` rule, except that ignores Svelte reactive values such as `$derived` and `$props`. If this rule is active, make sure to disable the base `prefer-const` rule, as it will conflict with this rule.
+This rule reports the same as the base ESLint `prefer-const` rule, except that ignores Svelte reactive values such as `$derived` and `$props` as default. If this rule is active, make sure to disable the base `prefer-const` rule, as it will conflict with this rule.
 
 <!--eslint-skip-->
 
@@ -46,7 +46,8 @@ This rule reports the same as the base ESLint `prefer-const` rule, except that i
     "error",
     {
       "destructuring": "any",
-      "ignoreReadonly": true
+      "ignoreReadonly": true,
+      "excludedRunes": ["$props", "$derived"]
     }
   ]
 }
@@ -56,6 +57,7 @@ This rule reports the same as the base ESLint `prefer-const` rule, except that i
   - `any` (default): if any variables in destructuring should be const, this rule warns for those variables.
   - `all`: if all variables in destructuring should be const, this rule warns the variables. Otherwise, ignores them.
 - `ignoreReadonly`: If `true`, this rule will ignore variables that are read between the declaration and the _first_ assignment.
+- `excludedRunes`: An array of rune names that should be ignored. Even if a rune is declared with `let`, it will still be ignored.
 
 ## :books: Further Reading
 
diff --git a/docs/rules/require-each-key.md b/docs/rules/require-each-key.md
index af8186b..cd0069c 100644
--- a/docs/rules/require-each-key.md
+++ b/docs/rules/require-each-key.md
@@ -44,7 +44,7 @@ Nothing.
 
 ## :books: Further Reading
 
-- [Svelte - Tutorial > 4. Logic / Keyed each blocks](https://svelte.dev/tutorial/keyed-each-blocks)
+- [Svelte - Tutorial > 4. Logic / Keyed each blocks](https://svelte.dev/tutorial/svelte/keyed-each-blocks)
 
 ## :rocket: Version
 
diff --git a/docs/rules/require-stores-init.md b/docs/rules/require-stores-init.md
index 177dd3c..13dcc99 100644
--- a/docs/rules/require-stores-init.md
+++ b/docs/rules/require-stores-init.md
@@ -10,8 +10,6 @@ since: 'v2.5.0'
 
 > require initial value in store
 
-- :gear: This rule is included in `"plugin:svelte/recommended"`.
-
 ## :book: Rule Details
 
 This rule is aimed to enforce initial values when initializing the Svelte stores.
diff --git a/docs/rules/valid-compile.md b/docs/rules/valid-compile.md
index 567ad6c..866d2d9 100644
--- a/docs/rules/valid-compile.md
+++ b/docs/rules/valid-compile.md
@@ -50,7 +50,7 @@ export default [
 ];
 ```
 
-See also [User Guide > Specify `svelte.config.js`](../user-guide.md#specify-svelte-config-js)
+See also [User Guide > Configuration](../user-guide.md#configuration)
 
 #### warningFilter
 
diff --git a/docs/rules/valid-prop-names-in-kit-pages.md b/docs/rules/valid-prop-names-in-kit-pages.md
index 7162f4b..3f3323c 100644
--- a/docs/rules/valid-prop-names-in-kit-pages.md
+++ b/docs/rules/valid-prop-names-in-kit-pages.md
@@ -41,7 +41,7 @@ At SvelteKit v1.0.0-next.405, instead of having multiple props corresponding to
 
 ## :wrench: Options
 
-Nothing. But if use are using not default routes folder, please set configuration according to the [user guide](../user-guide.md#settings-svelte-kit).
+Nothing. But if use are using not default routes folder, please set configuration according to the [user guide](../user-guide.md#settings-svelte).
 
 ## :books: Further Reading
 
diff --git a/docs/rules/valid-style-parse.md b/docs/rules/valid-style-parse.md
new file mode 100644
index 0000000..5e203ba
--- /dev/null
+++ b/docs/rules/valid-style-parse.md
@@ -0,0 +1,81 @@
+---
+pageClass: 'rule-details'
+sidebarDepth: 0
+title: 'svelte/valid-style-parse'
+description: 'require valid style element parsing'
+since: 'v3.0.0'
+---
+
+# svelte/valid-style-parse
+
+> require valid style element parsing
+
+## :book: Rule Details
+
+This rule reports issues with parsing of the `<style>` element by the svelte-eslint-parser.
+
+<!--eslint-skip-->
+
+```svelte
+<script>
+  /* eslint svelte/valid-style-parse: ["error"] */
+</script>
+
+<!-- ✓ GOOD -->
+<style>
+  .class {
+    font-weight: bold;
+  }
+</style>
+```
+
+```svelte
+<script>
+  /* eslint svelte/valid-style-parse: ["error"] */
+</script>
+
+<!-- ✓ GOOD -->
+<style lang="scss">
+  .class {
+    font-weight: bold;
+  }
+</style>
+```
+
+```svelte
+<script>
+  /* eslint svelte/valid-style-parse: ["error"] */
+</script>
+
+<!-- ✗ BAD -->
+<style>
+  .class
+    font-weight: bold;
+</style>
+```
+
+```svelte
+<script>
+  /* eslint svelte/valid-style-parse: ["error"] */
+</script>
+
+<!-- ✗ BAD -->
+<style lang="unknown">
+  .class {
+    font-weight: bold;
+  }
+</style>
+```
+
+## :wrench: Options
+
+Nothing.
+
+## :rocket: Version
+
+This rule was introduced in eslint-plugin-svelte v3.0.0
+
+## :mag: Implementation
+
+- [Rule source](https://github.com/sveltejs/eslint-plugin-svelte/blob/main/packages/eslint-plugin-svelte/src/rules/valid-style-parse.ts)
+- [Test source](https://github.com/sveltejs/eslint-plugin-svelte/blob/main/packages/eslint-plugin-svelte/tests/src/rules/valid-style-parse.ts)
diff --git a/docs/user-guide.md b/docs/user-guide.md
index eaec310..0eba2e2 100644
--- a/docs/user-guide.md
+++ b/docs/user-guide.md
@@ -1,177 +1,175 @@
 # User Guide
 
-> [!NOTE]
-> This document is in development.\
-> Please refer to the document for the version you are using.\
-> For example, <https://github.com/sveltejs/eslint-plugin-svelte/blob/eslint-plugin-svelte%402.46.0/docs/user-guide.md>
+<!--USAGE_GUIDE_START-->
 
-## :cd: Installation
+## Installation
 
 ```bash
-npm install --save-dev eslint eslint-plugin-svelte svelte
+npm install --save-dev svelte eslint eslint-plugin-svelte globals
 ```
 
-::: tip Requirements
-
-- ESLint v8.57.1, v9.0.0 and above
-- Node.js v18.20.4, v20.18.0, v22.10.0 and above
-
-:::
+> [!NOTE]
+>
+> **Requirements:**
+>
+> - ESLint v8.57.1, v9.0.0, and above
+> - Node.js v18.20.4, v20.18.0, v22.10.0, and above
 
-## :book: Usage
+## Usage
 
-<!--USAGE_GUIDE_START-->
+Use the `eslint.config.js` file to configure rules. For more details, see the [ESLint documentation](https://eslint.org/docs/latest/use/configure/configuration-files-new).
 
 ### Configuration
 
-Use `eslint.config.js` file to configure rules. See also: <https://eslint.org/docs/latest/use/configure/configuration-files-new>.
-
-Example **eslint.config.js**:
+#### JavaScript project
 
 ```js
-import eslintPluginSvelte from 'eslint-plugin-svelte';
-export default [
-  // add more generic rule sets here, such as:
-  // js.configs.recommended,
-  ...eslintPluginSvelte.configs.recommended,
-  {
-    rules: {
-      // override/add rules settings here, such as:
-      // 'svelte/rule-name': 'error'
-    }
-  }
-];
-```
-
-This plugin provides configs:
-
-- `eslintPluginSvelte.configs.base` ... Configuration to enable correct Svelte parsing.
-- `eslintPluginSvelte.configs.recommended` ... Above, plus rules to prevent errors or unintended behavior.
-- `eslintPluginSvelte.configs.prettier` ... Turns off rules that may conflict with [Prettier](https://prettier.io/) (You still need to configure prettier to work with svelte yourself, for example by using [prettier-plugin-svelte](https://github.com/sveltejs/prettier-plugin-svelte).).
-- `eslintPluginSvelte.configs.all` ... All rules. This configuration is not recommended for production use because it changes with every minor and major version of `eslint-plugin-svelte`. Use it at your own risk.
-
-See [the rule list](./rules.md) to get the `rules` that this plugin provides.
-
-#### Parser Configuration
-
-If you have specified a parser, you need to configure a parser for `.svelte`.
-
-For example, if you are using the `"@typescript-eslint/parser"`, and if you want to use TypeScript in `<script>` of `.svelte`, you need to add more `parserOptions` configuration.
+// eslint.config.js
+import js from '@eslint/js';
+import svelte from 'eslint-plugin-svelte';
+import globals from 'globals';
+import svelteConfig from './svelte.config.js';
 
-```js
-import eslintPluginSvelte from 'eslint-plugin-svelte';
-import * as svelteParser from 'svelte-eslint-parser';
-import * as typescriptParser from '@typescript-eslint/parser';
+/** @type {import('eslint').Linter.Config[]} */
 export default [
-  ...js.configs.recommended,
-  ...eslintPluginSvelte.configs.recommended,
+  js.configs.recommended,
+  ...svelte.configs.recommended,
   {
-    files: ['**/*.svelte'],
     languageOptions: {
-      parser: svelteParser,
-      parserOptions: {
-        parser: typescriptParser,
-        project: './path/to/your/tsconfig.json',
-        extraFileExtensions: ['.svelte']
+      globals: {
+        ...globals.browser,
+        ...globals.node // Add this if you are using SvelteKit in non-SPA mode
       }
     }
-  }
-];
-```
-
-If you have a mix of TypeScript and JavaScript in your project, use a multiple parser configuration.
-
-```js
-import eslintPluginSvelte from 'eslint-plugin-svelte';
-import * as svelteParser from 'svelte-eslint-parser';
-import * as typescriptParser from '@typescript-eslint/parser';
-import espree from 'espree';
-export default [
-  ...js.configs.recommended,
-  ...eslintPluginSvelte.configs.recommended,
+  },
   {
-    files: ['**/*.svelte'],
+    files: ['**/*.svelte', '**/*.svelte.js'],
     languageOptions: {
-      parser: svelteParser,
       parserOptions: {
-        parser: {
-          // Specify a parser for each lang.
-          ts: typescriptParser,
-          js: espree,
-          typescript: typescriptParser
-        },
-        project: './path/to/your/tsconfig.json',
-        extraFileExtensions: ['.svelte']
+        // We recommend importing and specifying svelte.config.js.
+        // By doing so, some rules in eslint-plugin-svelte will automatically read the configuration and adjust their behavior accordingly.
+        // While certain Svelte settings may be statically loaded from svelte.config.js even if you don’t specify it,
+        // explicitly specifying it ensures better compatibility and functionality.
+        svelteConfig
       }
     }
+  },
+  {
+    rules: {
+      // Override or add rule settings here, such as:
+      // 'svelte/rule-name': 'error'
+    }
   }
 ];
 ```
 
-See also <https://github.com/sveltejs/svelte-eslint-parser#readme>.
-
-::: warning ❗ Attention
-
-The TypeScript parser uses a singleton internally and it will only use the
-options given to it when it was first initialized. If trying to change the
-options for a different file or override, the parser will simply ignore the new
-options (which may result in an error). See
-[typescript-eslint/typescript-eslint#6778](https://github.com/typescript-eslint/typescript-eslint/issues/6778)
-for some context.
-
-:::
-
-#### Specify `svelte.config.js`
+#### TypeScript project
 
-If you are using `eslint.config.js`, we recommend that you import and specify `svelte.config.js`.
-By specifying it, some rules of `eslint-plugin-svelte` will read it and try to behave well for you by default.
-Some Svelte configurations will be statically loaded from `svelte.config.js` even if you don't specify it, but you need to specify it to make it work better.
-
-Example **eslint.config.js**:
+```shell
+npm install --save-dev typescript-eslint
+```
 
 ```js
-import eslintPluginSvelte from 'eslint-plugin-svelte';
+// eslint.config.js
+import js from '@eslint/js';
+import svelte from 'eslint-plugin-svelte';
+import globals from 'globals';
+import ts from 'typescript-eslint';
 import svelteConfig from './svelte.config.js';
-export default [
-  ...eslintPluginSvelte.configs.recommended,
+
+export default ts.config(
+  js.configs.recommended,
+  ...ts.configs.recommended,
+  ...svelte.configs.recommended,
+  {
+    languageOptions: {
+      globals: {
+        ...globals.browser,
+        ...globals.node
+      }
+    }
+  },
   {
-    files: [
-      '**/*.svelte',
-      '*.svelte'
-      // Add more files if you need.
-      // '**/*.svelte.ts', '*.svelte.ts', '**/*.svelte.js', '*.svelte.js',
-    ],
+    files: ['**/*.svelte', '**/*.svelte.ts', '**/*.svelte.js'],
+    // See more details at: https://typescript-eslint.io/packages/parser/
     languageOptions: {
       parserOptions: {
-        // Specify the `svelte.config.js`.
+        projectService: true,
+        extraFileExtensions: ['.svelte'], // Add support for additional file extensions, such as .svelte
+        parser: ts.parser,
+        // Specify a parser for each language, if needed:
+        // parser: {
+        //   ts: ts.parser,
+        //   js: espree,    // Use espree for .js files (add: import espree from 'espree')
+        //   typescript: ts.parser
+        // },
+
+        // We recommend importing and specifying svelte.config.js.
+        // By doing so, some rules in eslint-plugin-svelte will automatically read the configuration and adjust their behavior accordingly.
+        // While certain Svelte settings may be statically loaded from svelte.config.js even if you don’t specify it,
+        // explicitly specifying it ensures better compatibility and functionality.
         svelteConfig
       }
     }
+  },
+  {
+    rules: {
+      // Override or add rule settings here, such as:
+      // 'svelte/rule-name': 'error'
+    }
   }
-];
+);
 ```
 
-#### settings.svelte
+> [!WARNING]
+> The TypeScript parser uses a singleton internally, meaning it only respects the options provided during its initial initialization.
+> If you try to change the options for a different file or override them later, the parser will ignore the new options, which may lead to errors.
+> For more context, see [typescript-eslint/typescript-eslint#6778](https://github.com/typescript-eslint/typescript-eslint/issues/6778).
+
+### Available Configurations
+
+This plugin provides the following configurations:
+
+- **`eslintPluginSvelte.configs.base`** ... Enables correct Svelte parsing.
+- **`eslintPluginSvelte.configs.recommended`** ... Includes `base` configuration, plus rules to prevent errors or unintended behavior.
+- **`eslintPluginSvelte.configs.prettier`** ... Disables rules that may conflict with [Prettier](https://prettier.io/). You still need to configure Prettier to work with Svelte manually, for example, by using [prettier-plugin-svelte](https://github.com/sveltejs/prettier-plugin-svelte).
+- **`eslintPluginSvelte.configs.all`** ... Includes all available rules. **Note:** This configuration is not recommended for production use, as it changes with every minor and major version of `eslint-plugin-svelte`. Use at your own risk.
+
+For more details, see [the rule list](./rules.md) to explore the rules provided by this plugin.
 
-You can change the behavior of this plugin with some settings.
+### settings.svelte
 
-e.g.
+You can customize the behavior of this plugin using specific settings.
 
 ```js
+// eslint.config.js
 export default [
   // ...
   {
     settings: {
       svelte: {
+        // Specifies an array of rules to ignore reports within the template.
+        // For example, use this to disable rules in the template that may produce unavoidable false positives.
         ignoreWarnings: [
           '@typescript-eslint/no-unsafe-assignment',
           '@typescript-eslint/no-unsafe-member-access'
         ],
+        // Specifies options for Svelte compilation.
+        // This affects rules that rely on Svelte compilation,
+        // such as svelte/valid-compile and svelte/no-unused-svelte-ignore.
+        // Note that this setting does not impact ESLint’s custom parser.
         compileOptions: {
+          // Specifies options related to PostCSS. You can disable the PostCSS processing by setting it to false.
           postcss: {
+            // Specifies the path to the directory that contains the PostCSS configuration.
             configFilePath: './path/to/my/postcss.config.js'
           }
         },
+        // Even if settings.svelte.kit is not specified, the rules will attempt to load information from svelte.config.js.
+        // However, if the default behavior does not work as expected, you should specify settings.svelte.kit explicitly.
+        // If you are using SvelteKit with a non-default configuration, you need to set the following options.
+        // The schema is a subset of SvelteKit’s configuration, so refer to the SvelteKit documentation for more details.
+        // https://svelte.dev/docs/kit/configuration
         kit: {
           files: {
             routes: 'src/routes'
@@ -184,59 +182,11 @@ export default [
 ];
 ```
 
-#### settings.svelte.ignoreWarnings
-
-Specifies an array of rules that ignore reports in the template.  
-For example, set rules on the template that cannot avoid false positives.
-
-#### settings.svelte.compileOptions
-
-Specifies options for Svelte compile. Effects rules that use Svelte compile. The target rules are [svelte/valid-compile](./rules/valid-compile.md) and [svelte/no-unused-svelte-ignore](./rules/no-unused-svelte-ignore.md). **Note that it has no effect on ESLint's custom parser**.
-
-- `postcss` ... Specifies options related to PostCSS. You can disable the PostCSS process by specifying `false`.
-  - `configFilePath` ... Specifies the path of the directory containing the PostCSS configuration.
-
-#### settings.svelte.kit
-
-::: warning
-
-Even if you don't specify `settings.svelte.kit`, the rules will try to load information from `svelte.config.js`, so specify `settings.svelte.kit` if the default doesn't work.
-
-:::
-
-If you use SvelteKit with not default configuration, you need to set below configurations.
-The schema is subset of SvelteKit's configuration.
-Therefore please check [SvelteKit docs](https://kit.svelte.dev/docs/configuration) for more details.
-
-e.g.
-
-```js
-export default [
-  // ...
-  {
-    settings: {
-      svelte: {
-        kit: {
-          files: {
-            routes: 'src/routes'
-          }
-        }
-      }
-    }
-  }
-  // ...
-];
-```
-
-## :computer: Editor Integrations
+## Editor Integrations
 
-### Visual Studio Code
-
-Use the [dbaeumer.vscode-eslint](https://marketplace.visualstudio.com/items?itemName=dbaeumer.vscode-eslint) extension that Microsoft provides officially.
-
-You have to configure the `eslint.validate` option of the extension to check `.svelte` files, because the extension targets only `*.js` or `*.jsx` files by default.
-
-Example **.vscode/settings.json**:
+**Visual Studio Code**  
+Install [dbaeumer.vscode-eslint](https://marketplace.visualstudio.com/items?itemName=dbaeumer.vscode-eslint).  
+Configure `.svelte` files in `.vscode/settings.json`:
 
 ```json
 {
@@ -246,11 +196,7 @@ Example **.vscode/settings.json**:
 
 <!--USAGE_GUIDE_END-->
 
-## :question: FAQ
-
-### Parsing the `.svelte` file fails
-
-You should check the [parser configuration](#parser-configuration).
+## FAQ
 
 ### You're using TypeScript and the imported `*.svelte` component types cannot be resolved or appear to be
 
diff --git a/package.json b/package.json
index f2761f2..4c02f05 100644
--- a/package.json
+++ b/package.json
@@ -4,7 +4,7 @@
   "private": true,
   "license": "MIT",
   "funding": "https://github.com/sponsors/ota-meshi",
-  "packageManager": "pnpm@9.15.5",
+  "packageManager": "pnpm@9.15.9",
   "scripts": {
     "prerelease": "cd packages/eslint-plugin-svelte && pnpm clean && pnpm run build && cd ../.. && cp README.md packages/eslint-plugin-svelte",
     "release": "changeset publish",
@@ -24,7 +24,7 @@
     "@typescript-eslint/parser": "^8.16.0",
     "c8": "^10.1.2",
     "env-cmd": "^10.1.0",
-    "eslint": "~9.20.0",
+    "eslint": "~9.22.0",
     "eslint-config-prettier": "^10.0.0",
     "eslint-formatter-friendly": "^7.0.0",
     "eslint-plugin-eslint-plugin": "^6.3.2",
diff --git a/packages/eslint-plugin-svelte/CHANGELOG.md b/packages/eslint-plugin-svelte/CHANGELOG.md
index 4d95e3c..6b194ea 100644
--- a/packages/eslint-plugin-svelte/CHANGELOG.md
+++ b/packages/eslint-plugin-svelte/CHANGELOG.md
@@ -1,5 +1,173 @@
 # eslint-plugin-svelte
 
+## 3.1.0
+
+### Minor Changes
+
+- [#1064](https://github.com/sveltejs/eslint-plugin-svelte/pull/1064) [`df1647f`](https://github.com/sveltejs/eslint-plugin-svelte/commit/df1647f64c3abeffc46523a85ea8ea08cc5b4c85) Thanks [@baseballyama](https://github.com/baseballyama)! - feat: add `excludedRunes` option to the `prefer-const` rule
+
+### Patch Changes
+
+- [#1126](https://github.com/sveltejs/eslint-plugin-svelte/pull/1126) [`9b76b46`](https://github.com/sveltejs/eslint-plugin-svelte/commit/9b76b46e8637baff1bda0039edeacd647637993c) Thanks [@baseballyama](https://github.com/baseballyama)! - fix: check property key instead of value in `valid-prop-names-in-kit-pages` rule
+
+- [#1125](https://github.com/sveltejs/eslint-plugin-svelte/pull/1125) [`0a03362`](https://github.com/sveltejs/eslint-plugin-svelte/commit/0a0336267a41885871a181ed00454929bc2d79da) Thanks [@ota-meshi](https://github.com/ota-meshi)! - fix: support windows path delimiter
+
+- [#1108](https://github.com/sveltejs/eslint-plugin-svelte/pull/1108) [`976e4f0`](https://github.com/sveltejs/eslint-plugin-svelte/commit/976e4f0b5047f70b737e898743187afff88fea56) Thanks [@marekdedic](https://github.com/marekdedic)! - fix(consistent-selector-style): not reporting class selectors used in class directives
+
+- [#1107](https://github.com/sveltejs/eslint-plugin-svelte/pull/1107) [`3b01537`](https://github.com/sveltejs/eslint-plugin-svelte/commit/3b015374eb80d08a914321b36da36ccc9eac730f) Thanks [@marekdedic](https://github.com/marekdedic)! - fix(no-navigation-without-base): ignoring fragment links
+
+## 3.0.3
+
+### Patch Changes
+
+- [#1115](https://github.com/sveltejs/eslint-plugin-svelte/pull/1115) [`7f756b8`](https://github.com/sveltejs/eslint-plugin-svelte/commit/7f756b8535818008742eead569ebf03065db776b) Thanks [@baseballyama](https://github.com/baseballyama)! - fix: align required Node version with ESLint
+
+## 3.0.2
+
+### Patch Changes
+
+- [#1100](https://github.com/sveltejs/eslint-plugin-svelte/pull/1100) [`1358d89`](https://github.com/sveltejs/eslint-plugin-svelte/commit/1358d89206e9cf57faf5789d246ed99489f1685b) Thanks [@baseballyama](https://github.com/baseballyama)! - fix: handle `<script module>` in `valid-prop-names-in-kit-pages` rule
+
+## 3.0.1
+
+### Patch Changes
+
+- [#1091](https://github.com/sveltejs/eslint-plugin-svelte/pull/1091) [`83da078`](https://github.com/sveltejs/eslint-plugin-svelte/commit/83da0784e93fe148579de6fcd665ed94da76411d) Thanks [@ottomated](https://github.com/ottomated)! - fix: Don't remove mustache'd string literals if they contain curly braces
+
+## 3.0.0
+
+### Major Changes
+
+- [#937](https://github.com/sveltejs/eslint-plugin-svelte/pull/937) [`729394e`](https://github.com/sveltejs/eslint-plugin-svelte/commit/729394e34d2d6f51e00c89b67a40d0f9192260b4) Thanks [@ota-meshi](https://github.com/ota-meshi)! - feat!: drop support for old eslint
+
+- [#1033](https://github.com/sveltejs/eslint-plugin-svelte/pull/1033) [`3bfcc31`](https://github.com/sveltejs/eslint-plugin-svelte/commit/3bfcc31e0f2d127c7a1cb838c4c57125c30109ea) Thanks [@baseballyama](https://github.com/baseballyama)! - **Enabled in recommended config**:
+
+  - `svelte/infinite-reactive-loop`
+  - `svelte/no-dom-manipulating`
+  - `svelte/no-dupe-on-directives`
+  - `svelte/no-dupe-use-directives`
+  - `svelte/no-export-load-in-svelte-module-in-kit-pages`
+  - `svelte/no-immutable-reactive-statements`
+  - `svelte/no-inspect`
+  - `svelte/no-raw-special-elements`
+  - `svelte/no-reactive-functions`
+  - `svelte/no-reactive-literals`
+  - `svelte/no-reactive-reassign`
+  - `svelte/no-store-async`
+  - `svelte/no-svelte-internal`
+  - `svelte/no-useless-children-snippet`
+  - `svelte/no-useless-mustaches`
+  - `svelte/require-each-key`
+  - `svelte/require-event-dispatcher-types`
+  - `svelte/require-store-reactive-access`
+  - `svelte/require-stores-init`
+  - `svelte/valid-each-key`
+  - `svelte/valid-prop-names-in-kit-pages`
+
+  **Removed from recommended config**:
+
+  - `svelte/valid-compile`
+
+  This update introduces breaking changes due to newly enabled rules.
+
+- [#1086](https://github.com/sveltejs/eslint-plugin-svelte/pull/1086) [`1c679b5`](https://github.com/sveltejs/eslint-plugin-svelte/commit/1c679b50f310040257b89e4e03d3a88764fd7b28) Thanks [@baseballyama](https://github.com/baseballyama)! - breaking: remove `require-stores-init` from recommended rules
+
+- [#1043](https://github.com/sveltejs/eslint-plugin-svelte/pull/1043) [`778427e`](https://github.com/sveltejs/eslint-plugin-svelte/commit/778427e8bd1a015247dd7f19642e2c6ced0891a8) Thanks [@ota-meshi](https://github.com/ota-meshi)! - feat: add `*.svelte.{js,ts}` config to base config
+
+- [#900](https://github.com/sveltejs/eslint-plugin-svelte/pull/900) [`4759b47`](https://github.com/sveltejs/eslint-plugin-svelte/commit/4759b47b195abee605a0597360fc9fc4c6d95f68) Thanks [@marekdedic](https://github.com/marekdedic)! - breaking: deprecated the no-goto-without-base rule
+
+- [#885](https://github.com/sveltejs/eslint-plugin-svelte/pull/885) [`ce2ffad`](https://github.com/sveltejs/eslint-plugin-svelte/commit/ce2ffad105b1c6ed2df02a9c2b84f3b654d99ad5) Thanks [@ota-meshi](https://github.com/ota-meshi)! - feat!: drop support for old node versions (<18, 19, 21)
+
+- [#1011](https://github.com/sveltejs/eslint-plugin-svelte/pull/1011) [`7596287`](https://github.com/sveltejs/eslint-plugin-svelte/commit/7596287c9d1a0a31c90e23875f6b7beab20747f1) Thanks [@baseballyama](https://github.com/baseballyama)! - breaking: deprecate the `svelte/no-dynamic-slot-name` rule
+
+- [#930](https://github.com/sveltejs/eslint-plugin-svelte/pull/930) [`eae1b4f`](https://github.com/sveltejs/eslint-plugin-svelte/commit/eae1b4fcbfbaec926cfa63a5d08eafcb2238bf82) Thanks [@ota-meshi](https://github.com/ota-meshi)! - feat!: upgrade svelte-eslint-parser to v1
+
+- [#982](https://github.com/sveltejs/eslint-plugin-svelte/pull/982) [`04fc429`](https://github.com/sveltejs/eslint-plugin-svelte/commit/04fc4292ef68134691ac1808fd92688bd9982d37) Thanks [@baseballyama](https://github.com/baseballyama)! - feat!: Updated the `html-self-closing` rule to follow Svelte5
+
+- [#932](https://github.com/sveltejs/eslint-plugin-svelte/pull/932) [`b136ab4`](https://github.com/sveltejs/eslint-plugin-svelte/commit/b136ab4e54d8ff38f6ba7f49e1209be14dd18b0b) Thanks [@ota-meshi](https://github.com/ota-meshi)! - feat!: change the plugin to an ESM-only package
+
+### Minor Changes
+
+- [#980](https://github.com/sveltejs/eslint-plugin-svelte/pull/980) [`0b28198`](https://github.com/sveltejs/eslint-plugin-svelte/commit/0b28198edd78513e5efc4cfb45e8f1120148435e) Thanks [@baseballyama](https://github.com/baseballyama)! - feat: Implement util to conditionally run lint based on Svelte version and SvelteKit routes etc
+
+- [#1013](https://github.com/sveltejs/eslint-plugin-svelte/pull/1013) [`0ef0f99`](https://github.com/sveltejs/eslint-plugin-svelte/commit/0ef0f99e1781e0453598393b846e63bd159c215d) Thanks [@baseballyama](https://github.com/baseballyama)! - feat: add Svelte 5 support to `no-not-function-handler`
+
+- [#900](https://github.com/sveltejs/eslint-plugin-svelte/pull/900) [`4759b47`](https://github.com/sveltejs/eslint-plugin-svelte/commit/4759b47b195abee605a0597360fc9fc4c6d95f68) Thanks [@marekdedic](https://github.com/marekdedic)! - feat: added the no-navigation-without-base rule
+
+- [#925](https://github.com/sveltejs/eslint-plugin-svelte/pull/925) [`faf90ef`](https://github.com/sveltejs/eslint-plugin-svelte/commit/faf90ef9a6aab3ec647c30df67a54a25d8322324) Thanks [@marekdedic](https://github.com/marekdedic)! - feat: added the `consistent-selector-style` rule
+
+- [#945](https://github.com/sveltejs/eslint-plugin-svelte/pull/945) [`19f682b`](https://github.com/sveltejs/eslint-plugin-svelte/commit/19f682b73ce6bcccae52da0e59d5ab32a9d2c3c2) Thanks [@baseballyama](https://github.com/baseballyama)! - feat: Support `<svelte:boundary>`
+
+- [#844](https://github.com/sveltejs/eslint-plugin-svelte/pull/844) [`2bbd049`](https://github.com/sveltejs/eslint-plugin-svelte/commit/2bbd0497ab7e05db0aab3c8958db08e3fdf4b9b2) Thanks [@Winter](https://github.com/Winter)! - feat: Added suggestion to the `block-lang` rule.
+
+- [#984](https://github.com/sveltejs/eslint-plugin-svelte/pull/984) [`35d80a5`](https://github.com/sveltejs/eslint-plugin-svelte/commit/35d80a50f1053666153bfc7221b945055bfb76d1) Thanks [@baseballyama](https://github.com/baseballyama)! - feat: support `warningFilter` in `valid-compile` rule
+
+- [#933](https://github.com/sveltejs/eslint-plugin-svelte/pull/933) [`71eca84`](https://github.com/sveltejs/eslint-plugin-svelte/commit/71eca843c43250591ec6fa2dd1dc547b1074d6ad) Thanks [@mikededo](https://github.com/mikededo)! - Add `prefer-const` rule
+
+- [#918](https://github.com/sveltejs/eslint-plugin-svelte/pull/918) [`5da98c9`](https://github.com/sveltejs/eslint-plugin-svelte/commit/5da98c94f452c8436f26af4172e095fd91f66e1a) Thanks [@mikededo](https://github.com/mikededo)! - Added new `no-deprecated-raw-special-elements` rule
+
+- [#922](https://github.com/sveltejs/eslint-plugin-svelte/pull/922) [`f4a0fa7`](https://github.com/sveltejs/eslint-plugin-svelte/commit/f4a0fa78e06091348d985b5442a390eae47b0f4f) Thanks [@marekdedic](https://github.com/marekdedic)! - feat: added the no-useless-children-snippet rule
+
+- [#836](https://github.com/sveltejs/eslint-plugin-svelte/pull/836) [`3fa90aa`](https://github.com/sveltejs/eslint-plugin-svelte/commit/3fa90aa57a15ad18105b0d80b1ed975f1f782a11) Thanks [@renovate](https://github.com/apps/renovate)! - feat: support for typescript-eslint v8 to `svelte/indent` rule
+
+- [#963](https://github.com/sveltejs/eslint-plugin-svelte/pull/963) [`2c551b2`](https://github.com/sveltejs/eslint-plugin-svelte/commit/2c551b20557bcd53e35479336bf0c25e88a7643b) Thanks [@baseballyama](https://github.com/baseballyama)! - feat: support Svelte5 of `valid-prop-names-in-kit-pages` rule
+
+- [#1054](https://github.com/sveltejs/eslint-plugin-svelte/pull/1054) [`c587629`](https://github.com/sveltejs/eslint-plugin-svelte/commit/c587629ba2a7d7742965367eb3ad797a9075ef8a) Thanks [@marekdedic](https://github.com/marekdedic)! - feat: added the valid-style-parse rule
+
+- [#1085](https://github.com/sveltejs/eslint-plugin-svelte/pull/1085) [`a0588ef`](https://github.com/sveltejs/eslint-plugin-svelte/commit/a0588ef1fa2d3662a664f4d5564f9f225c67696e) Thanks [@baseballyama](https://github.com/baseballyama)! - chore: update `svelte-eslint-parser` to v1
+
+- [#1015](https://github.com/sveltejs/eslint-plugin-svelte/pull/1015) [`8369eaf`](https://github.com/sveltejs/eslint-plugin-svelte/commit/8369eaf5d2e77fccf0ac9fb3f663d94a2b323a4f) Thanks [@mikededo](https://github.com/mikededo)! - fix!: rename `no-deprecated-raw-special-elements` to `no-raw-special-elements`
+
+- [#911](https://github.com/sveltejs/eslint-plugin-svelte/pull/911) [`452ffed`](https://github.com/sveltejs/eslint-plugin-svelte/commit/452ffed53791cb9e158636bcd80a221d2840cc4a) Thanks [@marekdedic](https://github.com/marekdedic)! - feat(no-inline-styles): allowing transitions by default
+
+- [#1063](https://github.com/sveltejs/eslint-plugin-svelte/pull/1063) [`d39c90c`](https://github.com/sveltejs/eslint-plugin-svelte/commit/d39c90c86edfedb9be42a011a5ca4573f6661397) Thanks [@baseballyama](https://github.com/baseballyama)! - feat: detect version information from node_modules when not specified in package.json, like pnpm’s catalog
+
+### Patch Changes
+
+- [#961](https://github.com/sveltejs/eslint-plugin-svelte/pull/961) [`117e60d`](https://github.com/sveltejs/eslint-plugin-svelte/commit/117e60d4290966911450c7e0db8566279e4511bb) Thanks [@baseballyama](https://github.com/baseballyama)! - fix: support each blocks without an item
+
+- [#1009](https://github.com/sveltejs/eslint-plugin-svelte/pull/1009) [`a003664`](https://github.com/sveltejs/eslint-plugin-svelte/commit/a0036643b5451f0423cd61dafd092c39bd6f4bcb) Thanks [@baseballyama](https://github.com/baseballyama)! - chore: upgrade `svelte-eslint-parser` to `1.0.0-next.8`
+
+- [#1016](https://github.com/sveltejs/eslint-plugin-svelte/pull/1016) [`91999e3`](https://github.com/sveltejs/eslint-plugin-svelte/commit/91999e3520afe42b3cc51f0823f7c912b97176b4) Thanks [@baseballyama](https://github.com/baseballyama)! - chore: change the rule category of `valid-prop-names-in-kit-pages` to `SvelteKit`
+
+- [#993](https://github.com/sveltejs/eslint-plugin-svelte/pull/993) [`b97a13e`](https://github.com/sveltejs/eslint-plugin-svelte/commit/b97a13eb02f9c72346f29e64734f0a304890a197) Thanks [@baseballyama](https://github.com/baseballyama)! - chore: add `:exit` for each node listener
+
+- [#1048](https://github.com/sveltejs/eslint-plugin-svelte/pull/1048) [`513806c`](https://github.com/sveltejs/eslint-plugin-svelte/commit/513806cafe221487afd32bdcaa33ba8852dc881f) Thanks [@baseballyama](https://github.com/baseballyama)! - fix: allow `children` in `valid-prop-names-in-kit-pages` rule
+
+- [#1012](https://github.com/sveltejs/eslint-plugin-svelte/pull/1012) [`763cf7a`](https://github.com/sveltejs/eslint-plugin-svelte/commit/763cf7ace209dfb90cf7d1d7e6699a6c0fe240d6) Thanks [@baseballyama](https://github.com/baseballyama)! - chore: change the rule category of `no-export-load-in-svelte-module-in-kit-pages` to `SvelteKit`
+
+- [#1021](https://github.com/sveltejs/eslint-plugin-svelte/pull/1021) [`6557c69`](https://github.com/sveltejs/eslint-plugin-svelte/commit/6557c69d7f3595cdf226e681cadf3d0df4c5d972) Thanks [@baseballyama](https://github.com/baseballyama)! - chore: update `svelte-eslint-parser` to `1.0.0-next.10`
+
+- [#956](https://github.com/sveltejs/eslint-plugin-svelte/pull/956) [`157ee1f`](https://github.com/sveltejs/eslint-plugin-svelte/commit/157ee1fade79aab88f01d125b0b01cf375da4cd1) Thanks [@baseballyama](https://github.com/baseballyama)! - chore: update `svelte-eslint-parser` to v1.0.0-next.4
+
+- [#965](https://github.com/sveltejs/eslint-plugin-svelte/pull/965) [`47760ad`](https://github.com/sveltejs/eslint-plugin-svelte/commit/47760ad1b7f6b83d4c29380b3ef3cf09cde42dc8) Thanks [@marekdedic](https://github.com/marekdedic)! - chore: using svelte-eslint-parser for style selector parsing
+
+- [#990](https://github.com/sveltejs/eslint-plugin-svelte/pull/990) [`12049c0`](https://github.com/sveltejs/eslint-plugin-svelte/commit/12049c026f529feff6056e679ba62f07de948aa5) Thanks [@baseballyama](https://github.com/baseballyama)! - fix: ignore `css_unused_selector` compile error if `<style>` tag has `global` attribute
+
+- [#1035](https://github.com/sveltejs/eslint-plugin-svelte/pull/1035) [`f16729f`](https://github.com/sveltejs/eslint-plugin-svelte/commit/f16729f55877b1768ec8d240fd8e416f141781ce) Thanks [@baseballyama](https://github.com/baseballyama)! - chore: run `require-event-dispatcher-types` rule on Svelte 3/4 only
+
+- [#1040](https://github.com/sveltejs/eslint-plugin-svelte/pull/1040) [`ac7115c`](https://github.com/sveltejs/eslint-plugin-svelte/commit/ac7115c74b3f1e82ef15be81d9ef2026da76bb11) Thanks [@baseballyama](https://github.com/baseballyama)! - fix: account for \n and \r in `TemplateLiteral` handling for the `no-useless-mustaches` rule
+
+- [#1020](https://github.com/sveltejs/eslint-plugin-svelte/pull/1020) [`eae0e2e`](https://github.com/sveltejs/eslint-plugin-svelte/commit/eae0e2e52c2812ea630eea45e5be4f439191c806) Thanks [@baseballyama](https://github.com/baseballyama)! - chore: update `svelte-eslint-parser` to `1.0.0-next.9`
+
+- [#1017](https://github.com/sveltejs/eslint-plugin-svelte/pull/1017) [`806d72a`](https://github.com/sveltejs/eslint-plugin-svelte/commit/806d72ad55908fb967252ea0514672b026a6bd09) Thanks [@baseballyama](https://github.com/baseballyama)! - chore: do not run `no-reactive-functions` rule on Svelte 5 with runes
+
+- [#1014](https://github.com/sveltejs/eslint-plugin-svelte/pull/1014) [`74373ec`](https://github.com/sveltejs/eslint-plugin-svelte/commit/74373ec77aa9063c64657faefc01ca331e27b599) Thanks [@baseballyama](https://github.com/baseballyama)! - chore: do not run `no-reactive-reassign` rule on Svelte 5 with runes
+
+- [#1026](https://github.com/sveltejs/eslint-plugin-svelte/pull/1026) [`1bed311`](https://github.com/sveltejs/eslint-plugin-svelte/commit/1bed311084c58a9893e2b578f8b34c2fcb2e8d8b) Thanks [@baseballyama](https://github.com/baseballyama)! - chore: update `svelte-eslint-parser` to `v1.0.0-next.12`
+
+- [#941](https://github.com/sveltejs/eslint-plugin-svelte/pull/941) [`fce2f74`](https://github.com/sveltejs/eslint-plugin-svelte/commit/fce2f74b4b2fe8185742f0411739103301f8ea9f) Thanks [@ota-meshi](https://github.com/ota-meshi)! - fix: crash with eslint v9.16.0 in `svelte/no-inner-declarations`
+
+- [#1018](https://github.com/sveltejs/eslint-plugin-svelte/pull/1018) [`5bdc906`](https://github.com/sveltejs/eslint-plugin-svelte/commit/5bdc906fc1e32835ab3b75dfd2a6a08b4cbb968b) Thanks [@baseballyama](https://github.com/baseballyama)! - chore: run the `no-inspect` rule only in Svelte 5’s runes mode
+
+- [#1006](https://github.com/sveltejs/eslint-plugin-svelte/pull/1006) [`5fd91ba`](https://github.com/sveltejs/eslint-plugin-svelte/commit/5fd91baa1835cbffc58a8c473ccf70f3f02a34e6) Thanks [@baseballyama](https://github.com/baseballyama)! - chore: do not run `infinite-reactive-loop` rule on Svelte 5 with runes
+
+- [#1031](https://github.com/sveltejs/eslint-plugin-svelte/pull/1031) [`e50d2d4`](https://github.com/sveltejs/eslint-plugin-svelte/commit/e50d2d4a8820c222696c049e0b01988626845584) Thanks [@baseballyama](https://github.com/baseballyama)! - chore: do not run `no-reactive-literals` rule on Svelte 5 with runes
+
+- [#1032](https://github.com/sveltejs/eslint-plugin-svelte/pull/1032) [`722b36c`](https://github.com/sveltejs/eslint-plugin-svelte/commit/722b36ce3b8a16b965f61701dead22b95c7e8b3d) Thanks [@baseballyama](https://github.com/baseballyama)! - chore: run `no-useless-children-snippet` rule on Svelte 5 only
+
+- [#1007](https://github.com/sveltejs/eslint-plugin-svelte/pull/1007) [`8e9199a`](https://github.com/sveltejs/eslint-plugin-svelte/commit/8e9199ae326110778e4b0557616d394c6ac5e847) Thanks [@baseballyama](https://github.com/baseballyama)! - fix: update method for extracting major version
+
 ## 3.0.0-next.18
 
 ### Patch Changes
diff --git a/packages/eslint-plugin-svelte/package.json b/packages/eslint-plugin-svelte/package.json
index c92be3b..ba245b4 100644
--- a/packages/eslint-plugin-svelte/package.json
+++ b/packages/eslint-plugin-svelte/package.json
@@ -1,6 +1,6 @@
 {
   "name": "eslint-plugin-svelte",
-  "version": "3.0.0-next.18",
+  "version": "3.1.0",
   "description": "ESLint plugin for Svelte using AST",
   "repository": "git+https://github.com/sveltejs/eslint-plugin-svelte.git",
   "homepage": "https://sveltejs.github.io/eslint-plugin-svelte",
@@ -8,7 +8,7 @@
   "funding": "https://github.com/sponsors/ota-meshi",
   "license": "MIT",
   "engines": {
-    "node": "^18.20.4 || ^20.18.0 || >=22.10.0"
+    "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
   },
   "type": "module",
   "main": "lib/index.js",
@@ -65,7 +65,7 @@
     "postcss-load-config": "^3.1.4",
     "postcss-safe-parser": "^7.0.0",
     "semver": "^7.6.3",
-    "svelte-eslint-parser": "^1.0.0-next.13"
+    "svelte-eslint-parser": "^1.0.1"
   },
   "devDependencies": {
     "@babel/core": "^7.26.0",
@@ -86,7 +86,7 @@
     "assert": "^2.1.0",
     "esbuild": "^0.25.0",
     "eslint-scope": "^8.2.0",
-    "eslint-typegen": "^1.0.0",
+    "eslint-typegen": "^2.0.0",
     "eslint-visitor-keys": "^4.2.0",
     "espree": "^10.3.0",
     "less": "^4.2.1",
diff --git a/packages/eslint-plugin-svelte/src/configs/flat/recommended.ts b/packages/eslint-plugin-svelte/src/configs/flat/recommended.ts
index fc7f089..4582266 100644
--- a/packages/eslint-plugin-svelte/src/configs/flat/recommended.ts
+++ b/packages/eslint-plugin-svelte/src/configs/flat/recommended.ts
@@ -32,13 +32,13 @@ const config: Linter.Config[] = [
 			'svelte/no-store-async': 'error',
 			'svelte/no-svelte-internal': 'error',
 			'svelte/no-unknown-style-directive-property': 'error',
+			'svelte/no-unused-props': 'error',
 			'svelte/no-unused-svelte-ignore': 'error',
 			'svelte/no-useless-children-snippet': 'error',
 			'svelte/no-useless-mustaches': 'error',
 			'svelte/require-each-key': 'error',
 			'svelte/require-event-dispatcher-types': 'error',
 			'svelte/require-store-reactive-access': 'error',
-			'svelte/require-stores-init': 'error',
 			'svelte/system': 'error',
 			'svelte/valid-each-key': 'error',
 			'svelte/valid-prop-names-in-kit-pages': 'error'
diff --git a/packages/eslint-plugin-svelte/src/meta.ts b/packages/eslint-plugin-svelte/src/meta.ts
index 8b63b17..a06738e 100644
--- a/packages/eslint-plugin-svelte/src/meta.ts
+++ b/packages/eslint-plugin-svelte/src/meta.ts
@@ -2,4 +2,4 @@
 // This file has been automatically generated,
 // in order to update its content execute "pnpm run update"
 export const name = 'eslint-plugin-svelte';
-export const version = '3.0.0-next.18';
+export const version = '3.1.0';
diff --git a/packages/eslint-plugin-svelte/src/rule-types.ts b/packages/eslint-plugin-svelte/src/rule-types.ts
index 30f15c1..f0bc2b2 100644
--- a/packages/eslint-plugin-svelte/src/rule-types.ts
+++ b/packages/eslint-plugin-svelte/src/rule-types.ts
@@ -261,6 +261,11 @@ export interface RuleOptions {
    * @see https://sveltejs.github.io/eslint-plugin-svelte/rules/no-unused-class-name/
    */
   'svelte/no-unused-class-name'?: Linter.RuleEntry<SvelteNoUnusedClassName>
+  /**
+   * Warns about defined Props properties that are unused
+   * @see https://sveltejs.github.io/eslint-plugin-svelte/rules/no-unused-props/
+   */
+  'svelte/no-unused-props'?: Linter.RuleEntry<SvelteNoUnusedProps>
   /**
    * disallow unused svelte-ignore comments
    * @see https://sveltejs.github.io/eslint-plugin-svelte/rules/no-unused-svelte-ignore/
@@ -366,6 +371,11 @@ export interface RuleOptions {
    * @see https://sveltejs.github.io/eslint-plugin-svelte/rules/valid-prop-names-in-kit-pages/
    */
   'svelte/valid-prop-names-in-kit-pages'?: Linter.RuleEntry<[]>
+  /**
+   * require valid style element parsing
+   * @see https://sveltejs.github.io/eslint-plugin-svelte/rules/valid-style-parse/
+   */
+  'svelte/valid-style-parse'?: Linter.RuleEntry<[]>
 }
 
 /* ======= Declarations ======= */
@@ -512,6 +522,11 @@ type SvelteNoUnknownStyleDirectiveProperty = []|[{
 type SvelteNoUnusedClassName = []|[{
   allowedClassNames?: string[]
 }]
+// ----- svelte/no-unused-props -----
+type SvelteNoUnusedProps = []|[{
+  checkImportedTypes?: boolean
+  ignorePatterns?: string[]
+}]
 // ----- svelte/no-useless-mustaches -----
 type SvelteNoUselessMustaches = []|[{
   ignoreIncludesComment?: boolean
@@ -525,6 +540,7 @@ type SveltePreferClassDirective = []|[{
 type SveltePreferConst = []|[{
   destructuring?: ("any" | "all")
   ignoreReadBeforeAssign?: boolean
+  excludedRunes?: string[]
 }]
 // ----- svelte/shorthand-attribute -----
 type SvelteShorthandAttribute = []|[{
diff --git a/packages/eslint-plugin-svelte/src/rules/consistent-selector-style.ts b/packages/eslint-plugin-svelte/src/rules/consistent-selector-style.ts
index 00134c5..5af44ce 100644
--- a/packages/eslint-plugin-svelte/src/rules/consistent-selector-style.ts
+++ b/packages/eslint-plugin-svelte/src/rules/consistent-selector-style.ts
@@ -62,6 +62,7 @@ export default createRule('consistent-selector-style', {
 		const checkGlobal = context.options[0]?.checkGlobal ?? false;
 		const style = context.options[0]?.style ?? ['type', 'id', 'class'];
 
+		const whitelistedClasses: string[] = [];
 		const classSelections: Map<string, AST.SvelteHTMLElement[]> = new Map();
 		const idSelections: Map<string, AST.SvelteHTMLElement[]> = new Map();
 		const typeSelections: Map<string, AST.SvelteHTMLElement[]> = new Map();
@@ -109,6 +110,9 @@ export default createRule('consistent-selector-style', {
 		 * Checks a class selector
 		 */
 		function checkClassSelector(node: SelectorClass): void {
+			if (whitelistedClasses.includes(node.value)) {
+				return;
+			}
 			const selection = classSelections.get(node.value) ?? [];
 			for (const styleValue of style) {
 				if (styleValue === 'class') {
@@ -194,6 +198,9 @@ export default createRule('consistent-selector-style', {
 					addToArrayMap(classSelections, className, node);
 				}
 				for (const attribute of node.startTag.attributes) {
+					if (attribute.type === 'SvelteDirective' && attribute.kind === 'Class') {
+						whitelistedClasses.push(attribute.key.name.name);
+					}
 					if (attribute.type !== 'SvelteAttribute' || attribute.key.name !== 'id') {
 						continue;
 					}
diff --git a/packages/eslint-plugin-svelte/src/rules/no-navigation-without-base.ts b/packages/eslint-plugin-svelte/src/rules/no-navigation-without-base.ts
index 6f2a209..aaeb4c6 100644
--- a/packages/eslint-plugin-svelte/src/rules/no-navigation-without-base.ts
+++ b/packages/eslint-plugin-svelte/src/rules/no-navigation-without-base.ts
@@ -96,14 +96,15 @@ export default createRule('no-navigation-without-base', {
 				}
 				const hrefValue = node.value[0];
 				if (hrefValue.type === 'SvelteLiteral') {
-					if (!expressionIsAbsolute(hrefValue)) {
+					if (!expressionIsAbsolute(hrefValue) && !expressionIsFragment(hrefValue)) {
 						context.report({ loc: hrefValue.loc, messageId: 'linkNotPrefixed' });
 					}
 					return;
 				}
 				if (
 					!expressionStartsWithBase(context, hrefValue.expression, basePathNames) &&
-					!expressionIsAbsolute(hrefValue.expression)
+					!expressionIsAbsolute(hrefValue.expression) &&
+					!expressionIsFragment(hrefValue.expression)
 				) {
 					context.report({ loc: hrefValue.loc, messageId: 'linkNotPrefixed' });
 				}
@@ -348,3 +349,33 @@ function templateLiteralIsAbsolute(url: TSESTree.TemplateLiteral): boolean {
 function urlValueIsAbsolute(url: string): boolean {
 	return url.includes('://');
 }
+
+function expressionIsFragment(url: SvelteLiteral | TSESTree.Expression): boolean {
+	switch (url.type) {
+		case 'BinaryExpression':
+			return binaryExpressionIsFragment(url);
+		case 'Literal':
+			return typeof url.value === 'string' && urlValueIsFragment(url.value);
+		case 'SvelteLiteral':
+			return urlValueIsFragment(url.value);
+		case 'TemplateLiteral':
+			return templateLiteralIsFragment(url);
+		default:
+			return false;
+	}
+}
+
+function binaryExpressionIsFragment(url: TSESTree.BinaryExpression): boolean {
+	return url.left.type !== 'PrivateIdentifier' && expressionIsFragment(url.left);
+}
+
+function templateLiteralIsFragment(url: TSESTree.TemplateLiteral): boolean {
+	return (
+		(url.expressions.length >= 1 && expressionIsFragment(url.expressions[0])) ||
+		(url.quasis.length >= 1 && urlValueIsFragment(url.quasis[0].value.raw))
+	);
+}
+
+function urlValueIsFragment(url: string): boolean {
+	return url.startsWith('#');
+}
diff --git a/packages/eslint-plugin-svelte/src/rules/no-unused-props.ts b/packages/eslint-plugin-svelte/src/rules/no-unused-props.ts
new file mode 100644
index 0000000..1588f3b
--- /dev/null
+++ b/packages/eslint-plugin-svelte/src/rules/no-unused-props.ts
@@ -0,0 +1,340 @@
+import { createRule } from '../utils/index.js';
+import { getTypeScriptTools } from '../utils/ts-utils/index.js';
+import type { TSESTree } from '@typescript-eslint/types';
+import type ts from 'typescript';
+import { findVariable } from '../utils/ast-utils.js';
+import { toRegExp } from '../utils/regexp.js';
+import { getFilename } from '../utils/compat.js';
+
+type PropertyPath = string[];
+
+export default createRule('no-unused-props', {
+	meta: {
+		docs: {
+			description: 'Warns about defined Props properties that are unused',
+			category: 'Best Practices',
+			recommended: true
+		},
+		schema: [
+			{
+				type: 'object',
+				properties: {
+					checkImportedTypes: {
+						type: 'boolean',
+						default: false
+					},
+					ignorePatterns: {
+						type: 'array',
+						items: {
+							type: 'string'
+						},
+						default: []
+					}
+				},
+				additionalProperties: false
+			}
+		],
+		messages: {
+			unusedProp: "'{{name}}' is an unused Props property.",
+			unusedNestedProp: "'{{name}}' in '{{parent}}' is an unused property.",
+			unusedIndexSignature:
+				'Index signature is unused. Consider using rest operator (...) to capture remaining properties.'
+		},
+		type: 'suggestion',
+		conditions: [
+			{
+				svelteVersions: ['5'],
+				runes: [true, 'undetermined']
+			}
+		]
+	},
+	create(context) {
+		const fileName = getFilename(context);
+		const tools = getTypeScriptTools(context);
+		if (!tools) {
+			return {};
+		}
+
+		const typeChecker = tools.service.program.getTypeChecker();
+		if (!typeChecker) {
+			return {};
+		}
+
+		const options = context.options[0] ?? {};
+		const checkImportedTypes = options.checkImportedTypes ?? false;
+		const ignorePatterns = (options.ignorePatterns ?? []).map((p: string | RegExp) => {
+			if (typeof p === 'string') {
+				return toRegExp(p);
+			}
+			return p;
+		});
+
+		function shouldIgnore(name: string): boolean {
+			return ignorePatterns.some((pattern: RegExp) => pattern.test(name));
+		}
+
+		function shouldIgnoreType(type: ts.Type): boolean {
+			const typeStr = typeChecker.typeToString(type);
+			const symbol = type.getSymbol();
+			const symbolName = symbol?.getName();
+			return shouldIgnore(typeStr) || (symbolName ? shouldIgnore(symbolName) : false);
+		}
+
+		function isExternalType(type: ts.Type): boolean {
+			const symbol = type.getSymbol();
+			if (!symbol) return false;
+
+			const declarations = symbol.getDeclarations();
+			if (!declarations || declarations.length === 0) return false;
+
+			const sourceFile = declarations[0].getSourceFile();
+			return sourceFile.fileName !== fileName;
+		}
+
+		/**
+		 * Extracts property paths from member expressions.
+		 */
+		function getPropertyPath(node: TSESTree.Identifier): PropertyPath {
+			const paths: PropertyPath = [];
+			let currentNode: TSESTree.Node = node;
+			let parentNode: TSESTree.Node | null = currentNode.parent ?? null;
+
+			while (parentNode) {
+				if (parentNode.type === 'MemberExpression' && parentNode.object === currentNode) {
+					const property = parentNode.property;
+					if (property.type === 'Identifier') {
+						paths.push(property.name);
+					} else if (property.type === 'Literal' && typeof property.value === 'string') {
+						paths.push(property.value);
+					} else {
+						break;
+					}
+				}
+				currentNode = parentNode;
+				parentNode = currentNode.parent ?? null;
+			}
+
+			return paths;
+		}
+
+		/**
+		 * Finds all property access paths for a given variable.
+		 */
+		function getUsedNestedPropertyNames(node: TSESTree.Identifier): PropertyPath[] {
+			const variable = findVariable(context, node);
+			if (!variable) return [];
+
+			const paths: PropertyPath[] = [];
+			for (const reference of variable.references) {
+				if ('identifier' in reference && reference.identifier.type === 'Identifier') {
+					const referencePath = getPropertyPath(reference.identifier);
+					paths.push(referencePath);
+				}
+			}
+			return paths;
+		}
+
+		/**
+		 * Checks if a property is from TypeScript's built-in type definitions.
+		 * These properties should be ignored as they are not user-defined props.
+		 */
+		function isBuiltInProperty(prop: ts.Symbol): boolean {
+			const declarations = prop.getDeclarations();
+			if (!declarations || declarations.length === 0) return false;
+
+			const declaration = declarations[0];
+			const sourceFile = declaration.getSourceFile();
+			if (!sourceFile) return false;
+			return sourceFile.fileName.includes('node_modules/typescript/lib/');
+		}
+
+		function getUsedPropertiesFromPattern(pattern: TSESTree.ObjectPattern): Set<string> {
+			const usedProps = new Set<string>();
+			for (const prop of pattern.properties) {
+				if (prop.type === 'Property' && prop.key.type === 'Identifier') {
+					usedProps.add(prop.key.name);
+				} else if (prop.type === 'RestElement') {
+					// If there's a rest element, all properties are potentially used
+					return new Set();
+				}
+			}
+			return usedProps;
+		}
+
+		/**
+		 * Check if the type is a class type (has constructor or prototype)
+		 */
+		function isClassType(type: ts.Type): boolean {
+			if (!type) return false;
+
+			// Check if it's a class instance type
+			if (type.isClass()) return true;
+
+			// Check for constructor signatures
+			const constructorType = type.getConstructSignatures();
+			if (constructorType.length > 0) return true;
+
+			// Check if it has a prototype property
+			const symbol = type.getSymbol();
+			if (symbol?.members?.has('prototype' as ts.__String)) return true;
+
+			return false;
+		}
+
+		/**
+		 * Recursively checks for unused properties in a type.
+		 */
+		function checkUnusedProperties(
+			type: ts.Type,
+			usedPaths: PropertyPath[],
+			usedProps: Set<string>,
+			reportNode: TSESTree.Node,
+			parentPath: string[],
+			checkedTypes: Set<string>,
+			reportedProps: Set<string>
+		) {
+			// Skip checking if the type itself is a class
+			if (isClassType(type)) return;
+
+			const typeStr = typeChecker.typeToString(type);
+			if (checkedTypes.has(typeStr)) return;
+			checkedTypes.add(typeStr);
+			if (shouldIgnoreType(type)) return;
+			if (!checkImportedTypes && isExternalType(type)) return;
+
+			const properties = typeChecker.getPropertiesOfType(type);
+			const baseTypes = type.getBaseTypes();
+
+			if (!properties.length && (!baseTypes || baseTypes.length === 0)) {
+				return;
+			}
+
+			if (baseTypes) {
+				for (const baseType of baseTypes) {
+					checkUnusedProperties(
+						baseType,
+						usedPaths,
+						usedProps,
+						reportNode,
+						parentPath,
+						checkedTypes,
+						reportedProps
+					);
+				}
+			}
+
+			for (const prop of properties) {
+				if (isBuiltInProperty(prop)) continue;
+
+				const propName = prop.getName();
+				const currentPath = [...parentPath, propName];
+				const currentPathStr = [...parentPath, propName].join('.');
+
+				if (reportedProps.has(currentPathStr)) continue;
+
+				const propType = typeChecker.getTypeOfSymbol(prop);
+				const isUsedInPath = usedPaths.some((path) => {
+					const usedPath = path.join('.');
+					return usedPath === currentPathStr || usedPath.startsWith(`${currentPathStr}.`);
+				});
+
+				const isUsedInProps = usedProps.has(propName);
+
+				if (!isUsedInPath && !isUsedInProps) {
+					reportedProps.add(currentPathStr);
+					context.report({
+						node: reportNode,
+						messageId: parentPath.length ? 'unusedNestedProp' : 'unusedProp',
+						data: {
+							name: propName,
+							parent: parentPath.join('.')
+						}
+					});
+				}
+
+				const isUsedNested = usedPaths.some((path) => {
+					return path.join('.').startsWith(`${currentPathStr}.`);
+				});
+
+				if (isUsedNested || isUsedInProps) {
+					checkUnusedProperties(
+						propType,
+						usedPaths,
+						usedProps,
+						reportNode,
+						currentPath,
+						checkedTypes,
+						reportedProps
+					);
+				}
+			}
+
+			// Check for unused index signatures only at the root level
+			if (parentPath.length === 0) {
+				const indexType = type.getStringIndexType();
+				const numberIndexType = type.getNumberIndexType();
+				const hasIndexSignature = Boolean(indexType) || Boolean(numberIndexType);
+
+				if (hasIndexSignature && !hasRestElement(usedProps)) {
+					context.report({
+						node: reportNode,
+						messageId: 'unusedIndexSignature'
+					});
+				}
+			}
+		}
+
+		/**
+		 * Returns true if the destructuring pattern includes a rest element,
+		 * which means all remaining properties are potentially used.
+		 */
+		function hasRestElement(usedProps: Set<string>): boolean {
+			return usedProps.size === 0;
+		}
+
+		return {
+			'VariableDeclaration > VariableDeclarator': (node: TSESTree.VariableDeclarator) => {
+				// Only check $props declarations
+				if (
+					node.init?.type !== 'CallExpression' ||
+					node.init.callee.type !== 'Identifier' ||
+					node.init.callee.name !== '$props'
+				) {
+					return;
+				}
+
+				const tsNode = tools.service.esTreeNodeToTSNodeMap.get(node) as ts.VariableDeclaration;
+				if (!tsNode || !tsNode.type) return;
+
+				const propType = typeChecker.getTypeFromTypeNode(tsNode.type);
+				let usedPaths: PropertyPath[] = [];
+				let usedProps = new Set<string>();
+
+				if (node.id.type === 'ObjectPattern') {
+					usedProps = getUsedPropertiesFromPattern(node.id);
+					if (usedProps.size === 0) return;
+					const identifiers = node.id.properties
+						.filter((p): p is TSESTree.Property => p.type === 'Property')
+						.map((p) => p.value)
+						.filter((v): v is TSESTree.Identifier => v.type === 'Identifier');
+					for (const identifier of identifiers) {
+						const paths = getUsedNestedPropertyNames(identifier);
+						usedPaths.push(...paths);
+					}
+				} else if (node.id.type === 'Identifier') {
+					usedPaths = getUsedNestedPropertyNames(node.id);
+				}
+
+				checkUnusedProperties(
+					propType,
+					usedPaths,
+					usedProps,
+					node.id,
+					[],
+					new Set<string>(),
+					new Set<string>()
+				);
+			}
+		};
+	}
+});
diff --git a/packages/eslint-plugin-svelte/src/rules/no-useless-mustaches.ts b/packages/eslint-plugin-svelte/src/rules/no-useless-mustaches.ts
index 169cc8e..05d3af2 100644
--- a/packages/eslint-plugin-svelte/src/rules/no-useless-mustaches.ts
+++ b/packages/eslint-plugin-svelte/src/rules/no-useless-mustaches.ts
@@ -85,6 +85,10 @@ export default createRule('no-useless-mustaches', {
 				return;
 			}
 
+			if (rawValue.includes('{')) {
+				return;
+			}
+
 			let hasEscape = false;
 			if (rawValue !== strValue) {
 				// check escapes
diff --git a/packages/eslint-plugin-svelte/src/rules/prefer-const.ts b/packages/eslint-plugin-svelte/src/rules/prefer-const.ts
index 0d079b1..842bbc9 100644
--- a/packages/eslint-plugin-svelte/src/rules/prefer-const.ts
+++ b/packages/eslint-plugin-svelte/src/rules/prefer-const.ts
@@ -21,7 +21,7 @@ function findDeclarationCallee(node: TSESTree.Expression) {
  * Determines if a declaration should be skipped in the const preference analysis.
  * Specifically checks for Svelte's state management utilities ($props, $derived).
  */
-function shouldSkipDeclaration(declaration: TSESTree.Expression | null) {
+function shouldSkipDeclaration(declaration: TSESTree.Expression | null, excludedRunes: string[]) {
 	if (!declaration) {
 		return false;
 	}
@@ -31,7 +31,7 @@ function shouldSkipDeclaration(declaration: TSESTree.Expression | null) {
 		return false;
 	}
 
-	if (callee.type === 'Identifier' && ['$props', '$derived'].includes(callee.name)) {
+	if (callee.type === 'Identifier' && excludedRunes.includes(callee.name)) {
 		return true;
 	}
 
@@ -39,11 +39,7 @@ function shouldSkipDeclaration(declaration: TSESTree.Expression | null) {
 		return false;
 	}
 
-	if (
-		callee.object.name === '$derived' &&
-		callee.property.type === 'Identifier' &&
-		callee.property.name === 'by'
-	) {
+	if (excludedRunes.includes(callee.object.name)) {
 		return true;
 	}
 
@@ -58,16 +54,35 @@ export default createRule('prefer-const', {
 			category: 'Best Practices',
 			recommended: false,
 			extensionRule: 'prefer-const'
-		}
+		},
+		schema: [
+			{
+				type: 'object',
+				properties: {
+					destructuring: { enum: ['any', 'all'] },
+					ignoreReadBeforeAssign: { type: 'boolean' },
+					excludedRunes: {
+						type: 'array',
+						items: {
+							type: 'string'
+						}
+					}
+				},
+				additionalProperties: false
+			}
+		]
 	},
 	create(context) {
+		const config = context.options[0] ?? {};
+		const excludedRunes = config.excludedRunes ?? ['$props', '$derived'];
+
 		return defineWrapperListener(coreRule, context, {
 			createListenerProxy(coreListener) {
 				return {
 					...coreListener,
 					VariableDeclaration(node) {
 						for (const decl of node.declarations) {
-							if (shouldSkipDeclaration(decl.init)) {
+							if (shouldSkipDeclaration(decl.init, excludedRunes)) {
 								return;
 							}
 						}
diff --git a/packages/eslint-plugin-svelte/src/rules/require-stores-init.ts b/packages/eslint-plugin-svelte/src/rules/require-stores-init.ts
index 820267e..7683c1e 100644
--- a/packages/eslint-plugin-svelte/src/rules/require-stores-init.ts
+++ b/packages/eslint-plugin-svelte/src/rules/require-stores-init.ts
@@ -6,7 +6,9 @@ export default createRule('require-stores-init', {
 		docs: {
 			description: 'require initial value in store',
 			category: 'Best Practices',
-			recommended: true
+			// Please refer to the following before setting recommended to true.
+			// https://github.com/sveltejs/eslint-plugin-svelte/issues/1073
+			recommended: false
 		},
 		schema: [],
 		messages: {
diff --git a/packages/eslint-plugin-svelte/src/rules/valid-prop-names-in-kit-pages.ts b/packages/eslint-plugin-svelte/src/rules/valid-prop-names-in-kit-pages.ts
index ab3f9d5..5554b9c 100644
--- a/packages/eslint-plugin-svelte/src/rules/valid-prop-names-in-kit-pages.ts
+++ b/packages/eslint-plugin-svelte/src/rules/valid-prop-names-in-kit-pages.ts
@@ -3,7 +3,6 @@ import type { TSESTree } from '@typescript-eslint/types';
 import { createRule } from '../utils/index.js';
 import type { RuleContext } from '../types.js';
 import { getSvelteVersion } from '../utils/svelte-context.js';
-import { getFilename } from '../utils/compat.js';
 
 const EXPECTED_PROP_NAMES = ['data', 'errors', 'form', 'snapshot'];
 const EXPECTED_PROP_NAMES_SVELTE5 = [...EXPECTED_PROP_NAMES, 'children'];
@@ -17,18 +16,35 @@ function checkProp(
 	for (const p of node.id.properties) {
 		if (
 			p.type === 'Property' &&
-			p.value.type === 'Identifier' &&
-			!expectedPropNames.includes(p.value.name)
+			p.key.type === 'Identifier' &&
+			!expectedPropNames.includes(p.key.name)
 		) {
 			context.report({
-				node: p.value,
-				loc: p.value.loc,
+				node: p.key,
+				loc: p.key.loc,
 				messageId: 'unexpected'
 			});
 		}
 	}
 }
 
+function isModuleScript(node: AST.SvelteAttribute) {
+	// <script context="module">
+	if (
+		node.key.name === 'context' &&
+		node.value.some((v) => v.type === 'SvelteLiteral' && v.value === 'module')
+	) {
+		return true;
+	}
+
+	// <script module>
+	if (node.key.name === 'module' && node.value.length === 0) {
+		return true;
+	}
+
+	return false;
+}
+
 export default createRule('valid-prop-names-in-kit-pages', {
 	meta: {
 		docs: {
@@ -49,18 +65,13 @@ export default createRule('valid-prop-names-in-kit-pages', {
 	},
 	create(context) {
 		let isScript = false;
-		const isSvelte5 = getSvelteVersion(getFilename(context)) === '5';
+		const isSvelte5 = getSvelteVersion() === '5';
 		const expectedPropNames = isSvelte5 ? EXPECTED_PROP_NAMES_SVELTE5 : EXPECTED_PROP_NAMES;
 		return {
 			// <script>
 			'Program > SvelteScriptElement > SvelteStartTag': (node: AST.SvelteStartTag) => {
 				// except for <script context="module">
-				isScript = !node.attributes.some(
-					(a) =>
-						a.type === 'SvelteAttribute' &&
-						a.key.name === 'context' &&
-						a.value.some((v) => v.type === 'SvelteLiteral' && v.value === 'module')
-				);
+				isScript = !node.attributes.some((a) => a.type === 'SvelteAttribute' && isModuleScript(a));
 			},
 
 			// </script>
diff --git a/packages/eslint-plugin-svelte/src/rules/valid-style-parse.ts b/packages/eslint-plugin-svelte/src/rules/valid-style-parse.ts
new file mode 100644
index 0000000..685c68a
--- /dev/null
+++ b/packages/eslint-plugin-svelte/src/rules/valid-style-parse.ts
@@ -0,0 +1,40 @@
+import { createRule } from '../utils/index.js';
+import { getCwd, getSourceCode } from '../utils/compat.js';
+
+export default createRule('valid-style-parse', {
+	meta: {
+		docs: {
+			description: 'require valid style element parsing',
+			category: 'Possible Errors',
+			recommended: false
+		},
+		schema: [],
+		messages: {},
+		type: 'problem'
+	},
+	create(context) {
+		const sourceCode = getSourceCode(context);
+		if (!sourceCode.parserServices.isSvelte) {
+			return {};
+		}
+		const cwd = `${getCwd(context)}/`;
+
+		return {
+			SvelteStyleElement(node) {
+				const styleContext = sourceCode.parserServices.getStyleContext!();
+				if (styleContext.status === 'parse-error') {
+					context.report({
+						loc: node.loc,
+						message: `Error parsing style element. Error message: "${styleContext.error.message.replace(cwd, '')}"`
+					});
+				}
+				if (styleContext.status === 'unknown-lang') {
+					context.report({
+						loc: node.loc,
+						message: `Found unsupported style element language "${styleContext.sourceLang}"`
+					});
+				}
+			}
+		};
+	}
+});
diff --git a/packages/eslint-plugin-svelte/src/utils/get-node-module.ts b/packages/eslint-plugin-svelte/src/utils/get-node-module.ts
new file mode 100644
index 0000000..9b2a445
--- /dev/null
+++ b/packages/eslint-plugin-svelte/src/utils/get-node-module.ts
@@ -0,0 +1,71 @@
+import fs from 'fs';
+import path from 'path';
+import { createCache } from './cache.js';
+
+const isRunOnBrowser = !fs.readFileSync;
+const nodeModuleCache = createCache<string | null>();
+const nodeModulesCache = createCache<string[]>();
+
+/**
+ * Find package directory in node_modules
+ */
+function findPackageInNodeModules(dir: string, packageName: string): string | null {
+	if (isRunOnBrowser) return null;
+
+	const nodeModulesPath = path.join(dir, 'node_modules');
+	const packagePath = path.join(nodeModulesPath, packageName);
+
+	try {
+		const stats = fs.statSync(packagePath);
+		if (stats.isDirectory()) {
+			return packagePath;
+		}
+	} catch {
+		// ignore if directory not found
+	}
+
+	return null;
+}
+
+/**
+ * Get first found package path from node_modules
+ */
+export function getNodeModule(packageName: string, startPath = 'a.js'): string | null {
+	if (isRunOnBrowser) return null;
+
+	const cacheKey = `${startPath}:${packageName}`;
+	const cached = nodeModulesCache.get(cacheKey);
+	if (cached) {
+		return cached[0] ?? null;
+	}
+
+	const startDir = path.dirname(path.resolve(startPath));
+	let dir = startDir;
+	let prevDir = '';
+
+	do {
+		// check cache
+		const cachePath = nodeModuleCache.get(`${dir}:${packageName}`);
+		if (cachePath) {
+			if (cachePath !== null) {
+				nodeModulesCache.set(cacheKey, [cachePath]);
+				return cachePath;
+			}
+		} else {
+			// search new
+			const packagePath = findPackageInNodeModules(dir, packageName);
+			nodeModuleCache.set(`${dir}:${packageName}`, packagePath);
+			if (packagePath) {
+				nodeModulesCache.set(cacheKey, [packagePath]);
+				return packagePath;
+			}
+		}
+
+		// go to parent
+		prevDir = dir;
+		dir = path.resolve(dir, '..');
+	} while (dir !== prevDir);
+
+	nodeModulesCache.set(cacheKey, []);
+	return null;
+}
diff --git a/packages/eslint-plugin-svelte/src/utils/rules.ts b/packages/eslint-plugin-svelte/src/utils/rules.ts
index 2a2ddee..2242164 100644
--- a/packages/eslint-plugin-svelte/src/utils/rules.ts
+++ b/packages/eslint-plugin-svelte/src/utils/rules.ts
@@ -51,6 +51,7 @@ import noTargetBlank from '../rules/no-target-blank.js';
 import noTrailingSpaces from '../rules/no-trailing-spaces.js';
 import noUnknownStyleDirectiveProperty from '../rules/no-unknown-style-directive-property.js';
 import noUnusedClassName from '../rules/no-unused-class-name.js';
+import noUnusedProps from '../rules/no-unused-props.js';
 import noUnusedSvelteIgnore from '../rules/no-unused-svelte-ignore.js';
 import noUselessChildrenSnippet from '../rules/no-useless-children-snippet.js';
 import noUselessMustaches from '../rules/no-useless-mustaches.js';
@@ -72,6 +73,7 @@ import system from '../rules/system.js';
 import validCompile from '../rules/valid-compile.js';
 import validEachKey from '../rules/valid-each-key.js';
 import validPropNamesInKitPages from '../rules/valid-prop-names-in-kit-pages.js';
+import validStyleParse from '../rules/valid-style-parse.js';
 
 export const rules = [
 	typescriptEslintNoUnnecessaryCondition,
@@ -123,6 +125,7 @@ export const rules = [
 	noTrailingSpaces,
 	noUnknownStyleDirectiveProperty,
 	noUnusedClassName,
+	noUnusedProps,
 	noUnusedSvelteIgnore,
 	noUselessChildrenSnippet,
 	noUselessMustaches,
@@ -143,5 +146,6 @@ export const rules = [
 	system,
 	validCompile,
 	validEachKey,
-	validPropNamesInKitPages
+	validPropNamesInKitPages,
+	validStyleParse
 ] as RuleModule[];
diff --git a/packages/eslint-plugin-svelte/src/utils/svelte-context.ts b/packages/eslint-plugin-svelte/src/utils/svelte-context.ts
index e821e3a..dfcc9ae 100644
--- a/packages/eslint-plugin-svelte/src/utils/svelte-context.ts
+++ b/packages/eslint-plugin-svelte/src/utils/svelte-context.ts
@@ -2,8 +2,10 @@ import type { RuleContext } from '../types.js';
 import fs from 'fs';
 import path from 'path';
 import { getPackageJsons } from './get-package-json.js';
+import { getNodeModule } from './get-node-module.js';
 import { getFilename, getSourceCode } from './compat.js';
 import { createCache } from './cache.js';
+import { VERSION as SVELTE_VERSION } from 'svelte/compiler';
 
 const isRunInBrowser = !fs.readFileSync;
 
@@ -60,7 +62,7 @@ function getSvelteFileType(filePath: string): SvelteContext['svelteFileType'] {
 }
 
 function getSvelteKitFileTypeFromFilePath(filePath: string): SvelteContext['svelteKitFileType'] {
-	const fileName = filePath.split('/').pop();
+	const fileName = filePath.split(/[/\\]/).pop();
 	switch (fileName) {
 		case '+page.svelte': {
 			return '+page.svelte';
@@ -168,51 +170,37 @@ function getSvelteKitContext(
 	return result;
 }
 
-const svelteVersionCache = createCache<SvelteContext['svelteVersion']>();
-
-export function getSvelteVersion(filePath: string): SvelteContext['svelteVersion'] {
-	const cached = svelteVersionCache.get(filePath);
-	if (cached) return cached;
+function checkAndSetSvelteVersion(version: string): SvelteContext['svelteVersion'] | null {
+	const major = extractMajorVersion(version, false);
+	if (major == null) {
+		return null;
+	}
+	if (major === '3' || major === '4') {
+		return '3/4';
+	}
+	return major as SvelteContext['svelteVersion'];
+}
 
+export function getSvelteVersion(): SvelteContext['svelteVersion'] {
 	// Hack: if it runs in browser, it regards as Svelte project.
 	if (isRunInBrowser) {
-		svelteVersionCache.set(filePath, '5');
 		return '5';
 	}
 
-	try {
-		const packageJsons = getPackageJsons(filePath);
-		for (const packageJson of packageJsons) {
-			const version = packageJson.dependencies?.svelte ?? packageJson.devDependencies?.svelte;
-			if (typeof version !== 'string') {
-				continue;
-			}
-			const major = extractMajorVersion(version, false);
-			if (major === '3' || major === '4') {
-				svelteVersionCache.set(filePath, '3/4');
-				return '3/4';
-			}
-			svelteVersionCache.set(filePath, major as SvelteContext['svelteVersion']);
-			return major as SvelteContext['svelteVersion'];
-		}
-	} catch {
-		/** do nothing */
-	}
-
-	svelteVersionCache.set(filePath, null);
-	return null;
+	return checkAndSetSvelteVersion(SVELTE_VERSION);
 }
 
 const svelteKitVersionCache = createCache<SvelteContext['svelteKitVersion']>();
 
-/**
- * Check givin file is under SvelteKit project.
- *
- * If it runs on browser, it always returns true.
- *
- * @param filePath A file path.
- * @returns
- */
+function checkAndSetSvelteKitVersion(
+	version: string,
+	filePath: string
+): SvelteContext['svelteKitVersion'] {
+	const major = extractMajorVersion(version, true) as SvelteContext['svelteKitVersion'];
+	svelteKitVersionCache.set(filePath, major);
+	return major;
+}
+
 function getSvelteKitVersion(filePath: string): SvelteContext['svelteKitVersion'] {
 	const cached = svelteKitVersionCache.get(filePath);
 	if (cached) return cached;
@@ -223,27 +211,42 @@ function getSvelteKitVersion(filePath: string): SvelteContext['svelteKitVersion'
 		return '2';
 	}
 
+	const nodeModule = getNodeModule('@sveltejs/kit', filePath);
+	if (nodeModule) {
+		try {
+			const packageJson = JSON.parse(
+				fs.readFileSync(path.join(nodeModule, 'package.json'), 'utf8')
+			);
+			const result = checkAndSetSvelteKitVersion(packageJson.version, filePath);
+			if (result != null) {
+				return result;
+			}
+		} catch {
+			/** do nothing */
+		}
+	}
+
 	try {
 		const packageJsons = getPackageJsons(filePath);
-		if (packageJsons.length === 0) return null;
-		if (packageJsons[0].name === 'eslint-plugin-svelte') {
-			// Hack: CI removes `@sveltejs/kit` and it returns false and test failed.
-			// So always it returns 2 if it runs on the package.
-			svelteKitVersionCache.set(filePath, '2');
-			return '2';
-		}
+		if (packageJsons.length > 0) {
+			if (packageJsons[0].name === 'eslint-plugin-svelte') {
+				// Hack: CI removes `@sveltejs/kit` and it returns false and test failed.
+				// So always it returns 2 if it runs on the package.
+				svelteKitVersionCache.set(filePath, '2');
+				return '2';
+			}
 
-		for (const packageJson of packageJsons) {
-			const version =
-				packageJson.dependencies?.['@sveltejs/kit'] ??
-				packageJson.devDependencies?.['@sveltejs/kit'];
-			if (typeof version !== 'string') {
-				svelteKitVersionCache.set(filePath, null);
-				return null;
+			for (const packageJson of packageJsons) {
+				const version =
+					packageJson.dependencies?.['@sveltejs/kit'] ??
+					packageJson.devDependencies?.['@sveltejs/kit'];
+				if (typeof version === 'string') {
+					const result = checkAndSetSvelteKitVersion(version, filePath);
+					if (result != null) {
+						return result;
+					}
+				}
 			}
-			const major = extractMajorVersion(version, true) as SvelteContext['svelteKitVersion'];
-			svelteKitVersionCache.set(filePath, major);
-			return major;
 		}
 	} catch {
 		/** do nothing */
@@ -291,7 +294,7 @@ export function getSvelteContext(context: RuleContext): SvelteContext | null {
 	if (cached) return cached;
 
 	const svelteKitContext = getSvelteKitContext(context);
-	const svelteVersion = getSvelteVersion(filePath);
+	const svelteVersion = getSvelteVersion();
 	const svelteFileType = getSvelteFileType(filePath);
 
 	if (svelteVersion == null) {


Test patch:
diff --git a/packages/eslint-plugin-svelte/tests/fixtures/rules/consistent-selector-style/valid/id-class-type/class01-input.svelte b/packages/eslint-plugin-svelte/tests/fixtures/rules/consistent-selector-style/valid/id-class-type/class01-input.svelte
index f3564d8..b849abc 100644
--- a/packages/eslint-plugin-svelte/tests/fixtures/rules/consistent-selector-style/valid/id-class-type/class01-input.svelte
+++ b/packages/eslint-plugin-svelte/tests/fixtures/rules/consistent-selector-style/valid/id-class-type/class01-input.svelte
@@ -8,6 +8,8 @@
 
 <b data-key="val">Text 2</b>
 
+<b class:conditional={true}>Text 3</b>
+
 <style>
   .link {
     color: red;
@@ -32,4 +34,8 @@
   .bold::before {
     color: red;
   }
+
+  .conditional {
+    color: red;
+  }
 </style>
diff --git a/packages/eslint-plugin-svelte/tests/fixtures/rules/consistent-selector-style/valid/id-type-class/class01-input.svelte b/packages/eslint-plugin-svelte/tests/fixtures/rules/consistent-selector-style/valid/id-type-class/class01-input.svelte
index 878799b..ea39102 100644
--- a/packages/eslint-plugin-svelte/tests/fixtures/rules/consistent-selector-style/valid/id-type-class/class01-input.svelte
+++ b/packages/eslint-plugin-svelte/tests/fixtures/rules/consistent-selector-style/valid/id-type-class/class01-input.svelte
@@ -8,6 +8,8 @@
 
 <b data-key="val">Text 2</b>
 
+<b class:conditional={true}>Text 3</b>
+
 <style>
   .link {
     color: red;
@@ -32,4 +34,8 @@
   .bold::before {
     color: red;
   }
+
+  .conditional {
+    color: red;
+  }
 </style>
diff --git a/packages/eslint-plugin-svelte/tests/fixtures/rules/consistent-selector-style/valid/type-class-id/class01-input.svelte b/packages/eslint-plugin-svelte/tests/fixtures/rules/consistent-selector-style/valid/type-class-id/class01-input.svelte
index b770c05..01c9315 100644
--- a/packages/eslint-plugin-svelte/tests/fixtures/rules/consistent-selector-style/valid/type-class-id/class01-input.svelte
+++ b/packages/eslint-plugin-svelte/tests/fixtures/rules/consistent-selector-style/valid/type-class-id/class01-input.svelte
@@ -8,6 +8,8 @@
 
 <b data-key="val">Text 3</b>
 
+<b class:conditional={true}>Text 4</b>
+
 <style>
   .link {
     color: red;
@@ -32,4 +34,8 @@
   .bold::before {
     color: red;
   }
+
+  .conditional {
+    color: red;
+  }
 </style>
diff --git a/packages/eslint-plugin-svelte/tests/fixtures/rules/consistent-selector-style/valid/type-id-class/class01-input.svelte b/packages/eslint-plugin-svelte/tests/fixtures/rules/consistent-selector-style/valid/type-id-class/class01-input.svelte
index 92e8c4e..18eb6ab 100644
--- a/packages/eslint-plugin-svelte/tests/fixtures/rules/consistent-selector-style/valid/type-id-class/class01-input.svelte
+++ b/packages/eslint-plugin-svelte/tests/fixtures/rules/consistent-selector-style/valid/type-id-class/class01-input.svelte
@@ -10,6 +10,12 @@
 
 <b data-key="val">Text 3</b>
 
+<b class:conditional={true}>Text 4</b>
+
+<b class="conditional-two">Text 5</b>
+
+<b class:conditional-two={true}>Text 6</b>
+
 <style>
   .link {
     color: red;
@@ -34,4 +40,12 @@
   .bold::before {
     color: red;
   }
+
+  .conditional {
+    color: red;
+  }
+
+  .conditional-two {
+    color: red;
+  }
 </style>
diff --git a/packages/eslint-plugin-svelte/tests/fixtures/rules/consistent-selector-style/valid/type-id/class01-input.svelte b/packages/eslint-plugin-svelte/tests/fixtures/rules/consistent-selector-style/valid/type-id/class01-input.svelte
index 92e8c4e..d9a2ea3 100644
--- a/packages/eslint-plugin-svelte/tests/fixtures/rules/consistent-selector-style/valid/type-id/class01-input.svelte
+++ b/packages/eslint-plugin-svelte/tests/fixtures/rules/consistent-selector-style/valid/type-id/class01-input.svelte
@@ -10,6 +10,8 @@
 
 <b data-key="val">Text 3</b>
 
+<b class:conditinal={true}>Text 4</b>
+
 <style>
   .link {
     color: red;
@@ -34,4 +36,8 @@
   .bold::before {
     color: red;
   }
+
+  .conditinal {
+    color: red;
+  }
 </style>
diff --git a/packages/eslint-plugin-svelte/tests/fixtures/rules/consistent-selector-style/valid/type/class01-input.svelte b/packages/eslint-plugin-svelte/tests/fixtures/rules/consistent-selector-style/valid/type/class01-input.svelte
index 2f1bc20..73e331a 100644
--- a/packages/eslint-plugin-svelte/tests/fixtures/rules/consistent-selector-style/valid/type/class01-input.svelte
+++ b/packages/eslint-plugin-svelte/tests/fixtures/rules/consistent-selector-style/valid/type/class01-input.svelte
@@ -12,6 +12,8 @@
 
 <b data-key="val">Text 3</b>
 
+<b class:conditional={true}>Text 4</b>
+
 <style>
   .link {
     color: red;
@@ -41,4 +43,7 @@
     color: red;
   }
 
+  .conditional {
+    color: red;
+  }
 </style>
diff --git a/packages/eslint-plugin-svelte/tests/fixtures/rules/no-navigation-without-base/invalid/link-with-fragment01-errors.yaml b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-navigation-without-base/invalid/link-with-fragment01-errors.yaml
new file mode 100644
index 0000000..e006d4d
--- /dev/null
+++ b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-navigation-without-base/invalid/link-with-fragment01-errors.yaml
@@ -0,0 +1,16 @@
+- message: Found a link with a url that isn't prefixed with the base path.
+  line: 4
+  column: 10
+  suggestions: null
+- message: Found a link with a url that isn't prefixed with the base path.
+  line: 5
+  column: 9
+  suggestions: null
+- message: Found a link with a url that isn't prefixed with the base path.
+  line: 6
+  column: 9
+  suggestions: null
+- message: Found a link with a url that isn't prefixed with the base path.
+  line: 7
+  column: 9
+  suggestions: null
diff --git a/packages/eslint-plugin-svelte/tests/fixtures/rules/no-navigation-without-base/invalid/link-with-fragment01-input.svelte b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-navigation-without-base/invalid/link-with-fragment01-input.svelte
new file mode 100644
index 0000000..2e6a11d
--- /dev/null
+++ b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-navigation-without-base/invalid/link-with-fragment01-input.svelte
@@ -0,0 +1,7 @@
+<script>
+	const value = "/foo#section";
+</script>
+<a href="/foo#section">Click me!</a>
+<a href={'/foo#section'}>Click me!</a>
+<a href={'/' + 'foo#section'}>Click me!</a>
+<a href={value}>Click me!</a>
diff --git a/packages/eslint-plugin-svelte/tests/fixtures/rules/no-navigation-without-base/valid/link-fragment-url01-input.svelte b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-navigation-without-base/valid/link-fragment-url01-input.svelte
new file mode 100644
index 0000000..eeaa162
--- /dev/null
+++ b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-navigation-without-base/valid/link-fragment-url01-input.svelte
@@ -0,0 +1,10 @@
+<script>
+	const section = 'sectionName';
+</script>
+
+<a href="#">Click me!</a>
+<a href="#section">Click me!</a>
+<a href={'#section'}>Click me!</a>
+<a href={'#' + 'section'}>Click me!</a>
+<a href={'#' + section}>Click me!</a>
+<a href={`#${section}`}>Click me!</a>
diff --git a/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/invalid/_requirements.json b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/invalid/_requirements.json
new file mode 100644
index 0000000..0192b10
--- /dev/null
+++ b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/invalid/_requirements.json
@@ -0,0 +1,3 @@
+{
+	"svelte": ">=5.0.0-0"
+}
diff --git a/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/invalid/builtin-shadow-unused-errors.yaml b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/invalid/builtin-shadow-unused-errors.yaml
new file mode 100644
index 0000000..e1178cc
--- /dev/null
+++ b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/invalid/builtin-shadow-unused-errors.yaml
@@ -0,0 +1,4 @@
+- message: "'toString' is an unused Props property."
+  line: 5
+  column: 6
+  suggestions: null
diff --git a/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/invalid/builtin-shadow-unused-input.svelte b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/invalid/builtin-shadow-unused-input.svelte
new file mode 100644
index 0000000..2346d9f
--- /dev/null
+++ b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/invalid/builtin-shadow-unused-input.svelte
@@ -0,0 +1,7 @@
+<script lang="ts">
+	interface Props {
+		toString: () => string;
+	}
+	let props: Props = $props();
+	console.log(props);
+</script>
diff --git a/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/invalid/builtin-types-unused-errors.yaml b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/invalid/builtin-types-unused-errors.yaml
new file mode 100644
index 0000000..75deab8
--- /dev/null
+++ b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/invalid/builtin-types-unused-errors.yaml
@@ -0,0 +1,16 @@
+- message: "'regexp' is an unused Props property."
+  line: 10
+  column: 8
+  suggestions: null
+- message: "'promise' is an unused Props property."
+  line: 10
+  column: 8
+  suggestions: null
+- message: "'map' is an unused Props property."
+  line: 10
+  column: 8
+  suggestions: null
+- message: "'set' is an unused Props property."
+  line: 10
+  column: 8
+  suggestions: null
diff --git a/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/invalid/builtin-types-unused-input.svelte b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/invalid/builtin-types-unused-input.svelte
new file mode 100644
index 0000000..4d8f541
--- /dev/null
+++ b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/invalid/builtin-types-unused-input.svelte
@@ -0,0 +1,12 @@
+<script lang="ts">
+	type Props = {
+		date: Date;
+		regexp: RegExp;
+		promise: Promise<string>;
+		map: Map<string, number>;
+		set: Set<string>;
+	};
+
+	const props: Props = $props();
+	console.log(props.date.getTime());
+</script>
diff --git a/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/invalid/class-props-unused-errors.yaml b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/invalid/class-props-unused-errors.yaml
new file mode 100644
index 0000000..218b103
--- /dev/null
+++ b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/invalid/class-props-unused-errors.yaml
@@ -0,0 +1,4 @@
+- message: "'data' is an unused Props property."
+  line: 16
+  column: 8
+  suggestions: null
diff --git a/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/invalid/class-props-unused-input.svelte b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/invalid/class-props-unused-input.svelte
new file mode 100644
index 0000000..3f06f0a
--- /dev/null
+++ b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/invalid/class-props-unused-input.svelte
@@ -0,0 +1,18 @@
+<script lang="ts">
+	class User {
+		constructor(
+			public name: string,
+			public age: number
+		) {}
+	}
+
+	type Props = {
+		user: User;
+		data: {
+			value: string;
+		};
+	};
+
+	const props: Props = $props();
+	console.log(props.user.name);
+</script>
diff --git a/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/invalid/extends-unused-errors.yaml b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/invalid/extends-unused-errors.yaml
new file mode 100644
index 0000000..0d968bd
--- /dev/null
+++ b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/invalid/extends-unused-errors.yaml
@@ -0,0 +1,8 @@
+- message: "'role' is an unused Props property."
+  line: 13
+  column: 6
+  suggestions: null
+- message: "'email' is an unused Props property."
+  line: 13
+  column: 6
+  suggestions: null
diff --git a/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/invalid/extends-unused-input.svelte b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/invalid/extends-unused-input.svelte
new file mode 100644
index 0000000..3e01204
--- /dev/null
+++ b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/invalid/extends-unused-input.svelte
@@ -0,0 +1,15 @@
+<script lang="ts">
+	interface BaseProps {
+		id: string;
+		type: 'user' | 'admin';
+		role: string;
+	}
+
+	interface Props extends BaseProps {
+		name: string;
+		email: string;
+	}
+
+	let props: Props = $props();
+	console.log(props.id, props.type, props.name);
+</script>
diff --git a/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/invalid/generic-props-unused-errors.yaml b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/invalid/generic-props-unused-errors.yaml
new file mode 100644
index 0000000..7a1f0a0
--- /dev/null
+++ b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/invalid/generic-props-unused-errors.yaml
@@ -0,0 +1,4 @@
+- message: "'extra' is an unused Props property."
+  line: 6
+  column: 6
+  suggestions: null
diff --git a/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/invalid/generic-props-unused-input.svelte b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/invalid/generic-props-unused-input.svelte
new file mode 100644
index 0000000..f65f0de
--- /dev/null
+++ b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/invalid/generic-props-unused-input.svelte
@@ -0,0 +1,8 @@
+<script lang="ts">
+	import type { GenericProps } from './shared-types';
+	interface Props extends GenericProps<string> {
+		extra: boolean;
+	}
+	let { data, loading }: Props = $props();
+	console.log(data, loading);
+</script>
diff --git a/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/invalid/ignored-pattern-partial-config.json b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/invalid/ignored-pattern-partial-config.json
new file mode 100644
index 0000000..c049eb8
--- /dev/null
+++ b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/invalid/ignored-pattern-partial-config.json
@@ -0,0 +1,7 @@
+{
+	"options": [
+		{
+			"ignorePatterns": [".*DTO$"]
+		}
+	]
+}
diff --git a/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/invalid/ignored-pattern-partial-errors.yaml b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/invalid/ignored-pattern-partial-errors.yaml
new file mode 100644
index 0000000..a3a80e9
--- /dev/null
+++ b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/invalid/ignored-pattern-partial-errors.yaml
@@ -0,0 +1,8 @@
+- message: "'metadata' is an unused Props property."
+  line: 12
+  column: 8
+  suggestions: null
+- message: "'description' is an unused Props property."
+  line: 12
+  column: 8
+  suggestions: null
diff --git a/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/invalid/ignored-pattern-partial-input.svelte b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/invalid/ignored-pattern-partial-input.svelte
new file mode 100644
index 0000000..3711e44
--- /dev/null
+++ b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/invalid/ignored-pattern-partial-input.svelte
@@ -0,0 +1,14 @@
+<script lang="ts">
+	type BaseDTO = {
+		id: string;
+		metadata: Record<string, unknown>;
+	};
+
+	type Props = BaseDTO & {
+		name: string;
+		description: string;
+	};
+
+	const props: Props = $props();
+	console.log(props.id, props.name);
+</script>
diff --git a/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/invalid/imported-type-check-config.json b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/invalid/imported-type-check-config.json
new file mode 100644
index 0000000..2b42ee3
--- /dev/null
+++ b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/invalid/imported-type-check-config.json
@@ -0,0 +1,7 @@
+{
+	"options": [
+		{
+			"checkImportedTypes": true
+		}
+	]
+}
diff --git a/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/invalid/imported-type-check-errors.yaml b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/invalid/imported-type-check-errors.yaml
new file mode 100644
index 0000000..2f34147
--- /dev/null
+++ b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/invalid/imported-type-check-errors.yaml
@@ -0,0 +1,6 @@
+- message: "'name' is an unused Props property."
+  line: 6
+  column: 6
+  endLine: 6
+  endColumn: 20
+  suggestions: null
diff --git a/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/invalid/imported-type-check-input.svelte b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/invalid/imported-type-check-input.svelte
new file mode 100644
index 0000000..69c9a24
--- /dev/null
+++ b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/invalid/imported-type-check-input.svelte
@@ -0,0 +1,8 @@
+<script lang="ts">
+	import type { BaseProps } from './shared-types';
+	interface Props extends BaseProps {
+		age: number;
+	}
+	let { age }: Props = $props();
+	console.log(age);
+</script>
diff --git a/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/invalid/imported-type-unused-errors.yaml b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/invalid/imported-type-unused-errors.yaml
new file mode 100644
index 0000000..d4dfbf8
--- /dev/null
+++ b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/invalid/imported-type-unused-errors.yaml
@@ -0,0 +1,4 @@
+- message: "'role' is an unused Props property."
+  line: 6
+  column: 6
+  suggestions: null
diff --git a/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/invalid/imported-type-unused-input.svelte b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/invalid/imported-type-unused-input.svelte
new file mode 100644
index 0000000..90aa495
--- /dev/null
+++ b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/invalid/imported-type-unused-input.svelte
@@ -0,0 +1,8 @@
+<script lang="ts">
+	import type { ExtendedProps } from './shared-types';
+	interface Props extends ExtendedProps {
+		role: string;
+	}
+	let { name, age }: Props = $props();
+	console.log(name, age);
+</script>
diff --git a/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/invalid/index-signature-no-rest-errors.yaml b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/invalid/index-signature-no-rest-errors.yaml
new file mode 100644
index 0000000..03b89a8
--- /dev/null
+++ b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/invalid/index-signature-no-rest-errors.yaml
@@ -0,0 +1,9 @@
+- message: "'role' is an unused Props property."
+  line: 8
+  column: 6
+  suggestions: null
+- message: Index signature is unused. Consider using rest operator (...) to
+    capture remaining properties.
+  line: 8
+  column: 6
+  suggestions: null
diff --git a/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/invalid/index-signature-no-rest-input.svelte b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/invalid/index-signature-no-rest-input.svelte
new file mode 100644
index 0000000..77a1f17
--- /dev/null
+++ b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/invalid/index-signature-no-rest-input.svelte
@@ -0,0 +1,10 @@
+<script lang="ts">
+	interface Props {
+		name: string;
+		age: number;
+		role: string;
+		[key: string]: string | number;
+	}
+	let { name, age }: Props = $props();
+	console.log(name, age);
+</script>
diff --git a/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/invalid/intersection-unused-errors.yaml b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/invalid/intersection-unused-errors.yaml
new file mode 100644
index 0000000..128798a
--- /dev/null
+++ b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/invalid/intersection-unused-errors.yaml
@@ -0,0 +1,8 @@
+- message: "'version' is an unused Props property."
+  line: 17
+  column: 8
+  suggestions: null
+- message: "'updatedAt' is an unused Props property."
+  line: 17
+  column: 8
+  suggestions: null
diff --git a/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/invalid/intersection-unused-input.svelte b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/invalid/intersection-unused-input.svelte
new file mode 100644
index 0000000..67c81c9
--- /dev/null
+++ b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/invalid/intersection-unused-input.svelte
@@ -0,0 +1,19 @@
+<script lang="ts">
+	type WithId = {
+		id: string;
+		version: number;
+	};
+
+	type WithTimestamp = {
+		createdAt: Date;
+		updatedAt: Date;
+	};
+
+	type Props = WithId &
+		WithTimestamp & {
+			name: string;
+		};
+
+	const props: Props = $props();
+	console.log(props.id, props.name, props.createdAt);
+</script>
diff --git a/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/invalid/multiple-extends-unused-errors.yaml b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/invalid/multiple-extends-unused-errors.yaml
new file mode 100644
index 0000000..3fa480d
--- /dev/null
+++ b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/invalid/multiple-extends-unused-errors.yaml
@@ -0,0 +1,8 @@
+- message: "'email' is an unused Props property."
+  line: 17
+  column: 6
+  suggestions: null
+- message: "'permissions' is an unused Props property."
+  line: 17
+  column: 6
+  suggestions: null
diff --git a/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/invalid/multiple-extends-unused-input.svelte b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/invalid/multiple-extends-unused-input.svelte
new file mode 100644
index 0000000..49607ad
--- /dev/null
+++ b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/invalid/multiple-extends-unused-input.svelte
@@ -0,0 +1,19 @@
+<script lang="ts">
+	interface BaseProps {
+		id: string;
+		type: 'user' | 'admin';
+	}
+
+	interface UserProps extends BaseProps {
+		name: string;
+		email: string;
+	}
+
+	interface Props extends UserProps {
+		role: string;
+		permissions: string[];
+	}
+
+	let props: Props = $props();
+	console.log(props.id, props.type, props.name, props.role);
+</script>
diff --git a/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/invalid/nested-unused-errors.yaml b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/invalid/nested-unused-errors.yaml
new file mode 100644
index 0000000..4c887b8
--- /dev/null
+++ b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/invalid/nested-unused-errors.yaml
@@ -0,0 +1,4 @@
+- message: "'location' in 'user' is an unused property."
+  line: 8
+  column: 6
+  suggestions: null
diff --git a/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/invalid/nested-unused-input.svelte b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/invalid/nested-unused-input.svelte
new file mode 100644
index 0000000..8293761
--- /dev/null
+++ b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/invalid/nested-unused-input.svelte
@@ -0,0 +1,10 @@
+<script lang="ts">
+	interface Props {
+		user: {
+			name: string;
+			location: string;
+		};
+	}
+	let props: Props = $props();
+	console.log(props.user.name);
+</script>
diff --git a/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/invalid/optional-unused-errors.yaml b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/invalid/optional-unused-errors.yaml
new file mode 100644
index 0000000..094710d
--- /dev/null
+++ b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/invalid/optional-unused-errors.yaml
@@ -0,0 +1,8 @@
+- message: "'optionalCallback' is an unused Props property."
+  line: 9
+  column: 8
+  suggestions: null
+- message: "'optionalData' is an unused Props property."
+  line: 9
+  column: 8
+  suggestions: null
diff --git a/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/invalid/optional-unused-input.svelte b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/invalid/optional-unused-input.svelte
new file mode 100644
index 0000000..26af201
--- /dev/null
+++ b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/invalid/optional-unused-input.svelte
@@ -0,0 +1,14 @@
+<script lang="ts">
+	interface Props {
+		required: string;
+		optional?: number;
+		optionalCallback?: () => void;
+		optionalData?: { value: string };
+	}
+
+	const props: Props = $props();
+	console.log(props.required);
+	if (props.optional !== undefined) {
+		console.log(props.optional);
+	}
+</script>
diff --git a/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/invalid/parent-interface-unused-errors.yaml b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/invalid/parent-interface-unused-errors.yaml
new file mode 100644
index 0000000..691538c
--- /dev/null
+++ b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/invalid/parent-interface-unused-errors.yaml
@@ -0,0 +1,4 @@
+- message: "'id' is an unused Props property."
+  line: 11
+  column: 6
+  suggestions: null
diff --git a/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/invalid/parent-interface-unused-input.svelte b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/invalid/parent-interface-unused-input.svelte
new file mode 100644
index 0000000..be10ed2
--- /dev/null
+++ b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/invalid/parent-interface-unused-input.svelte
@@ -0,0 +1,13 @@
+<script lang="ts">
+	interface BaseProps {
+		id: string;
+		type: 'user' | 'admin';
+	}
+
+	interface Props extends BaseProps {
+		name: string;
+	}
+
+	let { name, type }: Props = $props();
+	console.log(name, type);
+</script>
diff --git a/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/invalid/shared-types.ts b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/invalid/shared-types.ts
new file mode 100644
index 0000000..a60e61e
--- /dev/null
+++ b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/invalid/shared-types.ts
@@ -0,0 +1,3 @@
+export interface BaseProps {
+	name: string;
+}
diff --git a/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/invalid/simple-unused-errors.yaml b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/invalid/simple-unused-errors.yaml
new file mode 100644
index 0000000..e555f29
--- /dev/null
+++ b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/invalid/simple-unused-errors.yaml
@@ -0,0 +1,4 @@
+- message: "'age' is an unused Props property."
+  line: 6
+  column: 6
+  suggestions: null
diff --git a/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/invalid/simple-unused-input.svelte b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/invalid/simple-unused-input.svelte
new file mode 100644
index 0000000..17d2e8a
--- /dev/null
+++ b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/invalid/simple-unused-input.svelte
@@ -0,0 +1,8 @@
+<script lang="ts">
+	interface Props {
+		name: string;
+		age: number;
+	}
+	let { name }: Props = $props();
+	console.log(name);
+</script>
diff --git a/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/invalid/unused-index-signature-errors.yaml b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/invalid/unused-index-signature-errors.yaml
new file mode 100644
index 0000000..b8ee310
--- /dev/null
+++ b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/invalid/unused-index-signature-errors.yaml
@@ -0,0 +1,5 @@
+- message: Index signature is unused. Consider using rest operator (...) to
+    capture remaining properties.
+  line: 7
+  column: 6
+  suggestions: null
diff --git a/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/invalid/unused-index-signature-input.svelte b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/invalid/unused-index-signature-input.svelte
new file mode 100644
index 0000000..8861e91
--- /dev/null
+++ b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/invalid/unused-index-signature-input.svelte
@@ -0,0 +1,8 @@
+<script lang="ts">
+	interface Props {
+		a: number;
+		[key: string]: unknown;
+	}
+
+	let { a }: Props = $props();
+</script>
diff --git a/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/valid/_requirements.json b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/valid/_requirements.json
new file mode 100644
index 0000000..0192b10
--- /dev/null
+++ b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/valid/_requirements.json
@@ -0,0 +1,3 @@
+{
+	"svelte": ">=5.0.0-0"
+}
diff --git a/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/valid/any-input.svelte b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/valid/any-input.svelte
new file mode 100644
index 0000000..cc3a654
--- /dev/null
+++ b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/valid/any-input.svelte
@@ -0,0 +1,5 @@
+<script lang="ts">
+	// There is no type annotation, so it is treated as any.
+	let props = $props();
+	console.log(props.anything);
+</script>
diff --git a/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/valid/assignment-input.svelte b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/valid/assignment-input.svelte
new file mode 100644
index 0000000..2e3aac0
--- /dev/null
+++ b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/valid/assignment-input.svelte
@@ -0,0 +1,4 @@
+<script lang="ts">
+	const props: { value: number } = $props();
+	let localValue = props.value;
+</script>
diff --git a/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/valid/basic-input.svelte b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/valid/basic-input.svelte
new file mode 100644
index 0000000..1d7029b
--- /dev/null
+++ b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/valid/basic-input.svelte
@@ -0,0 +1,8 @@
+<script lang="ts">
+	interface Props {
+		a: string;
+		b: number;
+	}
+	let { a, b }: Props = $props();
+	console.log(a, b);
+</script>
diff --git a/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/valid/builtin-types-input.svelte b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/valid/builtin-types-input.svelte
new file mode 100644
index 0000000..947bf9e
--- /dev/null
+++ b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/valid/builtin-types-input.svelte
@@ -0,0 +1,18 @@
+<script lang="ts">
+	type Props = {
+		date: Date;
+		regexp: RegExp;
+		promise: Promise<string>;
+		map: Map<string, number>;
+		set: Set<string>;
+	};
+
+	const props: Props = $props();
+	console.log(
+		props.date.getTime(),
+		props.regexp.test('test'),
+		props.promise.then(console.log),
+		props.map.get('key'),
+		props.set.has('value')
+	);
+</script>
diff --git a/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/valid/computed-member-input.svelte b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/valid/computed-member-input.svelte
new file mode 100644
index 0000000..72b5238
--- /dev/null
+++ b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/valid/computed-member-input.svelte
@@ -0,0 +1,4 @@
+<script lang="ts">
+	const props: { 'data-value': string } = $props();
+	const value = props['data-value'];
+</script>
diff --git a/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/valid/computed-property-input.svelte b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/valid/computed-property-input.svelte
new file mode 100644
index 0000000..1e7ad02
--- /dev/null
+++ b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/valid/computed-property-input.svelte
@@ -0,0 +1,7 @@
+<script lang="ts">
+	interface Props {
+		dynamic: string;
+	}
+	const props: Props = $props();
+	console.log(props['dynamic']);
+</script>
diff --git a/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/valid/conditional-type-input.svelte b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/valid/conditional-type-input.svelte
new file mode 100644
index 0000000..d24aa04
--- /dev/null
+++ b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/valid/conditional-type-input.svelte
@@ -0,0 +1,16 @@
+<script lang="ts">
+	type IsString<T> = T extends string ? true : false;
+	type StringOrNumber<T> = T extends string ? string : number;
+
+	interface Props<T> {
+		value: T;
+		isString: IsString<T>;
+		converted: StringOrNumber<T>;
+	}
+
+	const props: Props<string> = $props();
+	console.log(props.value.length);
+	if (props.isString) {
+		console.log(props.converted.toUpperCase());
+	}
+</script>
diff --git a/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/valid/extends-input.svelte b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/valid/extends-input.svelte
new file mode 100644
index 0000000..67513d7
--- /dev/null
+++ b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/valid/extends-input.svelte
@@ -0,0 +1,14 @@
+<script lang="ts">
+	interface BaseProps {
+		id: string;
+		type: 'user' | 'admin';
+	}
+
+	interface Props extends BaseProps {
+		name: string;
+		permissions: string[];
+	}
+
+	let props: Props = $props();
+	console.log(props.id, props.type, props.name, props.permissions);
+</script>
diff --git a/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/valid/function-props-input.svelte b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/valid/function-props-input.svelte
new file mode 100644
index 0000000..6a877eb
--- /dev/null
+++ b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/valid/function-props-input.svelte
@@ -0,0 +1,12 @@
+<script lang="ts">
+	interface Props {
+		onClick: (event: MouseEvent) => void;
+		transform: <T>(value: T) => string;
+		async: () => Promise<void>;
+	}
+
+	const props: Props = $props();
+	props.onClick(new MouseEvent('click'));
+	const result = props.transform(42);
+	await props.async();
+</script>
diff --git a/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/valid/ignored-conditional-type-config.json b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/valid/ignored-conditional-type-config.json
new file mode 100644
index 0000000..73df77c
--- /dev/null
+++ b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/valid/ignored-conditional-type-config.json
@@ -0,0 +1,7 @@
+{
+	"options": [
+		{
+			"ignorePatterns": ["^Conditional"]
+		}
+	]
+}
diff --git a/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/valid/ignored-conditional-type-input.svelte b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/valid/ignored-conditional-type-input.svelte
new file mode 100644
index 0000000..e70238f
--- /dev/null
+++ b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/valid/ignored-conditional-type-input.svelte
@@ -0,0 +1,11 @@
+<script lang="ts">
+	interface ConditionalProps {
+		name: string;
+	}
+	interface Props {
+		age: number;
+	}
+	type AllProps = ConditionalProps extends { name: string } ? Props : never;
+	let { age }: AllProps = $props();
+	console.log(age);
+</script>
diff --git a/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/valid/imported-type-config.json b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/valid/imported-type-config.json
new file mode 100644
index 0000000..2b42ee3
--- /dev/null
+++ b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/valid/imported-type-config.json
@@ -0,0 +1,7 @@
+{
+	"options": [
+		{
+			"checkImportedTypes": true
+		}
+	]
+}
diff --git a/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/valid/imported-type-default-input.svelte b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/valid/imported-type-default-input.svelte
new file mode 100644
index 0000000..f652747
--- /dev/null
+++ b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/valid/imported-type-default-input.svelte
@@ -0,0 +1,8 @@
+<script lang="ts">
+	import type { BaseProps } from './shared-types';
+	interface Props extends BaseProps {
+		age: number;
+	}
+	let { name, age }: Props = $props();
+	console.log(name, age);
+</script>
diff --git a/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/valid/imported-type-explicit-config.json b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/valid/imported-type-explicit-config.json
new file mode 100644
index 0000000..b67e452
--- /dev/null
+++ b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/valid/imported-type-explicit-config.json
@@ -0,0 +1,7 @@
+{
+	"options": [
+		{
+			"checkImportedTypes": false
+		}
+	]
+}
diff --git a/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/valid/imported-type-explicit-input.svelte b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/valid/imported-type-explicit-input.svelte
new file mode 100644
index 0000000..f652747
--- /dev/null
+++ b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/valid/imported-type-explicit-input.svelte
@@ -0,0 +1,8 @@
+<script lang="ts">
+	import type { BaseProps } from './shared-types';
+	interface Props extends BaseProps {
+		age: number;
+	}
+	let { name, age }: Props = $props();
+	console.log(name, age);
+</script>
diff --git a/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/valid/imported-type-input.svelte b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/valid/imported-type-input.svelte
new file mode 100644
index 0000000..f652747
--- /dev/null
+++ b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/valid/imported-type-input.svelte
@@ -0,0 +1,8 @@
+<script lang="ts">
+	import type { BaseProps } from './shared-types';
+	interface Props extends BaseProps {
+		age: number;
+	}
+	let { name, age }: Props = $props();
+	console.log(name, age);
+</script>
diff --git a/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/valid/index-signature-rest-input.svelte b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/valid/index-signature-rest-input.svelte
new file mode 100644
index 0000000..5795254
--- /dev/null
+++ b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/valid/index-signature-rest-input.svelte
@@ -0,0 +1,9 @@
+<script lang="ts">
+	interface Props {
+		name: string;
+		age: number;
+		[key: string]: string | number | boolean;
+	}
+	let { name, age, ...rest }: Props = $props();
+	console.log(name, age, rest.isAdmin, rest.role);
+</script>
diff --git a/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/valid/intersection-type-input.svelte b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/valid/intersection-type-input.svelte
new file mode 100644
index 0000000..2a3efb2
--- /dev/null
+++ b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/valid/intersection-type-input.svelte
@@ -0,0 +1,18 @@
+<script lang="ts">
+	type WithId = {
+		id: string;
+	};
+
+	type WithTimestamp = {
+		createdAt: Date;
+		updatedAt: Date;
+	};
+
+	type Props = WithId &
+		WithTimestamp & {
+			name: string;
+		};
+
+	const props: Props = $props();
+	console.log(props.id, props.name, props.createdAt.getTime(), props.updatedAt.getTime());
+</script>
diff --git a/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/valid/js-basic-input.svelte b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/valid/js-basic-input.svelte
new file mode 100644
index 0000000..215fd2a
--- /dev/null
+++ b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/valid/js-basic-input.svelte
@@ -0,0 +1,4 @@
+<script>
+	const { a } = $props();
+	console.log(a);
+</script>
diff --git a/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/valid/js-jsdoc-input.svelte b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/valid/js-jsdoc-input.svelte
new file mode 100644
index 0000000..63d35ea
--- /dev/null
+++ b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/valid/js-jsdoc-input.svelte
@@ -0,0 +1,10 @@
+<script>
+	/**
+	 * @typedef {Object} Props
+	 * @property {string} name - User name
+	 * @property {number} age - User age
+	 */
+	const props = $props();
+	// JSDoc is not checked, so no warning
+	console.log(props.name);
+</script>
diff --git a/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/valid/js-no-types-input.svelte b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/valid/js-no-types-input.svelte
new file mode 100644
index 0000000..204dc77
--- /dev/null
+++ b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/valid/js-no-types-input.svelte
@@ -0,0 +1,4 @@
+<script>
+	const props = $props();
+	console.log(props.anything);
+</script>
diff --git a/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/valid/member-input.svelte b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/valid/member-input.svelte
new file mode 100644
index 0000000..af761c5
--- /dev/null
+++ b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/valid/member-input.svelte
@@ -0,0 +1,8 @@
+<script lang="ts">
+	interface Props {
+		a: string;
+		b: number;
+	}
+	let props: Props = $props();
+	console.log(props.a, props.b);
+</script>
diff --git a/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/valid/module-script-input.svelte b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/valid/module-script-input.svelte
new file mode 100644
index 0000000..4bf0d56
--- /dev/null
+++ b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/valid/module-script-input.svelte
@@ -0,0 +1,11 @@
+<script module lang="ts">
+	interface Props {
+		name: string;
+		age: number;
+	}
+</script>
+
+<script lang="ts">
+	let { name, age }: Props = $props();
+	console.log(name, age);
+</script>
diff --git a/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/valid/multiple-index-signatures-input.svelte b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/valid/multiple-index-signatures-input.svelte
new file mode 100644
index 0000000..6c47f2f
--- /dev/null
+++ b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/valid/multiple-index-signatures-input.svelte
@@ -0,0 +1,10 @@
+<script lang="ts">
+	interface Props {
+		name: string;
+		age: number;
+		[key: string]: string | number | boolean;
+		[index: number]: string | number;
+	}
+	let { name, ...rest }: Props = $props();
+	console.log(name);
+</script>
diff --git a/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/valid/nested-props-input.svelte b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/valid/nested-props-input.svelte
new file mode 100644
index 0000000..3e7d799
--- /dev/null
+++ b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/valid/nested-props-input.svelte
@@ -0,0 +1,13 @@
+<script lang="ts">
+	interface Props {
+		user: {
+			name: string;
+			profile: {
+				age: number;
+				bio: string;
+			};
+		};
+	}
+	let props: Props = $props();
+	console.log(props.user.name, props.user.profile.age, props.user.profile.bio);
+</script>
diff --git a/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/valid/new-expression-input.svelte b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/valid/new-expression-input.svelte
new file mode 100644
index 0000000..be34418
--- /dev/null
+++ b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/valid/new-expression-input.svelte
@@ -0,0 +1,4 @@
+<script lang="ts">
+	const props: { config: { new (): any } } = $props();
+	new props.config();
+</script>
diff --git a/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/valid/optional-props-input.svelte b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/valid/optional-props-input.svelte
new file mode 100644
index 0000000..5945601
--- /dev/null
+++ b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/valid/optional-props-input.svelte
@@ -0,0 +1,14 @@
+<script lang="ts">
+	interface Props {
+		required: string;
+		optional?: number;
+		optionalCallback?: () => void;
+	}
+
+	const props: Props = $props();
+	console.log(props.required);
+	if (props.optional !== undefined) {
+		console.log(props.optional);
+	}
+	props.optionalCallback?.();
+</script>
diff --git a/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/valid/record-type-input.svelte b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/valid/record-type-input.svelte
new file mode 100644
index 0000000..13a5e08
--- /dev/null
+++ b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/valid/record-type-input.svelte
@@ -0,0 +1,10 @@
+<script lang="ts">
+	type Props = {
+		metadata: Record<string, unknown>;
+		settings: Record<string, boolean>;
+	};
+
+	const props: Props = $props();
+	const value = props.metadata['key'];
+	const enabled = props.settings['feature'];
+</script>
diff --git a/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/valid/recursive-type-input.svelte b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/valid/recursive-type-input.svelte
new file mode 100644
index 0000000..f4b66e1
--- /dev/null
+++ b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/valid/recursive-type-input.svelte
@@ -0,0 +1,9 @@
+<script lang="ts">
+	type RecursiveType = {
+		value: string;
+		children?: RecursiveType[];
+	};
+
+	const props: RecursiveType = $props();
+	console.log(props.value, props.children?.[0]?.value);
+</script>
diff --git a/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/valid/rename-unused-input.svelte b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/valid/rename-unused-input.svelte
new file mode 100644
index 0000000..d8cff60
--- /dev/null
+++ b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/valid/rename-unused-input.svelte
@@ -0,0 +1,10 @@
+<script lang="ts">
+	interface Props {
+		name: string;
+		age: number;
+		role: string;
+	}
+	// userRole is unused but it should be reported by @typescript-eslint/no-unused-vars rule.
+	let { name: userName, age: userAge, role: userRole }: Props = $props();
+	console.log(userName, userAge);
+</script>
diff --git a/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/valid/rest-and-index-input.svelte b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/valid/rest-and-index-input.svelte
new file mode 100644
index 0000000..45be434
--- /dev/null
+++ b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/valid/rest-and-index-input.svelte
@@ -0,0 +1,9 @@
+<script lang="ts">
+	interface Props {
+		a: string;
+		b: number;
+		[key: string]: unknown;
+	}
+	let { a, ...otherProps }: Props = $props();
+	console.log(a, otherProps);
+</script>
diff --git a/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/valid/rest-with-index-input.svelte b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/valid/rest-with-index-input.svelte
new file mode 100644
index 0000000..7583fb9
--- /dev/null
+++ b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/valid/rest-with-index-input.svelte
@@ -0,0 +1,8 @@
+<script lang="ts">
+	interface Props {
+		name: string;
+		[key: string]: unknown;
+	}
+	let { name, ...rest }: Props = $props();
+	console.log(name, rest);
+</script>
diff --git a/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/valid/shared-types.ts b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/valid/shared-types.ts
new file mode 100644
index 0000000..a60e61e
--- /dev/null
+++ b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/valid/shared-types.ts
@@ -0,0 +1,3 @@
+export interface BaseProps {
+	name: string;
+}
diff --git a/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/valid/template-usage-input.svelte b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/valid/template-usage-input.svelte
new file mode 100644
index 0000000..08e3f5a
--- /dev/null
+++ b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/valid/template-usage-input.svelte
@@ -0,0 +1,16 @@
+<script lang="ts">
+	interface Props {
+		title: string;
+		description: string;
+		items: string[];
+	}
+	let props: Props = $props();
+</script>
+
+<h1>{props.title}</h1>
+<p>{props.description}</p>
+<ul>
+	{#each props.items as item}
+		<li>{item}</li>
+	{/each}
+</ul>
diff --git a/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/valid/typed-props-input.svelte b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/valid/typed-props-input.svelte
new file mode 100644
index 0000000..1957cdc
--- /dev/null
+++ b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/valid/typed-props-input.svelte
@@ -0,0 +1,8 @@
+<script lang="ts">
+	interface Props {
+		name: string;
+		age: number;
+	}
+	let props: Props = $props();
+	console.log(props.name, props.age);
+</script>
diff --git a/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/valid/union-type-input.svelte b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/valid/union-type-input.svelte
new file mode 100644
index 0000000..ebf4f4b
--- /dev/null
+++ b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/valid/union-type-input.svelte
@@ -0,0 +1,11 @@
+<script lang="ts">
+	type Props = {
+		status: 'success' | 'error' | 'loading';
+		data: string | number;
+	};
+
+	const props: Props = $props();
+	if (props.status === 'success') {
+		console.log(props.data);
+	}
+</script>
diff --git a/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/valid/used-index-signature-input.svelte b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/valid/used-index-signature-input.svelte
new file mode 100644
index 0000000..b69a0cc
--- /dev/null
+++ b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/valid/used-index-signature-input.svelte
@@ -0,0 +1,9 @@
+<script lang="ts">
+	interface Props {
+		a: number;
+		[key: string]: unknown;
+	}
+
+	let { a, ...rest }: Props = $props();
+	console.log(rest);
+</script>
diff --git a/packages/eslint-plugin-svelte/tests/fixtures/rules/no-useless-mustaches/valid/valid-test01-input.svelte b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-useless-mustaches/valid/valid-test01-input.svelte
index 5da305a..46402ae 100644
--- a/packages/eslint-plugin-svelte/tests/fixtures/rules/no-useless-mustaches/valid/valid-test01-input.svelte
+++ b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-useless-mustaches/valid/valid-test01-input.svelte
@@ -8,5 +8,6 @@ foo 'foo'
 {1}
 {null}
 {`foo${foo}`}
+{'{foo'}
 
 <div data-text="foo 'foo' {foo} {'foo' || 'bar'} {1} {null}" />
diff --git a/packages/eslint-plugin-svelte/tests/fixtures/rules/prefer-const/invalid/option1/_config.json b/packages/eslint-plugin-svelte/tests/fixtures/rules/prefer-const/invalid/option1/_config.json
new file mode 100644
index 0000000..0751a96
--- /dev/null
+++ b/packages/eslint-plugin-svelte/tests/fixtures/rules/prefer-const/invalid/option1/_config.json
@@ -0,0 +1,3 @@
+{
+	"options": [{ "excludedRunes": [] }]
+}
diff --git a/packages/eslint-plugin-svelte/tests/fixtures/rules/prefer-const/invalid/option1/test01-errors.yaml b/packages/eslint-plugin-svelte/tests/fixtures/rules/prefer-const/invalid/option1/test01-errors.yaml
new file mode 100644
index 0000000..03d2d8e
--- /dev/null
+++ b/packages/eslint-plugin-svelte/tests/fixtures/rules/prefer-const/invalid/option1/test01-errors.yaml
@@ -0,0 +1,20 @@
+- message: "'prop1' is never reassigned. Use 'const' instead."
+  line: 2
+  column: 8
+  suggestions: null
+- message: "'prop2' is never reassigned. Use 'const' instead."
+  line: 2
+  column: 15
+  suggestions: null
+- message: "'zero' is never reassigned. Use 'const' instead."
+  line: 3
+  column: 6
+  suggestions: null
+- message: "'derived' is never reassigned. Use 'const' instead."
+  line: 4
+  column: 6
+  suggestions: null
+- message: "'derivedBy' is never reassigned. Use 'const' instead."
+  line: 5
+  column: 6
+  suggestions: null
diff --git a/packages/eslint-plugin-svelte/tests/fixtures/rules/prefer-const/invalid/option1/test01-input.svelte b/packages/eslint-plugin-svelte/tests/fixtures/rules/prefer-const/invalid/option1/test01-input.svelte
new file mode 100644
index 0000000..97f28d4
--- /dev/null
+++ b/packages/eslint-plugin-svelte/tests/fixtures/rules/prefer-const/invalid/option1/test01-input.svelte
@@ -0,0 +1,6 @@
+<script>
+	let { prop1, prop2 } = $props();
+	let zero = $state(0);
+	let derived = $derived(zero * 2);
+	let derivedBy = $derived.by(calc());
+</script>
diff --git a/packages/eslint-plugin-svelte/tests/fixtures/rules/prefer-const/invalid/option1/test01-output.svelte b/packages/eslint-plugin-svelte/tests/fixtures/rules/prefer-const/invalid/option1/test01-output.svelte
new file mode 100644
index 0000000..dd1b99c
--- /dev/null
+++ b/packages/eslint-plugin-svelte/tests/fixtures/rules/prefer-const/invalid/option1/test01-output.svelte
@@ -0,0 +1,6 @@
+<script>
+	const { prop1, prop2 } = $props();
+	const zero = $state(0);
+	const derived = $derived(zero * 2);
+	const derivedBy = $derived.by(calc());
+</script>
diff --git a/packages/eslint-plugin-svelte/tests/fixtures/rules/prefer-const/invalid/option2/_config.json b/packages/eslint-plugin-svelte/tests/fixtures/rules/prefer-const/invalid/option2/_config.json
new file mode 100644
index 0000000..9021ae6
--- /dev/null
+++ b/packages/eslint-plugin-svelte/tests/fixtures/rules/prefer-const/invalid/option2/_config.json
@@ -0,0 +1,3 @@
+{
+	"options": [{ "excludedRunes": ["$state"] }]
+}
diff --git a/packages/eslint-plugin-svelte/tests/fixtures/rules/prefer-const/invalid/option2/test01-errors.yaml b/packages/eslint-plugin-svelte/tests/fixtures/rules/prefer-const/invalid/option2/test01-errors.yaml
new file mode 100644
index 0000000..b784981
--- /dev/null
+++ b/packages/eslint-plugin-svelte/tests/fixtures/rules/prefer-const/invalid/option2/test01-errors.yaml
@@ -0,0 +1,16 @@
+- message: "'prop1' is never reassigned. Use 'const' instead."
+  line: 2
+  column: 8
+  suggestions: null
+- message: "'prop2' is never reassigned. Use 'const' instead."
+  line: 2
+  column: 15
+  suggestions: null
+- message: "'derived' is never reassigned. Use 'const' instead."
+  line: 4
+  column: 6
+  suggestions: null
+- message: "'derivedBy' is never reassigned. Use 'const' instead."
+  line: 5
+  column: 6
+  suggestions: null
diff --git a/packages/eslint-plugin-svelte/tests/fixtures/rules/prefer-const/invalid/option2/test01-input.svelte b/packages/eslint-plugin-svelte/tests/fixtures/rules/prefer-const/invalid/option2/test01-input.svelte
new file mode 100644
index 0000000..97f28d4
--- /dev/null
+++ b/packages/eslint-plugin-svelte/tests/fixtures/rules/prefer-const/invalid/option2/test01-input.svelte
@@ -0,0 +1,6 @@
+<script>
+	let { prop1, prop2 } = $props();
+	let zero = $state(0);
+	let derived = $derived(zero * 2);
+	let derivedBy = $derived.by(calc());
+</script>
diff --git a/packages/eslint-plugin-svelte/tests/fixtures/rules/prefer-const/invalid/option2/test01-output.svelte b/packages/eslint-plugin-svelte/tests/fixtures/rules/prefer-const/invalid/option2/test01-output.svelte
new file mode 100644
index 0000000..2e25c28
--- /dev/null
+++ b/packages/eslint-plugin-svelte/tests/fixtures/rules/prefer-const/invalid/option2/test01-output.svelte
@@ -0,0 +1,6 @@
+<script>
+	const { prop1, prop2 } = $props();
+	let zero = $state(0);
+	const derived = $derived(zero * 2);
+	const derivedBy = $derived.by(calc());
+</script>
diff --git a/packages/eslint-plugin-svelte/tests/fixtures/rules/prefer-const/valid/option1/_config.json b/packages/eslint-plugin-svelte/tests/fixtures/rules/prefer-const/valid/option1/_config.json
new file mode 100644
index 0000000..0751a96
--- /dev/null
+++ b/packages/eslint-plugin-svelte/tests/fixtures/rules/prefer-const/valid/option1/_config.json
@@ -0,0 +1,3 @@
+{
+	"options": [{ "excludedRunes": [] }]
+}
diff --git a/packages/eslint-plugin-svelte/tests/fixtures/rules/prefer-const/valid/option1/test01-input.svelte b/packages/eslint-plugin-svelte/tests/fixtures/rules/prefer-const/valid/option1/test01-input.svelte
new file mode 100644
index 0000000..dd1b99c
--- /dev/null
+++ b/packages/eslint-plugin-svelte/tests/fixtures/rules/prefer-const/valid/option1/test01-input.svelte
@@ -0,0 +1,6 @@
+<script>
+	const { prop1, prop2 } = $props();
+	const zero = $state(0);
+	const derived = $derived(zero * 2);
+	const derivedBy = $derived.by(calc());
+</script>
diff --git a/packages/eslint-plugin-svelte/tests/fixtures/rules/prefer-const/valid/option2/_config.json b/packages/eslint-plugin-svelte/tests/fixtures/rules/prefer-const/valid/option2/_config.json
new file mode 100644
index 0000000..03a86e4
--- /dev/null
+++ b/packages/eslint-plugin-svelte/tests/fixtures/rules/prefer-const/valid/option2/_config.json
@@ -0,0 +1,3 @@
+{
+	"options": [{ "excludedRunes": ["$props", "$derived", "$state"] }]
+}
diff --git a/packages/eslint-plugin-svelte/tests/fixtures/rules/prefer-const/valid/option2/test01-input.svelte b/packages/eslint-plugin-svelte/tests/fixtures/rules/prefer-const/valid/option2/test01-input.svelte
new file mode 100644
index 0000000..97f28d4
--- /dev/null
+++ b/packages/eslint-plugin-svelte/tests/fixtures/rules/prefer-const/valid/option2/test01-input.svelte
@@ -0,0 +1,6 @@
+<script>
+	let { prop1, prop2 } = $props();
+	let zero = $state(0);
+	let derived = $derived(zero * 2);
+	let derivedBy = $derived.by(calc());
+</script>
diff --git a/packages/eslint-plugin-svelte/tests/fixtures/rules/prefer-const/valid/test01-input.svelte b/packages/eslint-plugin-svelte/tests/fixtures/rules/prefer-const/valid/test01-input.svelte
index 5c768eb..33ac253 100644
--- a/packages/eslint-plugin-svelte/tests/fixtures/rules/prefer-const/valid/test01-input.svelte
+++ b/packages/eslint-plugin-svelte/tests/fixtures/rules/prefer-const/valid/test01-input.svelte
@@ -1,3 +1,6 @@
 <script>
-	const a = {};
+	let { prop1, prop2 } = $props();
+	const zero = $state(0);
+	let derived = $derived(zero * 2);
+	let derivedBy = $derived.by(calc());
 </script>
diff --git a/packages/eslint-plugin-svelte/tests/fixtures/rules/valid-prop-names-in-kit-pages/invalid/errors.yaml b/packages/eslint-plugin-svelte/tests/fixtures/rules/valid-prop-names-in-kit-pages/invalid/errors.yaml
index 56a75c0..1a29759 100644
--- a/packages/eslint-plugin-svelte/tests/fixtures/rules/valid-prop-names-in-kit-pages/invalid/errors.yaml
+++ b/packages/eslint-plugin-svelte/tests/fixtures/rules/valid-prop-names-in-kit-pages/invalid/errors.yaml
@@ -14,11 +14,3 @@
   line: 4
   column: 20
   suggestions: null
-- message: disallow props other than data or errors in SvelteKit page components.
-  line: 5
-  column: 21
-  suggestions: null
-- message: disallow props other than data or errors in SvelteKit page components.
-  line: 5
-  column: 36
-  suggestions: null
diff --git a/packages/eslint-plugin-svelte/tests/fixtures/rules/valid-prop-names-in-kit-pages/invalid/svelte-config-from-parser-options/errors.yaml b/packages/eslint-plugin-svelte/tests/fixtures/rules/valid-prop-names-in-kit-pages/invalid/svelte-config-from-parser-options/errors.yaml
index 56a75c0..1a29759 100644
--- a/packages/eslint-plugin-svelte/tests/fixtures/rules/valid-prop-names-in-kit-pages/invalid/svelte-config-from-parser-options/errors.yaml
+++ b/packages/eslint-plugin-svelte/tests/fixtures/rules/valid-prop-names-in-kit-pages/invalid/svelte-config-from-parser-options/errors.yaml
@@ -14,11 +14,3 @@
   line: 4
   column: 20
   suggestions: null
-- message: disallow props other than data or errors in SvelteKit page components.
-  line: 5
-  column: 21
-  suggestions: null
-- message: disallow props other than data or errors in SvelteKit page components.
-  line: 5
-  column: 36
-  suggestions: null
diff --git a/packages/eslint-plugin-svelte/tests/fixtures/rules/valid-prop-names-in-kit-pages/invalid/svelte-config/errors.yaml b/packages/eslint-plugin-svelte/tests/fixtures/rules/valid-prop-names-in-kit-pages/invalid/svelte-config/errors.yaml
index 56a75c0..1a29759 100644
--- a/packages/eslint-plugin-svelte/tests/fixtures/rules/valid-prop-names-in-kit-pages/invalid/svelte-config/errors.yaml
+++ b/packages/eslint-plugin-svelte/tests/fixtures/rules/valid-prop-names-in-kit-pages/invalid/svelte-config/errors.yaml
@@ -14,11 +14,3 @@
   line: 4
   column: 20
   suggestions: null
-- message: disallow props other than data or errors in SvelteKit page components.
-  line: 5
-  column: 21
-  suggestions: null
-- message: disallow props other than data or errors in SvelteKit page components.
-  line: 5
-  column: 36
-  suggestions: null
diff --git a/packages/eslint-plugin-svelte/tests/fixtures/rules/valid-prop-names-in-kit-pages/valid/svelte5-typescript/+page.svelte b/packages/eslint-plugin-svelte/tests/fixtures/rules/valid-prop-names-in-kit-pages/valid/svelte5-typescript/+page.svelte
new file mode 100644
index 0000000..2c292cc
--- /dev/null
+++ b/packages/eslint-plugin-svelte/tests/fixtures/rules/valid-prop-names-in-kit-pages/valid/svelte5-typescript/+page.svelte
@@ -0,0 +1,5 @@
+<script lang="ts">
+	let { data: pageData }: { data: PageData } = $props();
+</script>
+
+{pageData}
diff --git a/packages/eslint-plugin-svelte/tests/fixtures/rules/valid-prop-names-in-kit-pages/valid/svelte5-typescript/_config.json b/packages/eslint-plugin-svelte/tests/fixtures/rules/valid-prop-names-in-kit-pages/valid/svelte5-typescript/_config.json
new file mode 100644
index 0000000..7a71ebe
--- /dev/null
+++ b/packages/eslint-plugin-svelte/tests/fixtures/rules/valid-prop-names-in-kit-pages/valid/svelte5-typescript/_config.json
@@ -0,0 +1,13 @@
+{
+	"languageOptions": {
+		"parserOptions": {
+			"svelteConfig": {
+				"kit": {
+					"files": {
+						"routes": "tests/fixtures/rules/valid-prop-names-in-kit-pages/invalid/svelte5"
+					}
+				}
+			}
+		}
+	}
+}
diff --git a/packages/eslint-plugin-svelte/tests/fixtures/rules/valid-prop-names-in-kit-pages/valid/svelte5-typescript/requirements.json b/packages/eslint-plugin-svelte/tests/fixtures/rules/valid-prop-names-in-kit-pages/valid/svelte5-typescript/requirements.json
new file mode 100644
index 0000000..0192b10
--- /dev/null
+++ b/packages/eslint-plugin-svelte/tests/fixtures/rules/valid-prop-names-in-kit-pages/valid/svelte5-typescript/requirements.json
@@ -0,0 +1,3 @@
+{
+	"svelte": ">=5.0.0-0"
+}
diff --git a/packages/eslint-plugin-svelte/tests/fixtures/rules/valid-prop-names-in-kit-pages/valid/test002/+page.svelte b/packages/eslint-plugin-svelte/tests/fixtures/rules/valid-prop-names-in-kit-pages/valid/test002-1/+page.svelte
similarity index 100%
rename from packages/eslint-plugin-svelte/tests/fixtures/rules/valid-prop-names-in-kit-pages/valid/test002/+page.svelte
rename to packages/eslint-plugin-svelte/tests/fixtures/rules/valid-prop-names-in-kit-pages/valid/test002-1/+page.svelte
diff --git a/packages/eslint-plugin-svelte/tests/fixtures/rules/valid-prop-names-in-kit-pages/valid/test002/_config.json b/packages/eslint-plugin-svelte/tests/fixtures/rules/valid-prop-names-in-kit-pages/valid/test002-1/_config.json
similarity index 100%
rename from packages/eslint-plugin-svelte/tests/fixtures/rules/valid-prop-names-in-kit-pages/valid/test002/_config.json
rename to packages/eslint-plugin-svelte/tests/fixtures/rules/valid-prop-names-in-kit-pages/valid/test002-1/_config.json
diff --git a/packages/eslint-plugin-svelte/tests/fixtures/rules/valid-prop-names-in-kit-pages/valid/test002-2/+page.svelte b/packages/eslint-plugin-svelte/tests/fixtures/rules/valid-prop-names-in-kit-pages/valid/test002-2/+page.svelte
new file mode 100644
index 0000000..eb7393f
--- /dev/null
+++ b/packages/eslint-plugin-svelte/tests/fixtures/rules/valid-prop-names-in-kit-pages/valid/test002-2/+page.svelte
@@ -0,0 +1,8 @@
+<script module>
+	export let data;
+	export let errors;
+	export let foo;
+	export let bar;
+</script>
+
+{data}, {errors}, {foo}, {bar}
diff --git a/packages/eslint-plugin-svelte/tests/fixtures/rules/valid-prop-names-in-kit-pages/valid/test003/_config.json b/packages/eslint-plugin-svelte/tests/fixtures/rules/valid-prop-names-in-kit-pages/valid/test002-2/_config.json
similarity index 100%
rename from packages/eslint-plugin-svelte/tests/fixtures/rules/valid-prop-names-in-kit-pages/valid/test003/_config.json
rename to packages/eslint-plugin-svelte/tests/fixtures/rules/valid-prop-names-in-kit-pages/valid/test002-2/_config.json
diff --git a/packages/eslint-plugin-svelte/tests/fixtures/rules/valid-prop-names-in-kit-pages/valid/test003/+page.svelte b/packages/eslint-plugin-svelte/tests/fixtures/rules/valid-prop-names-in-kit-pages/valid/test003-1/+page.svelte
similarity index 100%
rename from packages/eslint-plugin-svelte/tests/fixtures/rules/valid-prop-names-in-kit-pages/valid/test003/+page.svelte
rename to packages/eslint-plugin-svelte/tests/fixtures/rules/valid-prop-names-in-kit-pages/valid/test003-1/+page.svelte
diff --git a/packages/eslint-plugin-svelte/tests/fixtures/rules/valid-prop-names-in-kit-pages/valid/test004/_config.json b/packages/eslint-plugin-svelte/tests/fixtures/rules/valid-prop-names-in-kit-pages/valid/test003-1/_config.json
similarity index 100%
rename from packages/eslint-plugin-svelte/tests/fixtures/rules/valid-prop-names-in-kit-pages/valid/test004/_config.json
rename to packages/eslint-plugin-svelte/tests/fixtures/rules/valid-prop-names-in-kit-pages/valid/test003-1/_config.json
diff --git a/packages/eslint-plugin-svelte/tests/fixtures/rules/valid-prop-names-in-kit-pages/valid/test003-2/+page.svelte b/packages/eslint-plugin-svelte/tests/fixtures/rules/valid-prop-names-in-kit-pages/valid/test003-2/+page.svelte
new file mode 100644
index 0000000..7eaf286
--- /dev/null
+++ b/packages/eslint-plugin-svelte/tests/fixtures/rules/valid-prop-names-in-kit-pages/valid/test003-2/+page.svelte
@@ -0,0 +1,5 @@
+<script module>
+	export const { data, errors } = { data: {}, errors: {} };
+</script>
+
+{data}, {errors}
diff --git a/packages/eslint-plugin-svelte/tests/fixtures/rules/valid-prop-names-in-kit-pages/valid/test003-2/_config.json b/packages/eslint-plugin-svelte/tests/fixtures/rules/valid-prop-names-in-kit-pages/valid/test003-2/_config.json
new file mode 100644
index 0000000..9861188
--- /dev/null
+++ b/packages/eslint-plugin-svelte/tests/fixtures/rules/valid-prop-names-in-kit-pages/valid/test003-2/_config.json
@@ -0,0 +1,11 @@
+{
+	"settings": {
+		"svelte": {
+			"kit": {
+				"files": {
+					"routes": "tests/fixtures"
+				}
+			}
+		}
+	}
+}
diff --git a/packages/eslint-plugin-svelte/tests/fixtures/rules/valid-prop-names-in-kit-pages/valid/test004/+page.svelte b/packages/eslint-plugin-svelte/tests/fixtures/rules/valid-prop-names-in-kit-pages/valid/test004-1/+page.svelte
similarity index 100%
rename from packages/eslint-plugin-svelte/tests/fixtures/rules/valid-prop-names-in-kit-pages/valid/test004/+page.svelte
rename to packages/eslint-plugin-svelte/tests/fixtures/rules/valid-prop-names-in-kit-pages/valid/test004-1/+page.svelte
diff --git a/packages/eslint-plugin-svelte/tests/fixtures/rules/valid-prop-names-in-kit-pages/valid/test004-1/_config.json b/packages/eslint-plugin-svelte/tests/fixtures/rules/valid-prop-names-in-kit-pages/valid/test004-1/_config.json
new file mode 100644
index 0000000..9861188
--- /dev/null
+++ b/packages/eslint-plugin-svelte/tests/fixtures/rules/valid-prop-names-in-kit-pages/valid/test004-1/_config.json
@@ -0,0 +1,11 @@
+{
+	"settings": {
+		"svelte": {
+			"kit": {
+				"files": {
+					"routes": "tests/fixtures"
+				}
+			}
+		}
+	}
+}
diff --git a/packages/eslint-plugin-svelte/tests/fixtures/rules/valid-prop-names-in-kit-pages/valid/test004-2/+page.svelte b/packages/eslint-plugin-svelte/tests/fixtures/rules/valid-prop-names-in-kit-pages/valid/test004-2/+page.svelte
new file mode 100644
index 0000000..c28b010
--- /dev/null
+++ b/packages/eslint-plugin-svelte/tests/fixtures/rules/valid-prop-names-in-kit-pages/valid/test004-2/+page.svelte
@@ -0,0 +1,5 @@
+<script module>
+	export const { data2: data, errors2: errors } = { data2: {}, errors2: {} };
+</script>
+
+{data}, {errors}
diff --git a/packages/eslint-plugin-svelte/tests/fixtures/rules/valid-prop-names-in-kit-pages/valid/test004-2/_config.json b/packages/eslint-plugin-svelte/tests/fixtures/rules/valid-prop-names-in-kit-pages/valid/test004-2/_config.json
new file mode 100644
index 0000000..9861188
--- /dev/null
+++ b/packages/eslint-plugin-svelte/tests/fixtures/rules/valid-prop-names-in-kit-pages/valid/test004-2/_config.json
@@ -0,0 +1,11 @@
+{
+	"settings": {
+		"svelte": {
+			"kit": {
+				"files": {
+					"routes": "tests/fixtures"
+				}
+			}
+		}
+	}
+}
diff --git a/packages/eslint-plugin-svelte/tests/fixtures/rules/valid-style-parse/invalid/invalid-css01-errors.yaml b/packages/eslint-plugin-svelte/tests/fixtures/rules/valid-style-parse/invalid/invalid-css01-errors.yaml
new file mode 100644
index 0000000..fade069
--- /dev/null
+++ b/packages/eslint-plugin-svelte/tests/fixtures/rules/valid-style-parse/invalid/invalid-css01-errors.yaml
@@ -0,0 +1,6 @@
+- message: 'Error parsing style element. Error message:
+    "tests/fixtures/rules/valid-style-parse/invalid/invalid-css01-input.svelte:4:11:
+    Unknown word .div-class/35"'
+  line: 7
+  column: 1
+  suggestions: null
diff --git a/packages/eslint-plugin-svelte/tests/fixtures/rules/valid-style-parse/invalid/invalid-css01-input.svelte b/packages/eslint-plugin-svelte/tests/fixtures/rules/valid-style-parse/invalid/invalid-css01-input.svelte
new file mode 100644
index 0000000..a3184db
--- /dev/null
+++ b/packages/eslint-plugin-svelte/tests/fixtures/rules/valid-style-parse/invalid/invalid-css01-input.svelte
@@ -0,0 +1,18 @@
+<div class="container">
+  <div class="div-class">Hello</div>
+
+  <span class="span-class">World!</span>
+</div>
+
+<style>
+  // This syntax is intentionally invalid CSS - this is to be used to test resiliency against invalid input
+  .container {
+    class .div-class/35
+      # Weird comment
+      color: red;
+
+    .span-class begin
+      font-weight: bold;
+    end
+  }
+</style>
diff --git a/packages/eslint-plugin-svelte/tests/fixtures/rules/valid-style-parse/invalid/invalid-scss01-errors.yaml b/packages/eslint-plugin-svelte/tests/fixtures/rules/valid-style-parse/invalid/invalid-scss01-errors.yaml
new file mode 100644
index 0000000..5067576
--- /dev/null
+++ b/packages/eslint-plugin-svelte/tests/fixtures/rules/valid-style-parse/invalid/invalid-scss01-errors.yaml
@@ -0,0 +1,6 @@
+- message: 'Error parsing style element. Error message:
+    "tests/fixtures/rules/valid-style-parse/invalid/invalid-scss01-input.svelte:4:11:
+    Unknown word .div-class/35"'
+  line: 7
+  column: 1
+  suggestions: null
diff --git a/packages/eslint-plugin-svelte/tests/fixtures/rules/valid-style-parse/invalid/invalid-scss01-input.svelte b/packages/eslint-plugin-svelte/tests/fixtures/rules/valid-style-parse/invalid/invalid-scss01-input.svelte
new file mode 100644
index 0000000..df4d586
--- /dev/null
+++ b/packages/eslint-plugin-svelte/tests/fixtures/rules/valid-style-parse/invalid/invalid-scss01-input.svelte
@@ -0,0 +1,18 @@
+<div class="container">
+  <div class="div-class">Hello</div>
+
+  <span class="span-class">World!</span>
+</div>
+
+<style lang="scss">
+  // This syntax is intentionally invalid CSS - this is to be used to test resiliency against invalid input
+  .container {
+    class .div-class/35
+      # Weird comment
+      color: red;
+
+    .span-class begin
+      font-weight: bold;
+    end
+  }
+</style>
diff --git a/packages/eslint-plugin-svelte/tests/fixtures/rules/valid-style-parse/invalid/unknown-lang01-errors.yaml b/packages/eslint-plugin-svelte/tests/fixtures/rules/valid-style-parse/invalid/unknown-lang01-errors.yaml
new file mode 100644
index 0000000..0ca96bb
--- /dev/null
+++ b/packages/eslint-plugin-svelte/tests/fixtures/rules/valid-style-parse/invalid/unknown-lang01-errors.yaml
@@ -0,0 +1,4 @@
+- message: Found unsupported style element language "invalid-lang"
+  line: 7
+  column: 1
+  suggestions: null
diff --git a/packages/eslint-plugin-svelte/tests/fixtures/rules/valid-style-parse/invalid/unknown-lang01-input.svelte b/packages/eslint-plugin-svelte/tests/fixtures/rules/valid-style-parse/invalid/unknown-lang01-input.svelte
new file mode 100644
index 0000000..43e1850
--- /dev/null
+++ b/packages/eslint-plugin-svelte/tests/fixtures/rules/valid-style-parse/invalid/unknown-lang01-input.svelte
@@ -0,0 +1,18 @@
+<div class="container">
+  <div class="div-class">Hello</div>
+
+  <span class="span-class">World!</span>
+</div>
+
+<style lang="invalid-lang">
+  // This syntax is intentionally invalid CSS - this is to be used to test resiliency against unknown style languages.
+  .container {
+    class .div-class/35
+      # Weird comment
+      color: red;
+
+    .span-class begin
+      font-weight: bold;
+    end
+  }
+</style>
diff --git a/packages/eslint-plugin-svelte/tests/fixtures/rules/valid-style-parse/valid/no-style01-input.svelte b/packages/eslint-plugin-svelte/tests/fixtures/rules/valid-style-parse/valid/no-style01-input.svelte
new file mode 100644
index 0000000..f3f9e58
--- /dev/null
+++ b/packages/eslint-plugin-svelte/tests/fixtures/rules/valid-style-parse/valid/no-style01-input.svelte
@@ -0,0 +1,3 @@
+<a href="https://svelte.dev">Hello</a>
+
+<span style="font-weight: bold;">World!</span>
diff --git a/packages/eslint-plugin-svelte/tests/fixtures/rules/valid-style-parse/valid/valid-css01-input.svelte b/packages/eslint-plugin-svelte/tests/fixtures/rules/valid-style-parse/valid/valid-css01-input.svelte
new file mode 100644
index 0000000..cca266a
--- /dev/null
+++ b/packages/eslint-plugin-svelte/tests/fixtures/rules/valid-style-parse/valid/valid-css01-input.svelte
@@ -0,0 +1,13 @@
+<div class="div-class">Hello</div>
+
+<span class="span-class">World!</span>
+
+<style>
+	.div-class {
+		color: red;
+	}
+
+	.span-class {
+		font-weight: bold;
+	}
+</style>
diff --git a/packages/eslint-plugin-svelte/tests/fixtures/rules/valid-style-parse/valid/valid-scss01-input.svelte b/packages/eslint-plugin-svelte/tests/fixtures/rules/valid-style-parse/valid/valid-scss01-input.svelte
new file mode 100644
index 0000000..f7130b5
--- /dev/null
+++ b/packages/eslint-plugin-svelte/tests/fixtures/rules/valid-style-parse/valid/valid-scss01-input.svelte
@@ -0,0 +1,18 @@
+<div class="container">
+	<div class="div-class">Hello</div>
+
+	<span class="span-class">World!</span>
+</div>
+
+<style lang="scss">
+	.container {
+		.div-class {
+			// This is an inline comment
+			color: red;
+		}
+
+		.span-class {
+			font-weight: bold;
+		}
+	}
+</style>
diff --git a/packages/eslint-plugin-svelte/tests/src/rules/no-unused-props.ts b/packages/eslint-plugin-svelte/tests/src/rules/no-unused-props.ts
new file mode 100644
index 0000000..104266b
--- /dev/null
+++ b/packages/eslint-plugin-svelte/tests/src/rules/no-unused-props.ts
@@ -0,0 +1,12 @@
+import { RuleTester } from '../../utils/eslint-compat.js';
+import rule from '../../../src/rules/no-unused-props.js';
+import { loadTestCases } from '../../utils/utils.js';
+
+const tester = new RuleTester({
+	languageOptions: {
+		ecmaVersion: 2020,
+		sourceType: 'module'
+	}
+});
+
+tester.run('no-unused-props', rule as any, loadTestCases('no-unused-props'));
diff --git a/packages/eslint-plugin-svelte/tests/src/rules/valid-style-parse.ts b/packages/eslint-plugin-svelte/tests/src/rules/valid-style-parse.ts
new file mode 100644
index 0000000..d5d1c5f
--- /dev/null
+++ b/packages/eslint-plugin-svelte/tests/src/rules/valid-style-parse.ts
@@ -0,0 +1,12 @@
+import { RuleTester } from '../../utils/eslint-compat.js';
+import rule from '../../../src/rules/valid-style-parse.js';
+import { loadTestCases } from '../../utils/utils.js';
+
+const tester = new RuleTester({
+	languageOptions: {
+		ecmaVersion: 2020,
+		sourceType: 'module'
+	}
+});
+
+tester.run('valid-style-parse', rule as any, loadTestCases('valid-style-parse'));