Skip to main content

Rules & Hooks

Rules enforce coding standards automatically. Hooks run on file changes.


Rules

Rules are applied automatically based on file path patterns.

TypeScript Rule

Files: *.ts, *.tsx

GuidelineDetails
No anyUse unknown if type is truly unknown
Strict typesCreate interfaces for API responses
NamingPascalCase for types/interfaces
ImportsUse @/ path alias, group by type
// Good
interface UserProfile {
id: string;
name: string;
}

// Bad
const data: any = fetchData();

React Rule

Files: *.tsx, components/**

GuidelineDetails
Functional onlyNo class components
One per fileName file same as component
Hooks at topInclude all useEffect dependencies
AccessibilityUse semantic HTML, aria-labels
// Good
export function WorkoutCard({ workout }: WorkoutCardProps) {
const [expanded, setExpanded] = useState(false);
// ...
}

// Bad
export class WorkoutCard extends React.Component {}

API Rule

Files: app/api/**/*.ts

GuidelineDetails
Auth firstAlways call getCurrentUser()
ValidateUse Zod for request bodies
Error formatReturn { error, details? }
Status codesUse proper HTTP codes
// Required pattern
export async function POST(request: NextRequest) {
const user = await getCurrentUser();
if (!user) {
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
}

const parsed = schema.safeParse(await request.json());
if (!parsed.success) {
return NextResponse.json({
error: 'Invalid data',
details: parsed.error.flatten()
}, { status: 400 });
}
// ...
}

Testing Rule

Files: *.test.ts, *.test.tsx, *.spec.ts

GuidelineDetails
Query by roleNot by test-id
userEventNot fireEvent
AsyncUse findBy* for async content
Mock resetsReset mocks in beforeEach
// Good
await user.click(screen.getByRole('button', { name: /submit/i }));
await screen.findByText(/success/i);

// Bad
fireEvent.click(screen.getByTestId('submit-btn'));

Hooks

Hooks run automatically after file operations.

ESLint Hook

Trigger: Edit or Write .ts, .tsx, .js, .jsx

Action: Runs eslint --fix to auto-fix issues.

Prettier Hook

Trigger: Edit or Write .ts, .tsx, .js, .jsx, .json, .md

Action: Runs prettier --write to format code.


Configuration

Rules Location

.claude/rules/
├── typescript.md
├── react.md
├── api.md
└── testing.md

Each rule file has a frontmatter specifying which paths it applies to:

---
paths: "**/*.ts,**/*.tsx"
---

# TypeScript Rules
...

Hooks Location

Hooks are defined in .claude/settings.json:

{
"hooks": {
"PostToolUse": [
{
"matcher": "Edit|Write",
"hooks": [
{
"type": "command",
"command": "npx eslint --fix \"$FILE\"",
"timeout": 10000
}
]
}
]
}
}