diff --git a/apps/web-tanstack/src/routes/__root.tsx b/apps/web-tanstack/src/routes/__root.tsx
index 25cde5e..28e1f83 100644
--- a/apps/web-tanstack/src/routes/__root.tsx
+++ b/apps/web-tanstack/src/routes/__root.tsx
@@ -1,6 +1,17 @@
import { Outlet, createRootRoute } from "@tanstack/react-router";
import { getNonce } from "@repo/core-shared/security/tanstack";
+function RootComponent() {
+ const { nonce } = Route.useLoaderData();
+ return (
+ <>
+ {/* nonce exposed to client so instrumentation-client.ts can read it */}
+
+
+ >
+ );
+}
+
export const Route = createRootRoute({
loader: async () => {
try {
@@ -12,14 +23,5 @@ export const Route = createRootRoute({
return { nonce: "" };
}
},
- component: () => {
- const { nonce } = Route.useLoaderData();
- return (
- <>
- {/* nonce exposed to client so instrumentation-client.ts can read it */}
-
-
- >
- );
- },
+ component: RootComponent,
});
diff --git a/packages/core-eslint/base.js b/packages/core-eslint/base.js
index 90ed3ef..c9122ee 100644
--- a/packages/core-eslint/base.js
+++ b/packages/core-eslint/base.js
@@ -3,6 +3,7 @@ import eslintConfigPrettier from "eslint-config-prettier";
import tseslint from "typescript-eslint";
import turboPlugin from "eslint-plugin-turbo";
import boundaries from "eslint-plugin-boundaries";
+import reactHooks from "eslint-plugin-react-hooks";
import globals from "globals";
import conformancePlugin from "./plugin.js";
import path from "node:path";
@@ -195,6 +196,13 @@ export default [
"no-restricted-imports": "off",
},
},
+ // React rules-of-hooks for every TSX surface (apps, core-ui, feature
+ // ui/**). Scoped to .tsx so non-React packages are untouched.
+ {
+ files: ["**/*.tsx"],
+ plugins: { "react-hooks": reactHooks },
+ rules: reactHooks.configs.recommended.rules,
+ },
// E1 — Event handlers must not be re-exported. Wire them only inside the
// consumer feature's bind-production / bind-dev-seed (spec § 2.2 Rule E1).
// J — Direct `payload.jobs.*` access is forbidden outside the integration
diff --git a/packages/core-eslint/package.json b/packages/core-eslint/package.json
index 5b4190a..fe578e0 100644
--- a/packages/core-eslint/package.json
+++ b/packages/core-eslint/package.json
@@ -19,6 +19,7 @@
"eslint": "^9.20.0",
"eslint-config-prettier": "^10.1.0",
"eslint-plugin-boundaries": "^4.2.2",
+ "eslint-plugin-react-hooks": "^5.2.0",
"eslint-plugin-turbo": "^2.4.0",
"typescript-eslint": "^8.25.0",
"vitest": "^3.1.0"
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 6e62c51..08312c0 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -733,6 +733,9 @@ importers:
eslint-plugin-boundaries:
specifier: ^4.2.2
version: 4.2.2(@typescript-eslint/parser@8.58.0(eslint@9.39.4(jiti@2.7.0))(typescript@5.9.3))(eslint@9.39.4(jiti@2.7.0))
+ eslint-plugin-react-hooks:
+ specifier: ^5.2.0
+ version: 5.2.0(eslint@9.39.4(jiti@2.7.0))
eslint-plugin-turbo:
specifier: ^2.4.0
version: 2.9.4(eslint@9.39.4(jiti@2.7.0))(turbo@2.9.4)
@@ -10680,6 +10683,15 @@ packages:
peerDependencies:
eslint: ">=6.0.0"
+ eslint-plugin-react-hooks@5.2.0:
+ resolution:
+ {
+ integrity: sha512-+f15FfK64YQwZdJNELETdn5ibXEUQmW1DZL6KXhNnc2heoy/sg9VJJeT7n8TlMWouzWqSWavFkIhHyIbIAEapg==,
+ }
+ engines: { node: ">=10" }
+ peerDependencies:
+ eslint: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0
+
eslint-plugin-turbo@2.9.4:
resolution:
{
@@ -24134,6 +24146,10 @@ snapshots:
- eslint-import-resolver-webpack
- supports-color
+ eslint-plugin-react-hooks@5.2.0(eslint@9.39.4(jiti@2.7.0)):
+ dependencies:
+ eslint: 9.39.4(jiti@2.7.0)
+
eslint-plugin-turbo@2.9.4(eslint@9.39.4(jiti@2.7.0))(turbo@2.9.4):
dependencies:
dotenv: 16.0.3