diff --git a/.changeset/blue-steaks-give.md b/.changeset/blue-steaks-give.md
new file mode 100644
index 000000000000..3592049169b7
--- /dev/null
+++ b/.changeset/blue-steaks-give.md
@@ -0,0 +1,5 @@
+---
+"@biomejs/biome": patch
+---
+
+Fixed [`noUndeclaredVariables`](https://biomejs.dev/linter/rules/no-undeclared-variables/) from erroneously flagging props only used in the template section in Vue SFCs
diff --git a/crates/biome_cli/tests/cases/vue_cross_language_rules.rs b/crates/biome_cli/tests/cases/vue_cross_language_rules.rs
index d9a9e74eab40..b3a1a7c04f9e 100644
--- a/crates/biome_cli/tests/cases/vue_cross_language_rules.rs
+++ b/crates/biome_cli/tests/cases/vue_cross_language_rules.rs
@@ -81,3 +81,148 @@ const { copy: copyInviteLink, copySuccess } = useCopyFromTextbox(inviteLink, inv
result,
));
}
+
+#[test]
+fn no_undeclared_variables_not_triggered_for_define_props_type_arg() {
+ let fs = MemoryFileSystem::default();
+ let mut console = BufferConsole::default();
+ fs.insert(
+ "biome.json".into(),
+ BIOME_CONFIG_HTML_FULL_SUPPORT.as_bytes(),
+ );
+ let file = Utf8Path::new("file.vue");
+ fs.insert(
+ file.into(),
+ r#"
+
+
+ test
+
+"#
+ .as_bytes(),
+ );
+ let (fs, result) = run_cli(
+ fs,
+ &mut console,
+ Args::from(["lint", "--only=noUndeclaredVariables", file.as_str()].as_slice()),
+ );
+ assert!(result.is_ok(), "run_cli returned {result:?}");
+
+ assert_cli_snapshot(SnapshotPayload::new(
+ module_path!(),
+ "no_undeclared_variables_not_triggered_for_define_props_type_arg",
+ fs,
+ console,
+ result,
+ ));
+}
+
+#[test]
+fn no_undeclared_variables_not_triggered_defined_props_options_api() {
+ let fs = MemoryFileSystem::default();
+ let mut console = BufferConsole::default();
+ fs.insert(
+ "biome.json".into(),
+ BIOME_CONFIG_HTML_FULL_SUPPORT.as_bytes(),
+ );
+ let file = Utf8Path::new("file.vue");
+ fs.insert(
+ file.into(),
+ r#"
+
+
+ test
+
+"#
+ .as_bytes(),
+ );
+ let (fs, result) = run_cli(
+ fs,
+ &mut console,
+ Args::from(["lint", "--only=noUndeclaredVariables", file.as_str()].as_slice()),
+ );
+ assert!(result.is_ok(), "run_cli returned {result:?}");
+
+ assert_cli_snapshot(SnapshotPayload::new(
+ module_path!(),
+ "no_undeclared_variables_not_triggered_defined_props_options_api",
+ fs,
+ console,
+ result,
+ ));
+}
+
+#[test]
+fn no_undeclared_variables_not_triggered_for_define_props_type_arg_2() {
+ let fs = MemoryFileSystem::default();
+ let mut console = BufferConsole::default();
+ fs.insert(
+ "biome.json".into(),
+ BIOME_CONFIG_HTML_FULL_SUPPORT.as_bytes(),
+ );
+ let file = Utf8Path::new("file.vue");
+ fs.insert(
+ file.into(),
+ r#"
+
+
+
+
+
+"#
+ .as_bytes(),
+ );
+ let (fs, result) = run_cli(
+ fs,
+ &mut console,
+ Args::from(["lint", "--only=noUndeclaredVariables", file.as_str()].as_slice()),
+ );
+ assert!(result.is_ok(), "run_cli returned {result:?}");
+
+ assert_cli_snapshot(SnapshotPayload::new(
+ module_path!(),
+ "no_undeclared_variables_not_triggered_for_define_props_type_arg_2",
+ fs,
+ console,
+ result,
+ ));
+}
diff --git a/crates/biome_cli/tests/snapshots/main_cases_vue_cross_language_rules/no_undeclared_variables_not_triggered_defined_props_options_api.snap b/crates/biome_cli/tests/snapshots/main_cases_vue_cross_language_rules/no_undeclared_variables_not_triggered_defined_props_options_api.snap
new file mode 100644
index 000000000000..00eac6d84fc9
--- /dev/null
+++ b/crates/biome_cli/tests/snapshots/main_cases_vue_cross_language_rules/no_undeclared_variables_not_triggered_defined_props_options_api.snap
@@ -0,0 +1,38 @@
+---
+source: crates/biome_cli/tests/snap_test.rs
+expression: redactor(content)
+---
+## `biome.json`
+
+```json
+{
+ "html": {
+ "linter": { "enabled": true },
+ "experimentalFullSupportEnabled": true
+ }
+}
+```
+
+## `file.vue`
+
+```vue
+
+
+
+ test
+
+
+```
+
+# Emitted Messages
+
+```block
+Checked 1 file in