diff --git a/.changeset/slimy-pillows-change.md b/.changeset/slimy-pillows-change.md
new file mode 100644
index 00000000..0c086dce
--- /dev/null
+++ b/.changeset/slimy-pillows-change.md
@@ -0,0 +1,5 @@
+---
+"eslint-plugin-astro": minor
+---
+
+feat add `astro/missing-client-only-directive-value` rule
diff --git a/README.md b/README.md
index 2b61c204..df9ca6f0 100644
--- a/README.md
+++ b/README.md
@@ -377,6 +377,7 @@ These rules relate to possible syntax or logic errors in Astro component code:
| Rule ID | Description | |
|:--------|:------------|:---|
+| [astro/missing-client-only-directive-value](https://ota-meshi.github.io/eslint-plugin-astro/rules/missing-client-only-directive-value/) | the client:only directive is missing the correct component's framework value | ⭐ |
| [astro/no-conflict-set-directives](https://ota-meshi.github.io/eslint-plugin-astro/rules/no-conflict-set-directives/) | disallow conflicting set directives and child contents | ⭐ |
| [astro/no-deprecated-astro-canonicalurl](https://ota-meshi.github.io/eslint-plugin-astro/rules/no-deprecated-astro-canonicalurl/) | disallow using deprecated `Astro.canonicalURL` | ⭐ |
| [astro/no-deprecated-astro-fetchcontent](https://ota-meshi.github.io/eslint-plugin-astro/rules/no-deprecated-astro-fetchcontent/) | disallow using deprecated `Astro.fetchContent()` | ⭐🔧 |
diff --git a/docs/rules.md b/docs/rules.md
index d3b156bd..7e604bae 100644
--- a/docs/rules.md
+++ b/docs/rules.md
@@ -13,6 +13,7 @@ These rules relate to possible syntax or logic errors in Astro component code:
| Rule ID | Description | |
|:--------|:------------|:---|
+| [astro/missing-client-only-directive-value](./rules/missing-client-only-directive-value.md) | the client:only directive is missing the correct component's framework value | ⭐ |
| [astro/no-conflict-set-directives](./rules/no-conflict-set-directives.md) | disallow conflicting set directives and child contents | ⭐ |
| [astro/no-deprecated-astro-canonicalurl](./rules/no-deprecated-astro-canonicalurl.md) | disallow using deprecated `Astro.canonicalURL` | ⭐ |
| [astro/no-deprecated-astro-fetchcontent](./rules/no-deprecated-astro-fetchcontent.md) | disallow using deprecated `Astro.fetchContent()` | ⭐🔧 |
diff --git a/docs/rules/missing-client-only-directive-value.md b/docs/rules/missing-client-only-directive-value.md
new file mode 100644
index 00000000..fe624138
--- /dev/null
+++ b/docs/rules/missing-client-only-directive-value.md
@@ -0,0 +1,49 @@
+---
+title: "astro/missing-client-only-directive-value"
+description: "the client:only directive is missing the correct component's framework value"
+---
+
+# astro/missing-client-only-directive-value
+
+> the client:only directive is missing the correct component's framework value
+
+- ❗ **_This rule has not been released yet._**
+- ⚙ This rule is included in `"plugin:astro/recommended"`.
+
+## 📖 Rule Details
+
+This rule reports not setting a value for the `client:only` directive.
+
+
+
+
+
+```astro
+---
+/* eslint astro/missing-client-only-directive-value: "error" */
+
+---
+
+{/* ✓ GOOD */}
+
+
+{/* ✗ BAD */}
+
+```
+
+
+
+## 🔧 Options
+
+Nothing.
+
+
+## 📚 Further Reading
+
+- [Astro Documentation | Template Directives Reference > client:only](https://docs.astro.build/en/reference/directives-reference/#clientonly)
+
+## 🔍 Implementation
+
+- [Rule source](https://github.com/ota-meshi/eslint-plugin-astro/blob/main/src/rules/missing-client-only-directive-value.ts)
+- [Test source](https://github.com/ota-meshi/eslint-plugin-astro/blob/main/tests/src/rules/missing-client-only-directive-value.ts)
+- [Test fixture sources](https://github.com/ota-meshi/eslint-plugin-astro/tree/main/tests/fixtures/rules/missing-client-only-directive-value)
diff --git a/src/configs/flat/recommended.ts b/src/configs/flat/recommended.ts
index 6dca72a5..08bd802f 100644
--- a/src/configs/flat/recommended.ts
+++ b/src/configs/flat/recommended.ts
@@ -7,6 +7,7 @@ export default [
{
rules: {
// eslint-plugin-astro rules
+ "astro/missing-client-only-directive-value": "error",
"astro/no-conflict-set-directives": "error",
"astro/no-deprecated-astro-canonicalurl": "error",
"astro/no-deprecated-astro-fetchcontent": "error",
diff --git a/src/configs/recommended.ts b/src/configs/recommended.ts
index 87b267f7..f029a8b5 100644
--- a/src/configs/recommended.ts
+++ b/src/configs/recommended.ts
@@ -9,6 +9,7 @@ export = {
extends: [baseExtend],
rules: {
// eslint-plugin-astro rules
+ "astro/missing-client-only-directive-value": "error",
"astro/no-conflict-set-directives": "error",
"astro/no-deprecated-astro-canonicalurl": "error",
"astro/no-deprecated-astro-fetchcontent": "error",
diff --git a/src/rules/missing-client-only-directive-value.ts b/src/rules/missing-client-only-directive-value.ts
new file mode 100644
index 00000000..4684223c
--- /dev/null
+++ b/src/rules/missing-client-only-directive-value.ts
@@ -0,0 +1,50 @@
+import type { AST } from "astro-eslint-parser"
+import { createRule } from "../utils"
+import { getAttributeName, getStaticAttributeValue } from "../utils/ast-utils"
+import { getSourceCode } from "../utils/compat"
+
+export default createRule("missing-client-only-directive-value", {
+ meta: {
+ docs: {
+ description:
+ "the client:only directive is missing the correct component's framework value",
+ category: "Possible Errors",
+ recommended: true,
+ },
+ schema: [],
+ messages: {
+ missingValue: "`client:only` directive is missing a value",
+ },
+ type: "problem",
+ },
+ create(context) {
+ const sourceCode = getSourceCode(context)
+ if (!sourceCode.parserServices.isAstro) {
+ return {}
+ }
+
+ /** VerifyDirectiveValue */
+ function verifyDirectiveValue(
+ attr: AST.JSXAttribute | AST.AstroTemplateLiteralAttribute,
+ ) {
+ const directiveName = getAttributeName(attr)
+ const directiveValue = getStaticAttributeValue(attr)
+
+ if (directiveName !== "client:only") return
+
+ if (directiveValue !== null) {
+ return
+ }
+
+ context.report({
+ node: attr.name,
+ messageId: "missingValue",
+ })
+ }
+
+ return {
+ JSXAttribute: verifyDirectiveValue,
+ AstroTemplateLiteralAttribute: verifyDirectiveValue,
+ }
+ },
+})
diff --git a/src/utils/rules.ts b/src/utils/rules.ts
index 76b2bb58..a0aa7e6d 100644
--- a/src/utils/rules.ts
+++ b/src/utils/rules.ts
@@ -2,6 +2,7 @@
// This file has been automatically generated,
// in order to update its content execute "npm run update"
import type { RuleModule } from "../types"
+import missingClientOnlyDirectiveValue from "../rules/missing-client-only-directive-value"
import noConflictSetDirectives from "../rules/no-conflict-set-directives"
import noDeprecatedAstroCanonicalurl from "../rules/no-deprecated-astro-canonicalurl"
import noDeprecatedAstroFetchcontent from "../rules/no-deprecated-astro-fetchcontent"
@@ -19,6 +20,7 @@ import validCompile from "../rules/valid-compile"
import { buildA11yRules } from "../a11y"
export const rules = [
+ missingClientOnlyDirectiveValue,
noConflictSetDirectives,
noDeprecatedAstroCanonicalurl,
noDeprecatedAstroFetchcontent,
diff --git a/tests/fixtures/rules/missing-client-only-directive-value/SomeSvelteComponent.svelte b/tests/fixtures/rules/missing-client-only-directive-value/SomeSvelteComponent.svelte
new file mode 100644
index 00000000..8e5d3132
--- /dev/null
+++ b/tests/fixtures/rules/missing-client-only-directive-value/SomeSvelteComponent.svelte
@@ -0,0 +1,6 @@
+
+
+
+ Hello Svelte!
+
diff --git a/tests/fixtures/rules/missing-client-only-directive-value/invalid/missing-client-only-directive-value-errors.json b/tests/fixtures/rules/missing-client-only-directive-value/invalid/missing-client-only-directive-value-errors.json
new file mode 100644
index 00000000..e821bb68
--- /dev/null
+++ b/tests/fixtures/rules/missing-client-only-directive-value/invalid/missing-client-only-directive-value-errors.json
@@ -0,0 +1,7 @@
+[
+ {
+ "message": "`client:only` directive is missing a value",
+ "line": 5,
+ "column": 22
+ }
+]
diff --git a/tests/fixtures/rules/missing-client-only-directive-value/invalid/missing-client-only-directive-value-input.astro b/tests/fixtures/rules/missing-client-only-directive-value/invalid/missing-client-only-directive-value-input.astro
new file mode 100644
index 00000000..1808b8bf
--- /dev/null
+++ b/tests/fixtures/rules/missing-client-only-directive-value/invalid/missing-client-only-directive-value-input.astro
@@ -0,0 +1,5 @@
+---
+import SomeSvelteComponent from "../SomeSvelteComponent.svelte"
+---
+
+
diff --git a/tests/fixtures/rules/missing-client-only-directive-value/valid/client-only-value.astro b/tests/fixtures/rules/missing-client-only-directive-value/valid/client-only-value.astro
new file mode 100644
index 00000000..a1aa6a99
--- /dev/null
+++ b/tests/fixtures/rules/missing-client-only-directive-value/valid/client-only-value.astro
@@ -0,0 +1,9 @@
+---
+import SomeSvelteComponent from "../SomeSvelteComponent.svelte"
+let string = "svelte"
+---
+
+
+
+
+
diff --git a/tests/src/rules/missing-client-only-directive-value.ts b/tests/src/rules/missing-client-only-directive-value.ts
new file mode 100644
index 00000000..6563abfa
--- /dev/null
+++ b/tests/src/rules/missing-client-only-directive-value.ts
@@ -0,0 +1,16 @@
+import { RuleTester } from "../../utils/eslint-compat"
+import rule from "../../../src/rules/missing-client-only-directive-value"
+import { loadTestCases } from "../../utils/utils"
+
+const tester = new RuleTester({
+ languageOptions: {
+ ecmaVersion: 2020,
+ sourceType: "module",
+ },
+})
+
+tester.run(
+ "missing-client-only-directive-value",
+ rule as any,
+ loadTestCases("missing-client-only-directive-value"),
+)