agent-rules-kit
Version:
CLI tool to bootstrap AI agent rules for multiple IDEs and frameworks. Generates optimized rules for Cursor, VS Code, Claude, and 6+ other AI coding assistants.
687 lines (597 loc) • 18 kB
Markdown
---
description: Naming conventions for React applications
globs: <root>/src/**/*.ts,<root>/src/**/*.tsx,<root>/src/**/*.js,<root>/src/**/*.jsx,<root>/components/**/*.ts,<root>/components/**/*.tsx,<root>/components/**/*.js,<root>/components/**/*.jsx
alwaysApply: false
---
This guide outlines the recommended naming conventions for React applications in {projectPath}, covering components, hooks, utilities, and project structure.
```
src/
├── components/
│ ├── common/
│ │ ├── Button/
│ │ │ ├── Button.tsx
│ │ │ ├── Button.test.tsx
│ │ │ ├── Button.stories.tsx
│ │ │ └── index.ts
│ │ └── Modal/
│ │ ├── Modal.tsx
│ │ ├── Modal.test.tsx
│ │ └── index.ts
│ └── features/
│ ├── UserProfile/
│ │ ├── UserProfile.tsx
│ │ ├── UserProfile.test.tsx
│ │ └── index.ts
│ └── ProductCard/
│ ├── ProductCard.tsx
│ ├── ProductCard.test.tsx
│ └── index.ts
├── hooks/
│ ├── useAuth.ts
│ ├── useLocalStorage.ts
│ └── useApi.ts
├── utils/
│ ├── formatters.ts
│ ├── validators.ts
│ └── constants.ts
├── services/
│ ├── api.ts
│ ├── authService.ts
│ └── userService.ts
├── types/
│ ├── user.ts
│ ├── product.ts
│ └── api.ts
├── contexts/
│ ├── AuthContext.tsx
│ ├── ThemeContext.tsx
│ └── UserContext.tsx
├── pages/
│ ├── HomePage.tsx
│ ├── AboutPage.tsx
│ └── UserProfilePage.tsx
└── assets/
├── images/
├── icons/
└── styles/
```
```typescript
// ✅ Good: PascalCase for component files
UserProfile.tsx
ProductCard.tsx
NavigationMenu.tsx
SearchInput.tsx
LoadingSpinner.tsx
// ✅ Good: Use descriptive, specific names
UserProfileCard.tsx // Not just Card.tsx
ProductSearchForm.tsx // Not just Form.tsx
DashboardNavigationMenu.tsx // Not just Menu.tsx
// ❌ Avoid: Generic or unclear names
Component.tsx
Utils.tsx
Helper.tsx
Thing.tsx
```
```typescript
// ✅ Good: Function component names match file names
// File: UserProfile.tsx
export default function UserProfile({ userId }: { userId: string }) {
return <div>User Profile for {userId}</div>;
}
// ✅ Good: Named exports for reusable components
// File: Button.tsx
export function Button({
variant = 'primary',
size = 'medium',
children,
...props
}: ButtonProps) {
return (
<button
className={`btn btn-${variant} btn-${size}`}
{...props}
>
{children}
</button>
);
}
// ✅ Good: Multiple related components in one file
// File: Card.tsx
export function Card({ children, className, ...props }: CardProps) {
return (
<div className={`card ${className}`} {...props}>
{children}
</div>
);
}
export function CardHeader({ children, className }: CardHeaderProps) {
return (
<div className={`card-header ${className}`}>
{children}
</div>
);
}
export function CardBody({ children, className }: CardBodyProps) {
return (
<div className={`card-body ${className}`}>
{children}
</div>
);
}
export function CardFooter({ children, className }: CardFooterProps) {
return (
<div className={`card-footer ${className}`}>
{children}
</div>
);
}
```
```typescript
// ✅ Good: Props interface naming
interface ButtonProps {
variant?: 'primary' | 'secondary' | 'danger' | 'success';
size?: 'small' | 'medium' | 'large';
disabled?: boolean;
loading?: boolean;
onClick?: (event: React.MouseEvent<HTMLButtonElement>) => void;
children: React.ReactNode;
className?: string;
}
interface UserProfileProps {
user: User;
isEditable?: boolean;
onEdit?: (user: User) => void;
onDelete?: (userId: string) => void;
}
interface ModalProps {
isOpen: boolean;
onClose: () => void;
title?: string;
size?: 'small' | 'medium' | 'large' | 'fullscreen';
children: React.ReactNode;
closeOnOverlayClick?: boolean;
closeOnEscapeKey?: boolean;
}
// ✅ Good: Generic props for flexible components
interface ListProps<T> {
items: T[];
renderItem: (item: T, index: number) => React.ReactNode;
keyExtractor: (item: T) => string | number;
loading?: boolean;
emptyMessage?: string;
className?: string;
}
```
```typescript
// ✅ Good: Always start with 'use' and be descriptive
useAuth.ts // Authentication logic
useLocalStorage.ts // Local storage management
useApi.ts // API calls
useFetch.ts // Data fetching
useDebounce.ts // Debounced values
useToggle.ts // Toggle state
useForm.ts // Form management
useClickOutside.ts // Click outside detection
useWindowSize.ts // Window size tracking
usePrevious.ts // Previous value tracking
// ✅ Good: Feature-specific hooks
useUserProfile.ts // User profile management
useShoppingCart.ts // Shopping cart logic
useProductSearch.ts // Product search functionality
useNotifications.ts // Notification system
// Hook implementation examples:
// File: useAuth.ts
export function useAuth() {
const [user, setUser] = useState<User | null>(null);
const [isLoading, setIsLoading] = useState(true);
const [error, setError] = useState<string | null>(null);
const login = async (credentials: LoginCredentials) => {
try {
setIsLoading(true);
setError(null);
const response = await authService.login(credentials);
setUser(response.user);
} catch (err) {
setError(err instanceof Error ? err.message : 'Login failed');
} finally {
setIsLoading(false);
}
};
const logout = async () => {
try {
await authService.logout();
setUser(null);
} catch (err) {
console.error('Logout error:', err);
}
};
return {
user,
isAuthenticated: !!user,
isLoading,
error,
login,
logout,
};
}
// File: useLocalStorage.ts
export function useLocalStorage<T>(
key: string,
initialValue: T
): [T, (value: T | ((val: T) => T)) => void] {
const [storedValue, setStoredValue] = useState<T>(() => {
try {
const item = window.localStorage.getItem(key);
return item ? JSON.parse(item) : initialValue;
} catch (error) {
console.error(`Error reading localStorage key "${key}":`, error);
return initialValue;
}
});
const setValue = (value: T | ((val: T) => T)) => {
try {
const valueToStore = value instanceof Function ? value(storedValue) : value;
setStoredValue(valueToStore);
window.localStorage.setItem(key, JSON.stringify(valueToStore));
} catch (error) {
console.error(`Error setting localStorage key "${key}":`, error);
}
};
return [storedValue, setValue];
}
```
```typescript
// ✅ Good: Event handler naming patterns
interface UserFormProps {
user?: User;
onSubmit: (userData: CreateUserData) => void;
onCancel: () => void;
onFieldChange?: (field: string, value: any) => void;
}
function UserForm({ user, onSubmit, onCancel, onFieldChange }: UserFormProps) {
// ✅ Good: Internal event handler naming
const handleSubmit = (event: React.FormEvent) => {
event.preventDefault();
const formData = new FormData(event.currentTarget as HTMLFormElement);
const userData = Object.fromEntries(formData.entries()) as CreateUserData;
onSubmit(userData);
};
const handleInputChange = (event: React.ChangeEvent<HTMLInputElement>) => {
const { name, value } = event.target;
onFieldChange?.(name, value);
};
const handleCancelClick = () => {
onCancel();
};
return (
<form onSubmit={handleSubmit}>
<input
name="name"
onChange={handleInputChange}
defaultValue={user?.name}
/>
<input
name="email"
type="email"
onChange={handleInputChange}
defaultValue={user?.email}
/>
<button type="submit">Save</button>
<button type="button" onClick={handleCancelClick}>Cancel</button>
</form>
);
}
// ✅ Good: Event handler naming in parent components
function UserManagement() {
const [users, setUsers] = useState<User[]>([]);
const [selectedUser, setSelectedUser] = useState<User | null>(null);
const handleUserCreate = async (userData: CreateUserData) => {
try {
const newUser = await userService.create(userData);
setUsers(prev => [...prev, newUser]);
} catch (error) {
console.error('Failed to create user:', error);
}
};
const handleUserEdit = (user: User) => {
setSelectedUser(user);
};
const handleUserDelete = async (userId: string) => {
try {
await userService.delete(userId);
setUsers(prev => prev.filter(user => user.id !== userId));
} catch (error) {
console.error('Failed to delete user:', error);
}
};
return (
<div>
<UserForm
user={selectedUser}
onSubmit={handleUserCreate}
onCancel={() => setSelectedUser(null)}
/>
<UserList
users={users}
onEdit={handleUserEdit}
onDelete={handleUserDelete}
/>
</div>
);
}
```
```typescript
// ✅ Good: Descriptive state variable names
function ProductSearch() {
const [searchQuery, setSearchQuery] = useState('');
const [searchResults, setSearchResults] = useState<Product[]>([]);
const [isSearching, setIsSearching] = useState(false);
const [searchError, setSearchError] = useState<string | null>(null);
const [selectedCategory, setSelectedCategory] = useState<string>('all');
const [sortOrder, setSortOrder] = useState<'asc' | 'desc'>('asc');
const [currentPage, setCurrentPage] = useState(1);
const [itemsPerPage, setItemsPerPage] = useState(20);
// ✅ Good: Boolean state naming
const [isModalOpen, setIsModalOpen] = useState(false);
const [isLoading, setIsLoading] = useState(false);
const [hasError, setHasError] = useState(false);
const [canEdit, setCanEdit] = useState(false);
const [shouldAutoSave, setShouldAutoSave] = useState(true);
// ✅ Good: Object state naming
const [userPreferences, setUserPreferences] = useState<UserPreferences>({
theme: 'light',
language: 'en',
notifications: true,
});
const [formData, setFormData] = useState<FormData>({
name: '',
email: '',
phone: '',
});
return (
// Component JSX
);
}
```
```typescript
// ✅ Good: Clear utility function names
// File: utils/formatters.ts
export function formatCurrency(amount: number, currency = 'USD'): string {
return new Intl.NumberFormat('en-US', {
style: 'currency',
currency,
}).format(amount);
}
export function formatDate(date: Date, format: 'short' | 'long' = 'short'): string {
return new Intl.DateTimeFormat('en-US', {
dateStyle: format === 'short' ? 'short' : 'full',
}).format(date);
}
export function formatPhoneNumber(phone: string): string {
const cleaned = phone.replace(/\D/g, '');
const match = cleaned.match(/^(\d{3})(\d{3})(\d{4})$/);
return match ? `(${match[1]}) ${match[2]}-${match[3]}` : phone;
}
// File: utils/validators.ts
export function isValidEmail(email: string): boolean {
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
return emailRegex.test(email);
}
export function isValidPhoneNumber(phone: string): boolean {
const phoneRegex = /^\+?[\d\s-()]+$/;
return phoneRegex.test(phone);
}
export function isStrongPassword(password: string): boolean {
const minLength = 8;
const hasUpperCase = /[A-Z]/.test(password);
const hasLowerCase = /[a-z]/.test(password);
const hasNumbers = /\d/.test(password);
const hasSpecialChar = /[!@
return password.length >= minLength && hasUpperCase && hasLowerCase && hasNumbers && hasSpecialChar;
}
// File: utils/array-utils.ts
export function groupBy<T, K extends keyof any>(
array: T[],
getKey: (item: T) => K
): Record<K, T[]> {
return array.reduce((groups, item) => {
const key = getKey(item);
if (!groups[key]) {
groups[key] = [];
}
groups[key].push(item);
return groups;
}, {} as Record<K, T[]>);
}
export function sortBy<T>(
array: T[],
getProperty: (item: T) => string | number,
order: 'asc' | 'desc' = 'asc'
): T[] {
return [...array].sort((a, b) => {
const aVal = getProperty(a);
const bVal = getProperty(b);
if (order === 'asc') {
return aVal < bVal ? -1 : aVal > bVal ? 1 : 0;
} else {
return aVal > bVal ? -1 : aVal < bVal ? 1 : 0;
}
});
}
```
```typescript
// ✅ Good: Clear type and interface names
// File: types/user.ts
export interface User {
id: string;
name: string;
email: string;
avatar?: string;
createdAt: Date;
updatedAt: Date;
}
export interface UserProfile extends User {
bio?: string;
website?: string;
location?: string;
preferences: UserPreferences;
}
export interface UserPreferences {
theme: 'light' | 'dark';
language: string;
notifications: NotificationSettings;
privacy: PrivacySettings;
}
export interface NotificationSettings {
email: boolean;
push: boolean;
sms: boolean;
}
export interface PrivacySettings {
profileVisibility: 'public' | 'private' | 'friends';
showEmail: boolean;
showLocation: boolean;
}
// ✅ Good: API-related types
export interface ApiResponse<T> {
data: T;
message: string;
success: boolean;
timestamp: string;
}
export interface PaginatedResponse<T> extends ApiResponse<T[]> {
pagination: {
page: number;
limit: number;
total: number;
totalPages: number;
};
}
export interface ApiError {
message: string;
code: string;
details?: Record<string, any>;
}
// ✅ Good: Form-related types
export type CreateUserData = Omit<User, 'id' | 'createdAt' | 'updatedAt'>;
export type UpdateUserData = Partial<CreateUserData>;
export type LoginCredentials = Pick<User, 'email'> & { password: string };
// ✅ Good: Union types
export type Theme = 'light' | 'dark';
export type UserRole = 'admin' | 'user' | 'moderator';
export type HttpMethod = 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH';
export type LoadingState = 'idle' | 'loading' | 'success' | 'error';
```
```typescript
// ✅ Good: Context naming
// File: contexts/AuthContext.tsx
interface AuthContextType {
user: User | null;
isAuthenticated: boolean;
isLoading: boolean;
login: (credentials: LoginCredentials) => Promise<void>;
logout: () => Promise<void>;
updateUser: (userData: UpdateUserData) => Promise<void>;
}
const AuthContext = createContext<AuthContextType | undefined>(undefined);
export function AuthProvider({ children }: { children: React.ReactNode }) {
// Implementation
return (
<AuthContext.Provider value={authValue}>
{children}
</AuthContext.Provider>
);
}
export function useAuth() {
const context = useContext(AuthContext);
if (context === undefined) {
throw new Error('useAuth must be used within an AuthProvider');
}
return context;
}
// File: contexts/ThemeContext.tsx
interface ThemeContextType {
theme: Theme;
toggleTheme: () => void;
setTheme: (theme: Theme) => void;
colors: ColorPalette;
}
const ThemeContext = createContext<ThemeContextType | undefined>(undefined);
export function ThemeProvider({ children }: { children: React.ReactNode }) {
// Implementation
return (
<ThemeContext.Provider value={themeValue}>
{children}
</ThemeContext.Provider>
);
}
export function useTheme() {
const context = useContext(ThemeContext);
if (context === undefined) {
throw new Error('useTheme must be used within a ThemeProvider');
}
return context;
}
```
```typescript
// ✅ Good: Constants naming
// File: utils/constants.ts
export const API_ENDPOINTS = {
USERS: '/api/users',
PRODUCTS: '/api/products',
ORDERS: '/api/orders',
AUTH: {
LOGIN: '/api/auth/login',
LOGOUT: '/api/auth/logout',
REGISTER: '/api/auth/register',
REFRESH: '/api/auth/refresh',
},
} as const;
export const HTTP_STATUS_CODES = {
OK: 200,
CREATED: 201,
NO_CONTENT: 204,
BAD_REQUEST: 400,
UNAUTHORIZED: 401,
FORBIDDEN: 403,
NOT_FOUND: 404,
CONFLICT: 409,
INTERNAL_SERVER_ERROR: 500,
} as const;
export const VALIDATION_RULES = {
EMAIL_REGEX: /^[^\s@]+@[^\s@]+\.[^\s@]+$/,
PHONE_REGEX: /^\+?[\d\s-()]+$/,
PASSWORD_MIN_LENGTH: 8,
USERNAME_MIN_LENGTH: 3,
USERNAME_MAX_LENGTH: 20,
} as const;
export const UI_CONSTANTS = {
DEBOUNCE_DELAY: 300,
ANIMATION_DURATION: 200,
TOAST_DURATION: 5000,
MODAL_Z_INDEX: 1000,
TOOLTIP_Z_INDEX: 1100,
} as const;
export const LOCAL_STORAGE_KEYS = {
AUTH_TOKEN: 'auth_token',
USER_PREFERENCES: 'user_preferences',
THEME: 'theme',
LANGUAGE: 'language',
} as const;
```
These naming conventions help maintain consistency and readability across your React application, making it easier for team members to understand and contribute to the codebase.