Doβs and Donβts
Component patterns and coding conventions with clear examples. Use the rule numbers to reference guidelines in code reviews (e.g. βplease follow Rule 1β).
Quick Reference
| # | Rule | Summary |
|---|---|---|
| 1 | Screens/Components | No nested screens/ folders |
| 2 | List/Detail Pattern | Use List and Detail suffixes |
| 3 | Index.tsx Convention | One folder per component with index.tsx |
| 4 | Component Names | Avoid redundant context in names |
| 5 | Utils Files | Use utils/ folder at shared level |
| 6 | Components Nesting | Keep nested components with parent |
| 7 | Constants | Colocate or use utils/constants/ |
| 8 | Import Preferences | Favor relative imports and TS aliases |
| 9 | useViewModel Pattern | Inject ViewModel as props |
Rule 1: Screens/Components
Folders inside screens/ cannot have their own screens/ subfolders. If a component is uniquely associated with a screen, place it in a components/ subfolder inside that screen folder.
Do:
Market/
ββ components/
β ββ MarketRowItem/
ββ screens/
ββ MarketList/
β ββ components/
β ββ BottomSection/
ββ MarketDetail/Donβt:
Market/
ββ screens/
ββ MarketList/
β ββ screens/ // β No nested screens
ββ MarketDetail/Rule 2: List/Detail Pattern
- Suffix list components with
List(always singular) - Suffix detail components with
Detail
Do:
Market/
ββ screens/
ββ MarketList/
ββ MarketDetail/Donβt:
Market/
ββ screens/
ββ ListOfMarketItem/ // β Wrong naming
ββ MarketItemPage/ // β Wrong namingRule 3: Index.tsx Convention
Create a dedicated folder per component and name the main file index.tsx.
Do:
components/
ββ MarketRowItem/
ββ index.tsx
ββ useMarketRowItemViewModel.tsxDonβt:
components/
ββ MarketRowItem.tsx // β No folder
ββ useMarketRowItemViewModel.tsx // β Outside component folderRule 4: Component Names
Avoid redundant context in component names. The folder structure provides context.
Do:
features/Deposit/screens/SelectNetwork/useSelectNetworkViewModel.tsxDonβt:
features/Deposit/screens/DepositFlowSelectNetwork/useDepositFlowSelectNetworkViewModel.tsx
// β "DepositFlow" is redundant - we're already in Deposit featureRule 5: Utils Files
Do not colocate utils as a single file. Use a utils/ folder and place it at the top level of components that import it.
Do:
Market/
ββ screens/
β ββ MarketDetail/
ββ components/
β ββ Header/
β ββ Footer/
ββ utils/
ββ index.tsx // Shared by MarketDetail, Header, FooterDonβt:
Market/
ββ components/
ββ Header/
ββ index.tsx
ββ utils.tsx // β Utils as sibling fileRule 6: Components Nesting
If a component is not reused elsewhere, create it next to its parent. Do not flatten nested components.
Do:
MarketDetail/
ββ components/
β ββ Stats/
β ββ Price/
β β ββ index.tsx
β ββ Supply/
β β ββ index.tsx
β ββ index.tsx
ββ index.tsxDonβt:
MarketDetail/
ββ Stats/
β ββ index.tsx
ββ Price/ // β Flattened - unclear hierarchy
β ββ index.tsx
ββ Supply/ // β Flattened - unclear hierarchy
β ββ index.tsx
ββ index.tsxRule 7: Constants
Colocate constants with their component. If shared between components, declare them in utils/constants/.
Do:
utils/
ββ constants/
ββ coins.ts // Shared by MarketDetail, MarketList, SendFlowDonβt:
MarketDetail/
ββ coins.ts // β Local constant
ββ index.tsx
MarketList/
ββ index.tsx // Imports from ../MarketDetail/coins βRule 8: Import Preferences
- Favor relative imports when not moving more than one folder up
- Favor TS aliases over absolute imports
- Add TS aliases when it improves readability
Do:
import MarketDetail from "LLM/features/Market/screens/MarketDetail";
import MarketDetail from "../MarketDetail";
import MarketDetail from "./MarketDetail";Donβt:
import MarketDetail from "src/features/Market/screens/MarketDetail"; // β Absolute path
import MarketDetail from "../../../MarketDetail"; // β Too many levels
import MarketDetail from "./MarketDetail/index.tsx"; // β Explicit indexRule 9: useViewModel Pattern
Inject the ViewModel hook result as props to the View component. Do not call the ViewModel hook inside the component body.
Do:
// index.tsx - Inject ViewModel as props
const MarketDetail = () => <View {...useMarketDetailViewModel()} />;
// View.tsx - Receives pre-computed props
const View = ({ price, volume, onRefresh }: Props) => (
<Screen>
<Text>{price}</Text>
<Text>{volume}</Text>
<Button onPress={onRefresh}>Refresh</Button>
</Screen>
);Donβt:
// β Don't call ViewModel inside the View component
const MarketDetail = () => {
const { price, volume, onRefresh } = useMarketDetailViewModel();
return (
<Screen>
<Text>{price}</Text>
<Text>{volume}</Text>
<Button onPress={onRefresh}>Refresh</Button>
</Screen>
);
};Why? Separating the ViewModel call from the View makes testing easier. You can test the View with different props without mocking hooks, and test the ViewModel logic independently.
See also
- MVVM Pattern β Full ViewModel implementation pattern
- Architecture β Folder structure and import rules
- How to Structure a Feature β Applying these rules when creating features