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

#RuleSummary
1Screens/ComponentsNo nested screens/ folders
2List/Detail PatternUse List and Detail suffixes
3Index.tsx ConventionOne folder per component with index.tsx
4Component NamesAvoid redundant context in names
5Utils FilesUse utils/ folder at shared level
6Components NestingKeep nested components with parent
7ConstantsColocate or use utils/constants/
8Import PreferencesFavor relative imports and TS aliases
9useViewModel PatternInject 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 naming

Rule 3: Index.tsx Convention

Create a dedicated folder per component and name the main file index.tsx.

Do:

components/
└─ MarketRowItem/
   β”œβ”€ index.tsx
   └─ useMarketRowItemViewModel.tsx
🚫

Don’t:

components/
β”œβ”€ MarketRowItem.tsx              // ❌ No folder
└─ useMarketRowItemViewModel.tsx  // ❌ Outside component folder

Rule 4: Component Names

Avoid redundant context in component names. The folder structure provides context.

Do:

features/Deposit/screens/SelectNetwork/useSelectNetworkViewModel.tsx
🚫

Don’t:

features/Deposit/screens/DepositFlowSelectNetwork/useDepositFlowSelectNetworkViewModel.tsx
// ❌ "DepositFlow" is redundant - we're already in Deposit feature

Rule 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, Footer
🚫

Don’t:

Market/
└─ components/
   └─ Header/
      β”œβ”€ index.tsx
      └─ utils.tsx  // ❌ Utils as sibling file

Rule 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.tsx
🚫

Don’t:

MarketDetail/
β”œβ”€ Stats/
β”‚  └─ index.tsx
β”œβ”€ Price/           // ❌ Flattened - unclear hierarchy
β”‚  └─ index.tsx
β”œβ”€ Supply/          // ❌ Flattened - unclear hierarchy
β”‚  └─ index.tsx
└─ index.tsx

Rule 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, SendFlow
🚫

Don’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 index

Rule 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

Ledger
Copyright Β© Ledger SAS. All rights reserved. Ledger, Ledger Stax, Ledger Flex, Ledger Nano, Ledger Nano S, Ledger OS, Ledger Wallet, [LEDGER] (logo), [L] (logo) are trademarks owned by Ledger SAS.