This project uses Next.js 15 with TypeScript and Tailwind CSS. We follow modern React patterns with functional components and hooks.
-
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
- Use
interfacefor component props with clear naming:
interface ButtonProps {
variant?: 'primary' | 'secondary';
size?: 'sm' | 'md' | 'lg';
onClick?: () => void;
children: React.ReactNode;
}- 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'];- 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;- 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
};- 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>- Use CSS custom properties for theming
- Follow the design system color palette
- Prefer Tailwind classes over custom CSS
- 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)
- 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
}
};- 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"/>- 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;
}- 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
};- 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
- Always add
altprop toimgtags - Use semantic HTML elements
- Add proper ARIA attributes
- Don't use
outline: nonewithout alternative focus styles - Use proper heading hierarchy
- 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>;
});- 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} />;
};// 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';- Prefer stable, well-maintained packages
- Keep dependencies up to date
- Use exact versions for critical dependencies
- Document any custom modifications
- 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
This guide is based on:
For questions about code style, please discuss with maintainers on our Discord community.