Project Guidlines

Project Guidlines

Project Guidelines

Components

Favor Functional Components

Favor functional components - they have a simpler syntax. No lifecycle methods, constructors or boilerplate. You can express the same logic with less characters without losing readability.

Unless you need an error boundary they should be your go-to approach. The mental model you need to keep in your head is a lot smaller.

Write Consistent Components

Stick to the same style for your components. Put helper functions in the same place, export the same way and follow the same naming patterns.

There isn’t a real benefit of one approach over the other

No matter if you’re exporting at the bottom of the file or directly in the definition of the component, pick one and stick to it.

Name Components

Always name your components. It helps when you’re reading an error stack trace and using the React Dev Tools.

It’s also easier to find where you are when developing if the component’s name is inside the file.

// 👎 Avoid this
export default () => <form>...</form>
// 👍 Name your functions
export default function Form() {
	return <form>...</form>
}

Organize Helper Functions

Helper functions that don’t need to hold a closure over the components should be moved outside. The ideal place is before the component definition so the file can be readable from top to bottom.

That reduces the noise in the component and leaves inside only those that need to be there.

// 👎 Avoid nesting functions which don't need to hold a closure.
function Component({ date }) {
	function parseDate(rawDate) {
		...
	}
	return <div>Date is {parseDate(date)}</div>
}
// 👍 Place the helper functions before the component
function parseDate(date) {
	...
}
function Component({ date }) {
	return <div>Date is {parseDate(date)}</div>
}

Don’t Hardcode Markup

Don’t hardcode markup for navigation, filters or lists. Use a configuration object and loop through the items instead.

This means you only have to change the markup and items in a single place.

// 👎 Hardcoded markup is harder to manage.
function Filters({ onFilterClick }) {
	return (
		<>
			<p>Book Genres</p>
			<ul>
				<li>
					<div onClick={() => onFilterClick('fiction')}>Fiction</div>
				</li>
				<li>
					<div onClick={() => onFilterClick('classics')}>
						Classics
					</div>
				</li>
				<li>
					<div onClick={() => onFilterClick('fantasy')}>Fantasy</div>
				</li>
				<li>
					<div onClick={() => onFilterClick('romance')}>Romance</div>
				</li>
			</ul>
		</>
	)

}
// 👍 Use loops and configuration objects
const GENRES = [
	{
		identifier: 'fiction',
		name: Fiction,
	},
	{
		identifier: 'classics',
		name: Classics,
	},
	{
		identifier: 'fantasy',
		name: Fantasy,
	},
	{
		identifier: 'romance',
		name: Romance,
	},
]

  

function Filters({ onFilterClick }) {
	return (
	<>
		<p>Book Genres</p>
		<ul>
			{GENRES.map(genre => (
			<li>
				<div onClick={() => onFilterClick(genre.identifier)}>
					{genre.name}
				</div>
			</li>
			))}
		</ul>
	</>
	)
}

Naming Conventions

Use Verbose, Descriptive Names

Your code becomes self-documenting when variables, components, and functions are named descriptively. For example:

// 👎 Avoid vague names
const handleClick = () => { ... }

// 👍 Use clear, descriptive names
const handleToggleModal = () => { ... }

Prefix Boolean Variables with is or has

For better readability and understanding, consistently prefix boolean values with is or has. For example:

// 👎 Harder to understand at first glance
const modalOpen = true;

// 👍 More intuitive
const isModalOpen = true;

Name Files and Folders to Reflect Their Content

Match files and folders to their contents. Use camelCase for helper and utility files, PascalCase for components, and avoid vague names.

// 👎 Avoid generic/incorrect file names
helpers.js
components.js

// 👍 Be specific and descriptive
formatDate.js
UserProfileCard.js

Styling

Use CSS Modules or Styled Components

Avoid global or unscoped CSS whenever possible—it leads to unintended side effects and makes maintainability harder. Scoping styles using CSS Modules or styled-components generally leads to cleaner and more predictable behavior.

Standardize Naming Patterns for CSS Classes

Adopt a universal naming approach, such as BEM or other agreed-upon patterns. Example:

/* BEM Example */
.button {
  ...
}
.button--primary {
  ...
}
.button__icon {
  ...
}

State Management

Minimize State in Components

Only store state in the smallest possible scope. If a state value doesn’t need to be in a component, either lift it up or use context/redux instead.

Use useReducer for Complex StateLogic

If your state has multiple sub-values or complex updating logic, use useReducer over useState.

Best Practices

Write Reusable Components

Aim to create components that can serve multiple use cases throughout the app. Avoid creating one-off components unless necessary.

Avoid any in TypeScript

Be specific with types and interfaces to prevent ambiguity and bugs in larger applications.

Comment Complex Logic

If logic inside the component or helper functionality is non-trivial, include clear, concise comments. Avoid commenting obvious statements.

Avoid Inline Styles

Inline styles can be difficult to read and maintain. Prefer class-based styles for scalability.

Separate Concerns

Keep your components focused on a single concern. For example, split UI components (displaying information) from container components (handling logic and state).

Avoid console.log in Production

Remove all unnecessary logs in production code. Use tools like Sentry/Datadog for logging and monitoring instead.

File Organization

Organize Files by Feature or Domain

Instead of grouping files by type (e.g., all components together), organize files by feature or domain. Example:

src/
|- features/
|  |- user/
|     |- UserProfileCard.js
|     |- UserSettings.js
|- shared/
|  |- components/
|- utils/

Create Index Files for Features

Within a feature folder, export all relevant components, utilities, and styles from an index.js. This helps simplify imports elsewhere:

// 👎 Long and repetitive imports
import { ComponentA } from 'features/featureX/ComponentA';
import { ComponentB } from 'features/featureX/ComponentB';

// 👍 Use an index file to centralize exports
import { ComponentA, ComponentB } from 'features/featureX';

Testing

Write Unit Tests for Components

Cover edge cases for props, state changes, and rendering logic to avoid regressions.

Use Meaningful Test Descriptions

Write descriptive test names that clearly describe the expected behavior. Example:

// 👎 Avoid unclear names
test('Input action')

  // 👍 Be descriptive
test('Input should perform an action when typed into')

Mock External Dependencies

Avoid network calls in unit tests. Instead, mock data and external service calls to keep tests fast and consistent.

Code Reviews

Set Clear Merge Criteria

Define a checklist for pull requests, including:

  • Proper naming conventions
  • Relevant tests written
  • Comments for complex logic

Request Feedback Early

Share your changes or architecture ideas early to avoid unnecessary rework later in the development cycle.


Keeping our project structured and organized with clear guidelines will ensure maintainability, scalability, and long-term success for our application.

Comments