Project Structure
Organize your frontend code effectively.
Overview
This guide explains the recommended project structure for EZ-Console frontend applications. Following this structure helps maintain code organization, scalability, and developer productivity.
When you consume the ez-console npm package, the public JavaScript API is exactly what web/src/index.ts exports — see Package exports (index.ts).
Standard Directory Structure
web/
├── src/
│ ├── pages/ # Page components
│ │ ├── products/ # Feature-based pages
│ │ │ ├── ProductList.tsx
│ │ │ ├── ProductForm.tsx
│ │ │ └── ProductDetail.tsx
│ │ └── Dashboard.tsx
│ ├── components/ # Reusable components
│ │ ├── common/ # Common components
│ │ │ ├── DataTable.tsx
│ │ │ └── SearchForm.tsx
│ │ └── products/ # Feature-specific components
│ │ └── ProductCard.tsx
│ ├── contexts/ # React contexts
│ │ ├── AuthContext.tsx
│ │ └── AppContext.tsx
│ ├── hooks/ # Custom React hooks
│ │ ├── useAuth.ts
│ │ └── usePermission.ts
│ ├── service/ # API services
│ │ ├── api/
│ │ │ ├── product.ts
│ │ │ └── user.ts
│ │ └── client.ts
│ ├── types/ # TypeScript types
│ │ ├── api.d.ts
│ │ └── index.d.ts
│ ├── utils/ # Utility functions
│ │ ├── format.ts
│ │ └── validation.ts
│ ├── i18n/ # Internationalization
│ │ ├── en-US/
│ │ └── zh-CN/
│ ├── routes/ # Route configuration
│ │ └── index.tsx
│ ├── App.tsx # Main app component
│ └── main.tsx # Entry point
├── public/ # Static assets
├── package.json
├── tsconfig.json
├── vite.config.ts
└── index.html
Directory Descriptions
src/pages/
Contains page-level components. Organize by feature:
// src/pages/products/ProductList.tsx
export const ProductList: React.FC = () => {
// Page implementation
};
Best Practices:
- One page component per file
- Group related pages in subdirectories
- Use descriptive names (e.g.,
ProductList.tsx, notList.tsx)
src/components/
Reusable UI components:
// src/components/common/DataTable.tsx
export const DataTable: React.FC<DataTableProps> = ({ data, columns }) => {
// Component implementation
};
Organization:
common/- Shared components used across featuresproducts/- Feature-specific components- Keep components small and focused
src/contexts/
React Context providers:
// src/contexts/AuthContext.tsx
export const AuthProvider: React.FC = ({ children }) => {
// Context implementation
};
Usage:
- Global state management
- Shared data across components
- Avoid overusing contexts
src/hooks/
Custom React hooks:
// src/hooks/useAuth.ts
export const useAuth = () => {
// Hook implementation
return { user, login, logout };
};
Naming Convention:
- Always start with
use - Descriptive names (e.g.,
useAuth,usePermission)
src/service/
API service layer:
// src/service/api/product.ts
export const productService = {
list: (params: ListParams) => apiGet('/products', params),
get: (id: string) => apiGet(`/products/${id}`),
create: (data: Product) => apiPost('/products', data),
update: (id: string, data: Product) => apiPut(`/products/${id}`, data),
delete: (id: string) => apiDelete(`/products/${id}`),
};
Organization:
- One service file per resource
- Group related API calls
- Use TypeScript for type safety
src/types/
TypeScript type definitions:
// src/types/api.d.ts
export interface Product {
id: string;
name: string;
price: number;
category: string;
}
export interface ApiResponse<T> {
code: string;
data: T;
total?: number;
}
Best Practices:
- Define types close to usage
- Use interfaces for object shapes
- Export types for reuse
src/utils/
Utility functions:
// src/utils/format.ts
export const formatCurrency = (amount: number): string => {
return `$${amount.toFixed(2)}`;
};
export const formatDate = (date: Date): string => {
return date.toLocaleDateString();
};
Organization:
- Group related utilities
- Keep functions pure when possible
- Add JSDoc comments
src/i18n/
Internationalization files:
src/i18n/
├── en-US/
│ ├── common.ts
│ └── product.ts
└── zh-CN/
├── common.ts
└── product.ts
Structure:
- One file per language per namespace
- Use dot notation for keys
- Keep translations organized
src/routes/
Route configuration:
// src/routes/index.tsx
export const routes = [
{
path: '/products',
element: <ProductList />,
name: 'products',
},
];
File Naming Conventions
Components
- PascalCase for components:
ProductList.tsx - camelCase for utilities:
formatDate.ts - kebab-case for assets:
product-icon.svg
Examples
✅ Good
- ProductList.tsx
- useAuth.ts
- formatDate.ts
- product-icon.svg
❌ Bad
- productList.tsx
- UseAuth.ts
- format_date.ts
- ProductIcon.svg
Import Organization
Organize imports in this order:
// 1. React and React-related
import { useState, useEffect } from 'react';
import { useNavigate } from 'react-router-dom';
// 2. Third-party libraries
import { Button, Table } from 'antd';
import { useRequest } from 'ahooks';
// 3. Framework imports
import { useTranslation, apiGet, PermissionGuard } from 'ez-console';
// 4. Internal imports (absolute paths)
import { ProductService } from '@/service/api/product';
import { formatCurrency } from '@/utils/format';
// 5. Types
import type { Product } from '@/types';
Path Aliases
Configure path aliases in tsconfig.json:
{
"compilerOptions": {
"paths": {
"@/*": ["src/*"],
"@/components/*": ["src/components/*"],
"@/pages/*": ["src/pages/*"],
"@/service/*": ["src/service/*"],
"@/utils/*": ["src/utils/*"],
"@/types/*": ["src/types/*"]
}
}
}
And in vite.config.ts:
import { defineConfig } from 'vite';
import path from 'path';
export default defineConfig({
resolve: {
alias: {
'@': path.resolve(__dirname, './src'),
},
},
});
Feature-Based Organization
For larger applications, organize by feature:
src/
├── features/
│ ├── products/
│ │ ├── components/
│ │ ├── pages/
│ │ ├── hooks/
│ │ ├── service/
│ │ └── types.ts
│ └── users/
│ ├── components/
│ ├── pages/
│ └── ...
Best Practices
1. Keep Components Small
// ✅ Good: Small, focused component
export const ProductCard: React.FC<{ product: Product }> = ({ product }) => {
return <Card>{product.name}</Card>;
};
// ❌ Bad: Too large, does too much
export const ProductPage: React.FC = () => {
// 500+ lines of code
};
2. Extract Reusable Logic
// ✅ Good: Extract to hook
const useProductList = () => {
const [products, setProducts] = useState([]);
// Logic here
return { products, loading, error };
};
// ❌ Bad: Logic in component
const ProductList = () => {
const [products, setProducts] = useState([]);
// Logic mixed with UI
};
3. Use TypeScript
// ✅ Good: Typed props
interface ProductCardProps {
product: Product;
onEdit?: (id: string) => void;
}
// ❌ Bad: No types
const ProductCard = ({ product, onEdit }) => {
// ...
};
4. Consistent File Structure
// ✅ Good: Consistent structure
// 1. Imports
// 2. Types/Interfaces
// 3. Component
// 4. Exports
Related Topics
- Application Shell - Main app structure
- Routing & Navigation - Route configuration
- State Management - State organization
Need help? Ask in GitHub Discussions.