1/23/2025
Comprehensive guidelines and best practices for developing React applications using TypeScript, Next.js, and Node.js. Includes coding standards, preferred libraries, file structure, performance optimization, testing requirements, and error handling.
# React (TypeScript, Next.js, Node.js)
# React (TypeScript, Next.js, Node.js) - Best Practices and Guidelines
## Key Principles
- **Write concise, technical responses with accurate TypeScript examples.**
- **Use functional, declarative programming. Avoid classes.**
- **Prefer iteration and modularization over duplication.**
- **Use descriptive variable names with auxiliary verbs (e.g., `isLoading`).**
- **Use lowercase with dashes for directories (e.g., `components/auth-wizard`).**
- **Favor named exports for components.**
- **Use the Receive an Object, Return an Object (RORO) pattern.**
## JavaScript/TypeScript
- **Use `function` keyword for pure functions. Omit semicolons.**
- **Use TypeScript for all code. Prefer interfaces over types. Avoid enums, use maps.**
- **File structure:**
- Exported component
- Subcomponents
- Helpers
- Static content
- Types
- **Avoid unnecessary curly braces in conditional statements.**
- **For single-line statements in conditionals, omit curly braces.**
- **Use concise, one-line syntax for simple conditional statements (e.g., `if (condition) doSomething()`).**
- **Prioritize error handling and edge cases:**
- Handle errors and edge cases at the beginning of functions.
- Use early returns for error conditions to avoid deeply nested if statements.
- Place the happy path last in the function for improved readability.
- Avoid unnecessary else statements; use if-return pattern instead.
- Use guard clauses to handle preconditions and invalid states early.
- Implement proper error logging and user-friendly error messages.
- Consider using custom error types or error factories for consistent error handling.
## Dependencies
- **Next.js 14 App Router**
- **Wagmi v2**
- **Viem v2**
## React/Next.js
- **Use functional components and TypeScript interfaces.**
- **Use declarative JSX.**
- **Use `function`, not `const`, for components.**
- **Use Shadcn UI, Radix, and Tailwind Aria for components and styling.**
- **Implement responsive design with Tailwind CSS.**
- **Use mobile-first approach for responsive design.**
- **Place static content and interfaces at file end.**
- **Use content variables for static content outside render functions.**
- **Minimize `use client`, `useEffect`, and `setState`. Favor RSC.**
- **Use Zod for form validation.**
- **Wrap client components in Suspense with fallback.**
- **Use dynamic loading for non-critical components.**
- **Optimize images:**
- WebP format
- Size data
- Lazy loading
- **Model expected errors as return values:**
- Avoid using `try/catch` for expected errors in Server Actions.
- Use `useActionState` to manage these errors and return them to the client.
- **Use error boundaries for unexpected errors:**
- Implement error boundaries using `error.tsx` and `global-error.tsx` files to handle unexpected errors and provide a fallback UI.
- **Use `useActionState` with `react-hook-form` for form validation.**
- **Code in `services/` dir always throw user-friendly errors that tanStackQuery can catch and show to the user.**
- **Use `next-safe-action` for all server actions:**
- Implement type-safe server actions with proper validation.
- Utilize the `action` function from `next-safe-action` for creating actions.
- Define input schemas using Zod for robust type checking and validation.
- Handle errors gracefully and return appropriate responses.
- Use `import type { ActionResponse } from '@/types/actions'`
- Ensure all server actions return the `ActionResponse` type
- Implement consistent error handling and success responses using `ActionResponse`
- Example:
```typescript
'use server'
import { createSafeActionClient } from 'next-safe-action'
import { z } from 'zod'
import type { ActionResponse } from '@/app/actions/actions'
const schema = z.object({
value: z.string()
})
export const someAction = createSafeActionClient()
.schema(schema)
.action(async (input): Promise => {
try {
// Action logic here
return { success: true, data: /* result */ }
} catch (error) {
return { success: false, error: error instanceof AppError ? error : appErrors.UNEXPECTED_ERROR, }
}
})
```
## Key Conventions
1. **Rely on Next.js App Router for state changes.**
2. **Prioritize Web Vitals (LCP, CLS, FID).**
3. **Minimize `use client` usage:**
- Prefer server components and Next.js SSR features.
- Use `use client` only for Web API access in small components.
- Avoid using `use client` for data fetching or state management.
Refer to Next.js documentation for Data Fetching, Rendering, and Routing best practices.