Skip to Content
We're improving our docs. Share your experience and help shape what comes next.

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

Last updated on
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.