Frontend Conventions
React / Next.js
- Use functional components.
- Prefer custom hooks for reusable component logic.
- Use Server Components by default in Next.js App Router.
- Use Client Components only when state, effects, browser APIs, refs, or client-side interactions are required.
- Keep component logic readable; avoid mixing data fetching, transformation, and rendering in one large block.
- Use arrow functions for frontend functions, components, and handlers.
- Do not declare frontend functions with the
functionkeyword. - Keep UI-only state and handlers inside the component file when they do not drive business logic, API calls, data fetching, or shared behavior.
- Place
useEffecthooks after local handlers/functions and immediately before the JSXreturn. - Prefer 1-2
useEffecthooks per component file; use at most 3 by default. - Merge
useEffecthooks when they share the same lifecycle purpose or dependency flow.
Bad:
tsx
function UserTabs() {
useEffect(() => {
syncActiveTab();
}, []);
const handleChangeUserProfileTab = (tab: string) => {
setActiveTab(tab);
};
return <Tabs onChange={handleChangeUserProfileTab} />;
}Good:
tsx
const UserTabs = () => {
const handleChangeTab = (tab: string) => {
setActiveTab(tab);
};
useEffect(() => {
syncActiveTab();
}, []);
return <Tabs onChange={handleChangeTab} />;
};Reusable logic example:
tsx
const useUserOptions = (users: User[]): SelectOption[] =>
users.map((user) => ({
label: user.name,
value: user.id,
}));Types
- Use
typefor component props. - Prefer
interfacefor API/data response shapes; usetypewhen unions, mapped types, utility types, or composition are needed.
Custom Hooks
- Custom hooks must use the
useprefix, such asuseProductPage. - Use custom hooks for reusable component logic or page-level logic that is easier to read outside the component.
- Custom hooks may contain state and handlers, and should return the state and handlers needed by the component.
- Keep purely UI-local state in the component when it is only used for view behavior, such as opening a modal or changing a tab.
- Organize state and handlers inside custom hooks by data flow.
- For example, place preparation steps before validation or submit handlers when that is the flow of the hook.
Example:
tsx
const useProductPage = () => {
const [selectedId, setSelectedId] = useState<string>();
const handleSelect = (id: string) => {
setSelectedId(id);
};
const handleSubmit = () => {
if (!selectedId) return;
submitProduct(selectedId);
};
return {
selectedId,
handleSelect,
handleSubmit,
};
};Components
- Keep components focused on one responsibility.
- Extract a child component when a section grows beyond 80 lines or becomes hard to scan.
- Prefer composition over deeply nested conditional rendering.
- Use stable and unique
keyprops for lists. - Do not create tiny components that are used once and do not improve readability.
- Keep file, component, and function names as simple as possible while preserving meaning.
- Do not use names containing implementation-shell words such as
shell,bash, or similar unless the domain explicitly requires them.
Handlers And Props
- Event handlers defined inside components use the
handleprefix. - Event callback props use the
onprefix. - Keep handler names short and specific.
- Prefer
handleSingleSelectover longer names such ashandleSingleSelectDropdown.
Data Fetching
- Use Server Components for server-rendered data when possible.
- Follow the project's client-side data fetching pattern when Server Components are not appropriate.
- Keep query/cache keys centralized when the project uses a query key factory or key pattern.
- Do not duplicate fetch logic across components.
Styling / Design System
- Strictly follow the existing project design system.
- If no project design system exists, follow the UI library currently used by the project.
- Prefer existing project tokens, token classes, and library-supported classes.
- For Tailwind CSS, prefer canonical/standard classes and avoid arbitrary classes when a token class exists, such as
text-20instead oftext-[20px]. - Do not hard-code colors, spacing, or typography when tokens or components exist.
- Do not change visual design, layout, spacing, colors, or component styling unless the task requires it.
UI States
- User-facing screens and async components should handle loading, empty, error, and success states when relevant.
- Forms should handle loading, validation, disabled, submitting, and error states when relevant.
React Data Transformations
- Do not use
forloops in React component rendering or frontend data transformation code; use array methods to keep data flow declarative. - Do not use nested iteration such as
mapinsidemapby default. - If nested iteration is unavoidable because of the returned data shape, add a short comment explaining why it is necessary.
- Prefer
map,filter,find, andreducefor common data transformations. - Use
flatMap,some, andeveryonly when the context specifically needs them. - Keep simple transformations inline when they are used once.
- Extract a helper only when the same transformation appears in at least two places or the transformation is meaningful enough to name.
Class Names
- Do not leave duplicate or conflicting
classNamevalues. If the intended class is unclear, ask the user which class to keep instead of deleting one automatically.
Example:
tsx
// Conflicting spacing classes: ask which one should be kept.
<button className="rounded px-2 py-2 text-14 px-6">
Save
</button>