diff --git a/.changeset/v-vapor-rule.md b/.changeset/v-vapor-rule.md
new file mode 100644
index 000000000000..39344981aaf5
--- /dev/null
+++ b/.changeset/v-vapor-rule.md
@@ -0,0 +1,5 @@
+---
+"@biomejs/biome": patch
+---
+
+Added the nursery rule `useVueVapor` to enforce `
+ /// ```
+ ///
+ /// ### Valid
+ ///
+ /// ```vue
+ ///
+ /// ```
+ ///
+ pub UseVueVapor {
+ version: "next",
+ name: "useVueVapor",
+ language: "html",
+ recommended: false,
+ domains: &[RuleDomain::Vue],
+ sources: &[],
+ fix_kind: FixKind::Unsafe,
+ }
+}
+
+impl Rule for UseVueVapor {
+ type Query = Ast;
+ type State = ();
+ type Signals = Option;
+ type Options = UseVueVaporOptions;
+
+ fn run(ctx: &RuleContext) -> Self::Signals {
+ let opening = ctx.query();
+
+ let name = opening.name().ok()?;
+ let name_token = name.value_token().ok()?;
+ if !name_token.text_trimmed().eq_ignore_ascii_case("script") {
+ return None;
+ }
+
+ let attributes = opening.attributes();
+ attributes.find_by_name("setup")?;
+
+ if attributes.find_by_name("vapor").is_some() {
+ return None;
+ }
+
+ Some(())
+ }
+
+ fn diagnostic(ctx: &RuleContext, _state: &Self::State) -> Option {
+ Some(
+ RuleDiagnostic::new(
+ rule_category!(),
+ ctx.query().range(),
+ markup! {
+ "This ""
+
+
+
+
diff --git a/crates/biome_html_analyze/tests/specs/nursery/useVueVapor/invalid.vue.snap b/crates/biome_html_analyze/tests/specs/nursery/useVueVapor/invalid.vue.snap
new file mode 100644
index 000000000000..3a3a480e67d9
--- /dev/null
+++ b/crates/biome_html_analyze/tests/specs/nursery/useVueVapor/invalid.vue.snap
@@ -0,0 +1,66 @@
+---
+source: crates/biome_html_analyze/tests/spec_tests.rs
+expression: invalid.vue
+---
+# Input
+```html
+
+
+
+
+
+
+
+
+```
+
+# Diagnostics
+```
+invalid.vue:3:1 lint/nursery/useVueVapor FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
+
+ i This
+
+ i Add vapor to opt in to Vue Vapor mode:
+ 6 │
+ > 7 │
+
+ i Add vapor to opt in to Vue Vapor mode:
+
+
+
+
+
+
diff --git a/crates/biome_html_analyze/tests/specs/nursery/useVueVapor/valid.vue.snap b/crates/biome_html_analyze/tests/specs/nursery/useVueVapor/valid.vue.snap
new file mode 100644
index 000000000000..33c0e0673378
--- /dev/null
+++ b/crates/biome_html_analyze/tests/specs/nursery/useVueVapor/valid.vue.snap
@@ -0,0 +1,23 @@
+---
+source: crates/biome_html_analyze/tests/spec_tests.rs
+expression: valid.vue
+---
+# Input
+```html
+
+
+
+
+
+
+
+
+
+
+```
diff --git a/crates/biome_rule_options/src/lib.rs b/crates/biome_rule_options/src/lib.rs
index 1cc128909ad6..6464645f7254 100644
--- a/crates/biome_rule_options/src/lib.rs
+++ b/crates/biome_rule_options/src/lib.rs
@@ -416,5 +416,6 @@ pub mod use_vue_valid_v_on;
pub mod use_vue_valid_v_once;
pub mod use_vue_valid_v_pre;
pub mod use_vue_valid_v_text;
+pub mod use_vue_vapor;
pub mod use_while;
pub mod use_yield;
diff --git a/crates/biome_rule_options/src/use_vue_vapor.rs b/crates/biome_rule_options/src/use_vue_vapor.rs
new file mode 100644
index 000000000000..22ee82dd9635
--- /dev/null
+++ b/crates/biome_rule_options/src/use_vue_vapor.rs
@@ -0,0 +1,6 @@
+use biome_deserialize_macros::{Deserializable, Merge};
+use serde::{Deserialize, Serialize};
+#[derive(Default, Clone, Debug, Deserialize, Deserializable, Merge, Eq, PartialEq, Serialize)]
+#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
+#[serde(rename_all = "camelCase", deny_unknown_fields, default)]
+pub struct UseVueVaporOptions {}
diff --git a/packages/@biomejs/backend-jsonrpc/src/workspace.ts b/packages/@biomejs/backend-jsonrpc/src/workspace.ts
index 1bd519a79312..c46d44240ca3 100644
--- a/packages/@biomejs/backend-jsonrpc/src/workspace.ts
+++ b/packages/@biomejs/backend-jsonrpc/src/workspace.ts
@@ -2257,6 +2257,11 @@ See
See
*/
useVueValidVText?: UseVueValidVTextConfiguration;
+ /**
+ * Enforce opting in to Vue Vapor mode in \