Enforce React/TypeScript code style rules. Run proactively after writing or modifying any React component, hook, or related file (.tsx, .ts in component/hook directories). Also run when the user explicitly invokes this skill. Auto-fix all violations found. Triggers on: creating components, writing hooks, editing React files, refactoring, or when user says 'review my code', 'code review', 'check style', 'react-code-review'.
Enforce strict React/TypeScript code style. Review all changed React files, identify violations, and auto-fix them.
Always use namespace import:
// CORRECT
import * as React from "react";
// WRONG
import React from "react";
import { useState, useEffect } from "react";
Components and hooks always use a folder with index.tsx:
my-component/
index.tsx
utils.ts (optional — local helpers/constants)
use-my-hook/ (optional — local hook)
index.tsx
use-my-hook/
index.tsx
utils.ts (optional)
utils.ts file in the component/hook folder.index.tsx alongside the component. All other types go in separate files.export default (ESLint handles Next.js-specific exceptions).type over interface unless interface is absolutely required for a specific syntax edge case.<ComponentName>Props.null | string doesn't need one).This is the canonical ordering. Every component MUST follow this structure:
import * as React from "react";
import { useTranslation } from "react-i18next";
import { useSomeCustomEffect } from "./use-some-custom-effect";
export type MyComponentProps = {
someProp: null | string;
};
export const MyComponent: React.FC<MyComponentProps> = (props) => {
const { someProp } = props;
//* State
const { t } = useTranslation();
const queryResult = useSomeQuery();
const [someState, setSomeState] = React.useState();
//* Refs
const someRef = React.useRef();
//* Variables
const isSomething = someProp != null;
const memoizedValue = React.useMemo(() => compute(someProp), [someProp]);
//* Handlers
const doSomething = () => {};
const memoizedHandler = React.useCallback(() => {}, []);
//* Effects
useSomeCustomEffect();
React.useEffect(() => {}, []);
if (someProp == null) {
return null;
}
return <div>...</div>;
};
Hooks use the same section ordering but export function syntax:
import * as React from "react";
export type UseSomethingResult = {
value: string;
update: (v: string) => void;
};
export function useSomething(input: string): UseSomethingResult {
//* State
const [value, setValue] = React.useState(input);
//* Refs
const prevRef = React.useRef(input);
//* Variables
const isChanged = value !== prevRef.current;
//* Handlers
const update = React.useCallback((v: string) => setValue(v), []);
//* Effects
React.useEffect(() => {
prevRef.current = input;
}, [input]);
return { value, update };
}
Headers use the //* comment style (double-slash asterisk, for better-comments extension):
//* State
//* Refs
//* Variables
//* Handlers
//* Effects
return. No section header for these.| What | Section |
|---|---|
useTranslation(), useContext() | State |
React.useState() | State |
| react-query / data fetching hooks | State |
React.useRef() | Refs |
| Derived/computed values | Variables |
React.useMemo() | Variables |
| Custom hooks returning non-stateful values | Variables |
| Event handlers, callbacks | Handlers |
React.useCallback() | Handlers |
React.useEffect() | Effects |
| Custom effect hooks | Effects |
Within State, order from most external (data fetching, context) to most internal (local UI state like modal open/close).
Always destructure props on the first line of the component body:
export const MyComponent: React.FC<MyComponentProps> = (props) => {
const { someProp, otherProp } = props;
// ...
};
.tsx and .ts files that were created or modified.