Frontend Conventions
Component Development
Component Structure
- Use functional components with hooks
- PascalCase file naming (
EventCard.jsx
) - Default exports for components
- Destructure props with defaults
// Component example
const EventCard = ({ title, className = '' }) => (
<div className={`bg-white rounded-lg shadow p-4 ${className}`}>
<h3 className='text-lg font-semibold'>{title}</h3>
</div>
)
export default EventCard
Adding New Components
// src/components/MyComponent.jsx
const MyComponent = ({ title, children }) => {
return (
<div className='p-4 bg-white rounded-lg shadow'>
<h2 className='text-xl font-bold'>{title}</h2>
{children}
</div>
)
}
export default MyComponent
// src/components/index.js
export { default as MyComponent } from './MyComponent'
State Management
Local State
- Use
useState
for component-specific state - Use
useReducer
for complex state logic
Global State
- React Context: For app-wide state (authentication, theme)
- Custom Hooks: For reusable logic (API calls, form handling, storage)
// Using Context for global state
import { useAuth } from '../context'
const LoginPage = () => {
const { login, loading, error } = useAuth()
const handleSubmit = async e => {
e.preventDefault()
await login({ email, password })
}
return <form onSubmit={handleSubmit}>{/* form fields */}</form>
}
// Using Custom Hooks for reusable logic
import { useApi, useLocalStorage } from '../hooks'
const EventsPage = () => {
const { data: events, loading, error } = useApi('/api/events')
const [favorites, setFavorites] = useLocalStorage('favorites', [])
return <div>{/* render events */}</div>
}
API Integration
Service Layer
- Centralized API calls in
services/
- Consistent error handling
- JWT token authentication
// src/services/eventService.js
export const getEvents = async () => {
const response = await fetch('/api/events')
return response.json()
}
export const createEvent = async eventData => {
const response = await fetch('/api/events', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(eventData),
})
return response.json()
}
Error Handling
- Backend returns errors in format:
{ errors: { field: ["message"] } }
- Display field-specific validation errors
Styling
Tailwind CSS
- Use utility classes for styling
- Component composition approach
- Mobile-first responsive design
// Utility classes example
<div className='flex items-center justify-between p-4 bg-blue-500 text-white rounded-lg'>
<h1 className='text-2xl font-bold'>Title</h1>
<button className='px-4 py-2 bg-white text-blue-500 rounded hover:bg-gray-100'>
Click me
</button>
</div>
Custom Styles
- Keep custom CSS minimal in
src/styles/
- Prefer Tailwind utility classes over custom CSS
Code Quality
ESLint Configuration
- React best practices
- Prettier integration
- Auto-fix on save
Prettier Configuration
- Single quotes
- No semicolons
- 2-space indentation
- Trailing commas where valid
File Organization
Barrel Exports
Use index.js
files for clean imports:
// ✅ Good - barrel exports
import { EventCard, EventForm, Button } from '../components'
import { AuthProvider, useAuth } from '../context'
// ❌ Avoid - individual imports
import EventCard from '../components/EventCard'
import EventForm from '../components/EventForm'
Best Practices
- Keep components small and focused
- Extract reusable logic into custom hooks
- Use descriptive prop names
- Add JSDoc comments for complex props
/**
* Button component with multiple variants
* @param {string} variant - Button style variant (primary, secondary, danger)
* @param {string} size - Button size (sm, md, lg)
* @param {boolean} disabled - Whether button is disabled
* @param {ReactNode} children - Button content
*/
const Button = ({ variant = 'primary', size = 'md', disabled, children }) => {
// component implementation
}