react-design-checker
v2.0.0
Published
CLI tool to check React/React Native projects against design and code quality rules
Downloads
212
Maintainers
Readme
React Design Checker
A powerful CLI tool to enforce best practices, architecture patterns, and code quality standards in React and React Native projects.
Features • Installation • Configuration • Rules • Examples
📋 Overview
React Design Checker is a comprehensive linting and validation tool that goes beyond syntax checking. It enforces:
- Architectural patterns (Atomic Design, MVC, Feature-based)
- Performance optimizations (memo usage, inline function detection)
- Accessibility standards (ARIA labels, alt text)
- Code quality metrics (file size, prop count, complexity)
- React best practices (hooks, lifecycle methods, TypeScript)
- Platform-specific rules (React Native optimizations)
✨ Features
🏗️ Architecture Enforcement
- Dynamic folder structure validation
- Multiple architecture pattern support (Atomic Design, MVC, Feature-based)
- Custom naming conventions
⚡ Performance Optimization
- Detects inline functions and objects in JSX
- Validates React.memo usage for optimization
- Identifies unnecessary re-renders
- File size monitoring
♿ Accessibility Compliance
- Checks for missing ARIA labels
- Validates image alt text
- Ensures keyboard navigation support
🔍 Code Quality
- TypeScript type safety validation
- Prevents console.log in production
- Detects deprecated React methods
- Service/Hook separation enforcement
📱 React Native Support
- Platform-specific optimizations
- ScrollView nesting validation
- Platform API usage verification
🚀 Installation
Quick Start
# Clone the repository
git clone https://github.com/your-username/react-design-checker.git
cd react-design-checker
# Install dependencies
npm install
# or
yarn install
# Build the library
npm run build
# Link globally for local testing
npm linkUse in Your Project
# Navigate to your React/React Native project
cd ../your-react-project
# Link the checker
npm link react-design-checker
# Run the checker
react-design-checkerNPM Installation (Coming Soon)
npm install -g react-design-checker⚙️ Configuration
Create a design.json file in your project root to define your custom architecture and naming conventions.
Configuration Options
| Option | Description | Type | Required |
|--------|-------------|------|----------|
| folderStructure.type | Architecture pattern | atomic | mvc | feature | Yes |
| folderStructure.folders | Required folder paths | string[] | Yes |
| namingConventions.component | Component naming style | PascalCase | kebab-case | Yes |
| namingConventions.hook | Hook naming style | useCamelCase | Yes |
| namingConventions.file | File naming style | kebab-case | PascalCase | No |
Example Configurations
Atomic Design Pattern
{
"folderStructure": {
"type": "atomic",
"folders": [
"src/components/atoms",
"src/components/molecules",
"src/components/organisms",
"src/components/templates",
"src/components/pages",
"src/hooks",
"src/context",
"src/utils"
]
},
"namingConventions": {
"component": "PascalCase",
"hook": "useCamelCase",
"file": "kebab-case"
}
}MVC Pattern
{
"folderStructure": {
"type": "mvc",
"folders": [
"src/models",
"src/views",
"src/controllers",
"src/services",
"src/hooks",
"src/utils"
]
},
"namingConventions": {
"component": "PascalCase",
"hook": "useCamelCase",
"file": "PascalCase"
}
}Feature-Based Architecture
{
"folderStructure": {
"type": "feature",
"folders": [
"src/features/authentication",
"src/features/dashboard",
"src/features/profile",
"src/shared/components",
"src/shared/hooks",
"src/shared/utils"
]
},
"namingConventions": {
"component": "PascalCase",
"hook": "useCamelCase",
"file": "kebab-case"
}
}🚨 Rules and Violations
1️⃣ Folder Structure Validation
What it checks: Verifies that all required folders exist according to your design.json configuration.
Violation Example:
Missing required folder: src/hooksFix:
mkdir -p src/hooks2️⃣ Logic in UI Components
What it checks: Ensures UI components don't contain complex conditional logic in JSX.
❌ Bad:
return (
<View>
{condition && <Text>Hello</Text>}
{items.map(item => item.active && <Item key={item.id} data={item} />)}
</View>
);✅ Good:
const visibleItems = items.filter(item => item.active);
const greeting = condition ? <Text>Hello</Text> : null;
return (
<View>
{greeting}
{visibleItems.map(item => <Item key={item.id} data={item} />)}
</View>
);3️⃣ Naming Conventions
What it checks: Enforces consistent naming across your codebase.
| Type | Convention | Example |
|------|-----------|---------|
| Component Files | PascalCase | Button.tsx, UserProfile.tsx |
| Hooks | useCamelCase | useAuth.ts, useFetchData.ts |
| Utilities | camelCase | formatDate.ts, apiHelpers.ts |
❌ Bad:
button.tsx
myhook.ts
UserProfile_component.jsx✅ Good:
Button.tsx
useMyHook.ts
UserProfile.jsx4️⃣ Console Logs and Debugging
What it checks: Prevents debug statements from reaching production.
❌ Bad:
console.log("User data:", userData);
debugger;
console.error("This shouldn't be here");✅ Good:
// Use proper logging libraries in production
if (__DEV__) {
console.log("User data:", userData);
}5️⃣ Hook and Service Separation
What it checks: Ensures async operations and business logic are separated from UI components.
❌ Bad:
function UserProfile() {
const [user, setUser] = useState(null);
useEffect(() => {
// Direct API call in component
fetch('/api/user')
.then(res => res.json())
.then(data => setUser(data));
}, []);
return <View>{user?.name}</View>;
}✅ Good:
// Custom hook (useUser.ts)
function useUser() {
const [user, setUser] = useState(null);
useEffect(() => {
fetchUser().then(setUser);
}, []);
return user;
}
// Component
function UserProfile() {
const user = useUser();
return <View>{user?.name}</View>;
}6️⃣ TypeScript Type Safety
What it checks: Prevents the use of any type and enforces proper typing.
❌ Bad:
const fetchData = async (): Promise<any> => {
const data: any = await fetch('/api/data');
return data;
};✅ Good:
interface UserData {
id: string;
name: string;
email: string;
}
const fetchData = async (): Promise<UserData> => {
const response = await fetch('/api/data');
const data: UserData = await response.json();
return data;
};7️⃣ File Size Limit
What it checks: Ensures component files stay maintainable (≤ 50KB default).
Violation:
File size 65KB exceeds 50KB limit: Button.tsxFix:
- Split large components into smaller, reusable pieces
- Extract logic into custom hooks
- Move constants and utilities to separate files
Example refactoring:
// Before (large file)
function Dashboard() {
// 500+ lines of code
}
// After (split into modules)
// Dashboard.tsx
import { useHookName } from './useHookName';
import { DashboardHeader } from './DashboardHeader';
import { DashboardContent } from './DashboardContent';
function Dashboard() {
// Orchestration only
}8️⃣ Maximum Props Count
What it checks: Limits component props to 5 for better maintainability.
❌ Bad:
interface ButtonProps {
label: string;
onClick: () => void;
color: string;
size: string;
icon: string;
disabled: boolean;
loading: boolean;
}
const Button = (props: ButtonProps) => { /* ... */ };✅ Good:
interface ButtonProps {
label: string;
onClick: () => void;
variant?: 'primary' | 'secondary';
disabled?: boolean;
icon?: string;
}
// Or use composition
interface ButtonStyleProps {
color: string;
size: string;
icon: string;
}
interface ButtonProps {
label: string;
onClick: () => void;
style?: ButtonStyleProps;
}9️⃣ Deprecated React Methods
What it checks: Detects legacy lifecycle methods and deprecated APIs.
❌ Bad:
class MyComponent extends React.Component {
componentWillMount() {
// Deprecated
}
componentWillReceiveProps(nextProps) {
// Deprecated
}
}✅ Good:
function MyComponent() {
useEffect(() => {
// Use hooks instead
}, []);
return <View />;
}🔟 React.memo Usage
What it checks: Ensures functional components with props are optimized with React.memo.
❌ Bad:
const Button = (props) => {
return <TouchableOpacity onPress={props.onPress}>
<Text>{props.label}</Text>
</TouchableOpacity>;
};✅ Good:
const Button = React.memo((props) => {
return <TouchableOpacity onPress={props.onPress}>
<Text>{props.label}</Text>
</TouchableOpacity>;
});Note: Don't use React.memo when:
- Component always re-renders with parent
- Props change on every render
- Component is lightweight
1️⃣1️⃣ Inline Functions in JSX
What it checks: Detects inline function definitions in JSX props that cause unnecessary re-renders.
❌ Bad:
<Button onPress={() => handleClick()} />
<FlatList renderItem={(item) => <Item data={item} />} />✅ Good:
const handlePress = useCallback(() => {
handleClick();
}, []);
const renderItem = useCallback((item) => {
return <Item data={item} />;
}, []);
<Button onPress={handlePress} />
<FlatList renderItem={renderItem} />1️⃣2️⃣ List Key Rule
What it checks: Ensures each item in .map() has a stable, unique key.
❌ Bad:
{items.map(item => <Item data={item} />)}
{items.map((item, index) => <Item key={index} data={item} />)}✅ Good:
{items.map(item => <Item key={item.id} data={item} />)}
{items.map(item => <Item key={`${item.id}-${item.name}`} data={item} />)}Key best practices:
- Use unique, stable identifiers (IDs from database)
- Avoid array indices as keys (causes issues when list changes)
- Ensure keys are unique among siblings
1️⃣3️⃣ React Native Platform Rules
What it checks: Validates proper Platform API usage and prevents unsafe patterns.
❌ Bad:
// Missing Platform import
if (Platform.OS === 'ios') {
// ...
}
// Unsafe ScrollView nesting
<ScrollView>
<ScrollView>
{/* Nested scrolling causes issues */}
</ScrollView>
</ScrollView>✅ Good:
import { Platform, ScrollView } from 'react-native';
if (Platform.OS === 'ios') {
// ...
}
// Use FlatList or alternative pattern
<ScrollView>
<FlatList
scrollEnabled={false}
data={items}
renderItem={renderItem}
/>
</ScrollView>1️⃣4️⃣ Inline Objects and Arrays in JSX
What it checks: Prevents inline object/array creation in JSX that causes re-renders.
❌ Bad:
<Text style={{ color: "red", fontSize: 16 }}>Hello</Text>
<FlatList data={[1, 2, 3]} />
<Component config={{ api: "url", timeout: 5000 }} />✅ Good:
const textStyle = { color: "red", fontSize: 16 };
const numbers = [1, 2, 3];
const apiConfig = { api: "url", timeout: 5000 };
<Text style={textStyle}>Hello</Text>
<FlatList data={numbers} />
<Component config={apiConfig} />Or use useMemo for dynamic values:
const style = useMemo(() => ({
color: theme.primary,
fontSize: 16
}), [theme.primary]);
<Text style={style}>Hello</Text>1️⃣5️⃣ Accessibility and Localization
What it checks: Ensures proper accessibility labels and alt text for images.
❌ Bad:
<Image source={profilePic} />
<TouchableOpacity onPress={handlePress}>
<Icon name="search" />
</TouchableOpacity>✅ Good:
<Image
source={profilePic}
accessibilityLabel="User profile picture"
alt="Profile photo"
/>
<TouchableOpacity
onPress={handlePress}
accessibilityLabel="Search button"
accessibilityRole="button"
>
<Icon name="search" />
</TouchableOpacity>React Native accessibility props:
accessibilityLabel: Description for screen readersaccessibilityRole: Semantic role (button, link, etc.)accessibilityHint: Additional contextaccessible: Enable/disable accessibility
📊 Examples
Complete Before & After Example
Before: Problematic Component
// button.tsx (bad naming)
import React from 'react';
const Button = (props: any) => {
console.log('Button rendered');
return (
<TouchableOpacity
style={{ padding: 10, backgroundColor: 'blue' }}
onPress={() => props.onClick()}
>
<Text style={{ color: 'white' }}>{props.label}</Text>
</TouchableOpacity>
);
};
export default Button;Issues detected:
- ❌ Incorrect filename (should be
Button.tsx) - ❌ Using
anytype - ❌ Console.log in component
- ❌ Inline styles cause re-renders
- ❌ Inline function in onPress
- ❌ Not using React.memo
- ❌ Missing accessibility labels
After: Fixed Component
// Button.tsx (correct naming)
import React, { useCallback, useMemo } from 'react';
import { TouchableOpacity, Text, StyleSheet } from 'react-native';
interface ButtonProps {
label: string;
onClick: () => void;
variant?: 'primary' | 'secondary';
}
const Button = React.memo<ButtonProps>(({
label,
onClick,
variant = 'primary'
}) => {
const handlePress = useCallback(() => {
onClick();
}, [onClick]);
const buttonStyle = useMemo(() => [
styles.button,
variant === 'secondary' && styles.buttonSecondary
], [variant]);
return (
<TouchableOpacity
style={buttonStyle}
onPress={handlePress}
accessibilityRole="button"
accessibilityLabel={`${label} button`}
>
<Text style={styles.text}>{label}</Text>
</TouchableOpacity>
);
});
const styles = StyleSheet.create({
button: {
padding: 10,
backgroundColor: 'blue',
},
buttonSecondary: {
backgroundColor: 'gray',
},
text: {
color: 'white',
},
});
Button.displayName = 'Button';
export default Button;Improvements:
- ✅ Correct PascalCase filename
- ✅ Proper TypeScript interfaces
- ✅ No console.logs
- ✅ Optimized with React.memo
- ✅ Callbacks memoized with useCallback
- ✅ Styles extracted and memoized
- ✅ Accessibility labels added
- ✅ DisplayName for debugging
🎯 Best Practices Summary
Component Structure
- Keep components under 300 lines
- Use functional components with hooks
- Limit props to 5 or fewer
- Extract reusable logic into custom hooks
Performance
- Wrap components with
React.memowhen appropriate - Use
useCallbackfor function props - Use
useMemofor expensive calculations - Avoid inline objects and functions in JSX
Type Safety
- Always define proper TypeScript types
- Avoid
anytype - Use interfaces for component props
- Define return types for functions
Code Organization
- Follow consistent folder structure
- Use proper naming conventions
- Separate business logic from UI
- Create custom hooks for reusable logic
Accessibility
- Add
accessibilityLabelto all interactive elements - Use semantic
accessibilityRolevalues - Provide
alttext for images - Test with screen readers
🛠️ CLI Options
# Run with default settings
react-design-checker
# Run with specific config file
react-design-checker --config ./custom-design.json
# Run on specific directory
react-design-checker --path ./src/components
# Generate detailed report
react-design-checker --report --output ./report.json
# Fix auto-fixable issues
react-design-checker --fix
# Ignore specific rules
react-design-checker --ignore inline-functions,console-logs📝 Output Examples
Terminal Output
🔍 React Design Checker v1.0.0
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
📁 Checking folder structure...
✅ All required folders exist
🔤 Checking naming conventions...
❌ src/components/button.tsx
→ File should be PascalCase: Button.tsx
⚡ Checking performance optimizations...
❌ src/components/UserList.tsx:45
→ Inline function in JSX prop 'onPress'
❌ src/components/Card.tsx:23
→ Inline style object detected
♿ Checking accessibility...
❌ src/components/Avatar.tsx:12
→ Image missing accessibilityLabel
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
📊 Summary:
Total files checked: 47
Issues found: 12
❌ Errors: 8
⚠️ Warnings: 4
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━🤝 Contributing
We welcome contributions! Please see our Contributing Guide for details.
Development Setup
# Clone the repository
git clone https://github.com/your-username/react-design-checker.git
# Install dependencies
npm install
# Run tests
npm test
# Run in development mode
npm run dev
# Build
npm run build📄 License
This project is licensed under the MIT License - see the LICENSE file for details.
🙏 Acknowledgments
- Inspired by ESLint, Prettier, and the React community's best practices
- Built with TypeScript and Node.js
- Special thanks to all contributors
📧 Support
- Issues: GitHub Issues
- Discussions: GitHub Discussions
- Email: [email protected]
Made with ❤️ by the React Design Checker Team
