Project structure
Package organization
Organize the project into multiple packages using npm workspaces to simplify the management of large, complex projects. This approach allows you to break down the project into smaller, manageable modules right from the start.
Based on our experience in several projects, as a project grows in scope and complexity, the need to create multiple packages often arises. Without an initial modular structure, this requires significant refactoring later, adding unnecessary overhead. By starting with npm workspaces, you can future-proof your project while keeping the structure clean and scalable from the beginning. It’s a low-cost setup with high long-term benefits.
Here are some common packages typically used in a project. A central application package serves as the main entry point for the application, which can be named anything, while reusable UI components should be organized into a dedicated design-system package.
packages/
├── design-system/ // Shared components, theme, colors etc.
├── config-xyz // Configuration packages
Folder and file organization:
Use a feature-based structure to group related code together. This makes it easier to scale and maintain the project.
Example
packages/design-system/src/
├── components/ // Components
├── features/ // Feature-specific components and logic
├── hooks/ // Custom hooks
├── services/ // should not directly handle UI logic, only API calls, utilities, or business logic
├── store/ // State management (e.g., Redux or Context)
├── assets/ // Static files like images, fonts, and styles
├── styles/ // Global styles and theme configuration
├── lib/ // Any library files.
└── index.tsx // Main entry point
web/src/
-- index.css // Global stylesheet entry point for the web app
-- __tests__ // Unit and integration test files for the app.
-- __mocks__ // Mock implementations for tests.
-- assets/ // Static assets like images, icons, and fonts.
-- e2e/ // End-to-end test files for app workflows.
-- main.tsx // Main application entry file that renders the React app.
-- index.css // Tailwind and shared style imports for global CSS.
-- vite-env.d.ts // TypeScript declaration file for Vite.
Component folder structure
Each component should reside in its own folder to maintain organization and encapsulation. The recommended structure for a component folder is as follows:
component-name/
├── component-name.stories.tsx // Storybook file.
├── index.tsx // Entry point for importing/exporting the component
├── types.ts // TypeScript types and interfaces used in the component
├── hooks.ts // Custom hooks related to the component
├── someFunction.ts // Utility functions used exclusively by the component.
└── ... // Additional files specific to the component
Environment configurations
Keep .env files in root.
Example
REACT_APP_API_URL=https://api.example.com
REACT_APP_ENV=development
Naming conventions
Use the following for naming folders, files and components files.
- kebab-casing for all folders.
- Example: user-profile, product-list, global-styles.
- camelCasing for all files including component and no component files
- Example: userProfile.js, apiService.js, authContext.js, header.jsx, app.jsx
Why these conventions?
Consistent naming conventions make it easier to navigate and understand the codebase, especially in large projects. While naming conventions can vary across many open source projects and are often subjective, it’s crucial to adopt a unified standard. After some internal discussions within the team, we’ve decided to use this naming convention across all our projects to maintain consistency, reduce confusion, and streamline collaboration.
Entry points
Keep your index.js or index.tsx minimal. Modularize configuration files (e.g., routes, store etc) into separate folders to keep the entry point clean. It should only include:
- Rendering the root React component with ReactDOM.
- Wrapping the app with providers (e.g., React.StrictMode, Redux Provider, ThemeProvider).
Example
import React from 'react';
import ReactDOM from 'react-dom/client';
import { Provider } from 'react-redux';
import App from './App';
import store from './store';
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<React.StrictMode>
<Provider store={store}>
<App />
</Provider>
</React.StrictMode>
);







