CODE_STYLE_GUIDE.md
This project uses Next.js 15 with TypeScript and Tailwind CSS. We follow modern React patterns with functional components and hooks.
src/components/*.tsx)Imports
import type)Type Definitions
Reusable Components
Main Component
SkillsSection in skills-section.tsx)Export Statement
export default MainComponent; or named exportsinterface for component props with clear naming:interface ButtonProps {
variant?: 'primary' | 'secondary';
size?: 'sm' | 'md' | 'lg';
onClick?: () => void;
children: React.ReactNode;
}
any types - use unknown or proper typesas const for literal types// Good
const themes = ['light', 'dark'] as const;
type Theme = (typeof themes)[number];
// Bad
const themes: any = ['light', 'dark'];
React.forwardRef when ref forwarding is needed// 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;
useCallback and useMemo for performance optimizationconst MyComponent = () => {
const [state, setState] = useState();
const { data, loading } = useCustomHook();
const memoizedValue = useMemo(() => expensiveCalculation(), [dependency]);
// component JSX
};
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>
kebab-case.tsx (e.g., skills-section.tsx)use-hook-name.ts (e.g., use-local-storage.ts)kebab-case.ts (e.g., profile-types.ts)kebab-case.ts (e.g., markdown-generator.ts)if, for, while, switch( and before closing )// 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
}
};
// 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"/>
camelCase for prop namesPascalCase if prop value is a React componentinterface FormProps {
initialValues?: Record<string, unknown>;
onSubmit?: (data: FormData) => void;
isLoading?: boolean;
ErrorComponent?: React.ComponentType;
}
handle or onconst handleInputChange = (event: React.ChangeEvent<HTMLInputElement>) => {
setValue(event.target.value);
};
const handleSubmit = async (event: React.FormEvent) => {
event.preventDefault();
// submit logic
};
alt prop to img tagsoutline: none without alternative focus stylesReact.memo for expensive componentsuseCallback and useMemo appropriately// Good
const MemoizedComponent = React.memo(({ data }: Props) => {
const processedData = useMemo(() => processData(data), [data]);
const handleClick = useCallback(() => {
// click logic
}, []);
return <div></div>;
});
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';
This guide is based on:
For questions about code style, please discuss with maintainers on our Discord community.