7.4 KiB
7.4 KiB
Coding Style Guide
Project Architecture
This project uses Next.js 15 with TypeScript and Tailwind CSS. We follow modern React patterns with functional components and hooks.
File Layout (src/components/*.tsx)
-
Imports
- React imports first
- Third-party library imports
- Internal component imports
- Type imports (using
import type)
-
Type Definitions
- Interface definitions for props
- Type aliases if needed
-
Reusable Components
- Smaller components needed for the main component
- Place above the main component, not inside it
-
Main Component
- Main exported component (e.g.,
SkillsSectioninskills-section.tsx)
- Main exported component (e.g.,
-
Export Statement
export default MainComponent;or named exports
TypeScript Guidelines
Component Props
- Use
interfacefor component props with clear naming:
interface ButtonProps {
variant?: 'primary' | 'secondary';
size?: 'sm' | 'md' | 'lg';
onClick?: () => void;
children: React.ReactNode;
}
Type Safety
- Avoid
anytypes - useunknownor proper types - Use strict TypeScript configuration
- Leverage type inference where possible
- Use
as constfor literal types
// Good
const themes = ['light', 'dark'] as const;
type Theme = (typeof themes)[number];
// Bad
const themes: any = ['light', 'dark'];
Component Patterns
Functional Components
- Use ES6 arrow functions for all components
- Use
React.forwardRefwhen ref forwarding is needed - Prefer named exports for reusable components
// Good
export const Button = ({ variant = 'primary', ...props }: ButtonProps) => {
return <button className={`btn btn-${variant}`} {...props} />;
};
// Also good for main components
const SkillsSection = ({ skills, onSkillToggle }: SkillsSectionProps) => {
// component logic
};
export default SkillsSection;
Hooks Usage
- Use custom hooks for reusable logic
- Keep hooks at the top of components
- Use
useCallbackanduseMemofor performance optimization
const MyComponent = () => {
const [state, setState] = useState();
const { data, loading } = useCustomHook();
const memoizedValue = useMemo(() => expensiveCalculation(), [dependency]);
// component JSX
};
Styling with Tailwind CSS
Class Organization
- Use Tailwind utility classes
- Group related classes together
- Use responsive prefixes (
sm:,md:,lg:)
// Good
<div className="flex flex-col gap-4 rounded-lg border p-4 sm:flex-row sm:gap-6">
<button className="bg-primary text-primary-foreground hover:bg-primary/90 rounded px-4 py-2 transition-colors">
Click me
</button>
</div>
CSS Variables
- Use CSS custom properties for theming
- Follow the design system color palette
- Prefer Tailwind classes over custom CSS
File Naming Conventions
- Components:
kebab-case.tsx(e.g.,skills-section.tsx) - Hooks:
use-hook-name.ts(e.g.,use-local-storage.ts) - Types:
kebab-case.ts(e.g.,profile-types.ts) - Utils:
kebab-case.ts(e.g.,markdown-generator.ts)
Spacing & Formatting
JavaScript/TypeScript
- Use 2 spaces for indentation (not 4)
- Use spaces after
if,for,while,switch - No spaces after opening
(and before closing) - Use spaces around destructuring
// Good
const { name, email } = user;
const handleClick = ({ target }: MouseEvent) => {
if (target instanceof HTMLElement) {
// logic
}
};
// Bad
const { name, email } = user;
const handleClick = ({ target }: MouseEvent) => {
if (target instanceof HTMLElement) {
// logic
}
};
JSX Formatting
- Space before self-closing tag slash
- No spaces in JSX curly braces
- Use new lines for multiple props
// Good
<Input
value={value}
onChange={handleChange}
placeholder="Enter text"
/>
<Icon className="h-4 w-4" />
// Bad
<Input value={value} onChange={handleChange} placeholder="Enter text"/>
<Icon className="h-4 w-4"/>
Props & Event Handling
Prop Naming
- Use
camelCasefor prop names - Use
PascalCaseif prop value is a React component - Use descriptive names with proper prefixes
interface FormProps {
initialValues?: Record<string, unknown>;
onSubmit?: (data: FormData) => void;
isLoading?: boolean;
ErrorComponent?: React.ComponentType;
}
Event Handlers
- Prefix with
handleoron - Use descriptive names
- Type event handlers properly
const handleInputChange = (event: React.ChangeEvent<HTMLInputElement>) => {
setValue(event.target.value);
};
const handleSubmit = async (event: React.FormEvent) => {
event.preventDefault();
// submit logic
};
Best Practices
Code Quality
- Always add semicolons
- Use meaningful variable and function names
- Keep components small and focused (< 200 lines)
- Extract complex logic into custom hooks
- Use TypeScript strict mode
Accessibility
- Always add
altprop toimgtags - Use semantic HTML elements
- Add proper ARIA attributes
- Don't use
outline: nonewithout alternative focus styles - Use proper heading hierarchy
Performance
- Use
React.memofor expensive components - Implement proper dependency arrays for hooks
- Avoid inline objects and functions in JSX
- Use
useCallbackanduseMemoappropriately
// Good
const MemoizedComponent = React.memo(({ data }: Props) => {
const processedData = useMemo(() => processData(data), [data]);
const handleClick = useCallback(() => {
// click logic
}, []);
return <div>{/* JSX */}</div>;
});
Error Handling
- Use error boundaries for component errors
- Handle async operations properly
- Provide fallback UI for loading states
const AsyncComponent = () => {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
fetchData()
.then(setData)
.catch(setError)
.finally(() => setLoading(false));
}, []);
if (loading) return <LoadingSpinner />;
if (error) return <ErrorMessage error={error} />;
if (!data) return <EmptyState />;
return <DataDisplay data={data} />;
};
Dependencies & Imports
Import Organization
// 1. React imports
import { useState, useEffect, useCallback } from 'react';
import type { ReactNode } from 'react';
// 2. Third-party libraries
import { motion } from 'framer-motion';
import { ChevronDown } from 'lucide-react';
// 3. Internal imports
import { Button } from '@/components/ui/button';
import { useLocalStorage } from '@/hooks/use-local-storage';
import type { ProfileData } from '@/types/profile';
Package Management
- Prefer stable, well-maintained packages
- Keep dependencies up to date
- Use exact versions for critical dependencies
- Document any custom modifications
Testing Considerations
- Write testable components with clear props
- Avoid complex side effects in components
- Use dependency injection for external services
- Keep business logic separate from UI logic
Further Reading
This guide is based on:
For questions about code style, please discuss with maintainers on our Discord community.