Version 1
This project follows a feature-based folder structure with shared global resources.
All imports use the @ alias configured in vite.config.ts:
// Use this
import { Button } from '@/components'
// Not this
import { Button } from '../../../components/button'src/
├── assets/ # Icons, images, fonts
├── components/ # Reusable UI components (global)
├── config/ # Axios, React Query, test setup
├── constants/ # App-wide constants and config values
├── features/ # Domain-specific implementations
├── hooks/ # Custom React hooks (global)
├── interfaces/ # TypeScript types and interfaces
├── queries/ # React Query data-fetching hooks
├── routes/ # TanStack Router route definitions
├── styles/ # Global CSS and variables
├── utils/ # Helper functions
└── main.tsx # App entry point
Presentational UI components shared across the app. No business logic — props in, UI out.
components/
├── button/
│ ├── button.tsx
│ └── button.module.css
├── input/
├── heading/
├── card/
├── badge/
├── loader/
├── spinner/
├── table/
├── modal/
├── sidebar/
├── dropdown/
└── index.ts
import { Button, Input, Card } from '@/components'Self-contained, domain-specific components that contain business logic. See Features below.
import { MetricsSummary, ActivityFeed } from '@/features'Custom hooks that encapsulate reusable logic. Co-locate tests alongside each hook.
hooks/
├── use-form.tsx
├── use-form.test.tsx
├── use-pagination.tsx
├── use-media-query.tsx
├── use-sort-data.tsx
├── use-sidebar-filters.tsx
└── index.ts
import { useForm, usePagination, useMediaQuery } from '@/hooks'Pure helper functions with no side effects.
import { formatDate, formatCurrency } from '@/utils'Features are self-contained units that combine components, hooks, and queries to implement a specific piece of functionality.
feature-name/
├── feature-name.tsx
├── feature-name.module.css
├── feature-name.test.tsx # optional
└── index.ts # optional re-export
features/
├── metrics-summary/
├── activity-feed/
├── recent-transactions/
├── user-table/
├── report-filters/
├── chart-panel/
├── notification-banner/
├── sidebar-nav/
├── date-range-picker/
└── index.ts
// features/metrics-summary/metrics-summary.tsx
import { Card, Heading } from '@/components'
import { useGetAnalytics } from '@/queries'
import styles from './metrics-summary.module.css'
export const MetricsSummary = () => {
const { data } = useGetAnalytics()
return (
<div className={styles.grid}>
<Card title="Revenue">{data?.revenue}</Card>
<Card title="Users">{data?.users}</Card>
</div>
)
}Note: This is not required if tanstack-query (formally known as react-query) is not used.
React Query hooks for data fetching, organised by domain.
queries/
├── users/
├── analytics/
├── reports/
└── index.ts
import { useGetAnalytics, useGetUsers } from '@/queries'All TypeScript types and interfaces, centralised.
import { UserInterface, ReportInterface } from '@/interfaces'App-wide constants — API endpoints, feature flags, config values.
import { API_BASE_URL, FEATURE_FLAGS } from '@/constants'| Use a Feature when... | Use a Component when... |
|---|---|
| It has business logic | It's purely presentational |
| It fetches or transforms data | It only receives props |
| It's domain-specific | It's generic and reusable |
| It composes multiple things | It's a simple UI building block |
Key rule: Features should not import from other features. If two features share logic, extract it into a hook or utility.
| Type | Convention | Example |
|---|---|---|
| Components | kebab-case.tsx |
metrics-summary.tsx |
| Styles | kebab-case.module.css |
metrics-summary.module.css |
| Tests | kebab-case.test.tsx |
metrics-summary.test.tsx |
| Hooks | use-kebab-case.tsx |
use-pagination.tsx |
| Interfaces | kebab-case.interface.tsx |
user.interface.tsx |
| Utils | camelCase.ts |
formatDate.ts |
Always use absolute imports — the @/ prefix for anything from src/.
Export through index.ts — each folder exposes a single public API:
// components/index.ts
export * from './button/button'
export * from './card/card'Co-locate tests — keep *.test.tsx files next to the code they test.
Use CSS Modules — *.module.css for scoped styles, no global class pollution.
Don't cross-import features — features are peers, not parents. Shared logic goes in hooks/ or utils/.
Do not manually edit src/routeTree.gen.ts — this is auto-generated by TanStack Router.