Trezor Suite follows strict coding conventions to maintain consistency across the codebase. All code must adhere to these guidelines before merging.
Trezor Suite uses automated formatting tools to enforce code style:
Prettier
Handles code formatting automatically
ESLint
Enforces code quality and catches errors
Prettier Configuration
The project uses the following Prettier settings:
{
"printWidth": 100,
"arrowParens": "avoid",
"bracketSpacing": true,
"singleQuote": true,
"semi": true,
"tabWidth": 4,
"useTabs": false,
"bracketSameLine": false
}
YAML files use 2-space indentation instead of 4 spaces.
Always format your code before committing changes.
yarn g:prettier --write <changed-files>
yarn g:eslint --fix <changed-files>
Basic Syntax Rules
If-Else Statements
Always use braces for if-else blocks, except for simple inline-if without else:
if (condition) doSomething();
Code Spacing
Use empty lines to structure code into logical groups:
export const getPrerequisites = ({ router, device, transport }: PrerequisitesInput) => {
const excluded = getExcludedPrerequisites(router);
const prerequisite = getPrerequisiteName({ router, device, transport });
if (typeof prerequisite === 'undefined') return;
if (excluded.includes(prerequisite)) return;
return prerequisite;
};
export const getPrerequisites = ({ router, device, transport }: PrerequisitesInput) => {
const excluded = getExcludedPrerequisites(router);
const prerequisite = getPrerequisiteName({ router, device, transport });
if (typeof prerequisite === 'undefined') {
return;
}
if (excluded.includes(prerequisite)) {
return;
}
return prerequisite;
};
Group related declarations together, separated from conditional logic by empty lines.
Function Parameters
For functions with more than two parameters, wrap them in an object:
const logAnimalNames = (cat: string, dog: string, guineaPig?: string, showHeading?: boolean) => {
if (showHeading) {
console.log('My Animals');
}
console.log(cat);
console.log(dog);
if (guineaPig) {
console.log(guineaPig);
}
};
// Confusing call - what does true mean?
logAnimalNames('Nancy', 'Rob', null, true);
interface LogAnimalParams {
cat: string;
dog: string;
guineaPig?: string;
showHeading?: boolean;
}
const logAnimalNames = ({ cat, dog, guineaPig, showHeading }: LogAnimalParams) => {
if (showHeading) {
console.log('My Animals');
}
console.log(cat);
console.log(dog);
if (guineaPig) {
console.log(guineaPig);
}
};
// Clear and self-documenting
logAnimalNames({ cat: 'Nancy', dog: 'Rob', showHeading: true });
Conditional Rendering in React
Always ensure JSX conditions evaluate to boolean values only.
React can render values like 0, "", or [], which may cause unexpected behavior:
// Could render 0 if value is 0
{value && <Component value={value} />}
// Boolean condition
{hasValue && <Component value={value} />}
// Double negation
{!!value && <Component value={value} />}
// Early return
if (!value) return null;
return <Component value={value} />;
Import and Export
Named Exports Only
Use named exports exclusively for consistency and better tree-shaking.
export const myFunction = () => {};
export const MyComponent = () => {};
const MyComponent = () => {};
export default MyComponent;
Benefits:
- Easier refactoring with mass-replace
- Better tree-shaking support
- Consistent naming across imports
- Easier code searching
Storybook files are exempt from this rule as they require default exports.
Barrel Files
- Use
index.ts files only in packages to define public interfaces
- Do not use them inside modules to export from directories
- They can introduce circular dependencies and hurt tree-shaking
Naming Conventions
General Rules
Values = Nouns
Variables and constants should be nouns:const userAccount = ...;
const networkSymbol = 'btc';
Functions = Verbs
Functions should start with action verbs:const getUserData = () => ...;
const calculateTotal = () => ...;
Booleans = Questions
Boolean values should ask a question:const isDisabled = true;
const hasFinished = false;
const canProceed = checkPermissions();
Components = Nouns
React components should always be nouns:const AccountSelector = () => ...;
const TransactionList = () => ...;
Abbreviations
Use all capital letters for abbreviations:
const someFAQConstant = ...;
function enterTHPPairingCode() {}
const someFaqConstant = ...;
function enterThpPairingCode() {}
Component Props Interface
Always name component interfaces with the Props suffix:
interface ButtonProps {
label: string;
onClick: () => void;
}
const Button = ({ label, onClick }: ButtonProps) => {
return <button onClick={onClick}>{label}</button>;
};
Suite-Specific Naming
Crypto Networks
// Good
const network = networks['btc'];
// Good
const networkSymbol = networks['btc'].symbol;
// Bad
const coin = networks['btc'].symbol;
const currency = networks['btc'].symbol;
Fiat Currencies
Always prefix fiat-related names with fiat:
const fiatCurrency = 'usd';
const selectFiatCurrency = () => 'usd';
const formatFiatCurrency = value => value;
const currency = 'usd';
const selectCurrency = () => 'usd';
const formatCurrency = value => value;
Assets
Asset refers to a group of accounts for the same network:
Good - List of asset groups
const AssetsList = () => <... />;
Good - Single account detail
const AccountDetailScreen = () => <Screen />;
Bad - Confusing terminology
const AssetDetailScreen = () => <Screen />; // Asset is multiple accounts, not one
All comments must start with an uppercase letter and end with a period:
// This is a comment that will help you understand what is happening in the
// code below.
const someFunction = () => null;
ESLint Rules
Key ESLint rules enforced in the project:
'import/no-extraneous-dependencies': 'error'
'no-restricted-imports': [
'error',
{
patterns: [
{
regex: '/libDev/src',
message: 'Importing from "*/libDev/src" path is not allowed.',
},
],
},
]
Storybook files (*.stories.tsx) have relaxed rules:
- Default exports are allowed
- Hooks can be used outside components
Component Structure
Follow this order when organizing component files:
1. Imports
Import statements at the top
2. Styles
Styled components or style definitions
3. Constants
Component-level constants
4. Helpers
Component-level helper functions
5. Types
Type definitions and interfaces
6. Props Type
Component props interface
7. Component
The component implementation
Inside Components
Maintain consistent ordering within component bodies:
- Redux selectors (global state)
useState (local state)
- Non-effect hooks (
useRef, useForm, useDispatch)
- Effects (
useEffect)
- Functions and callbacks
- Derived values and sub-components
- Render/return statement
Passing Props to Components
Pass only the necessary properties, not entire objects.
const DeviceVersion = ({ version }) => <div>{version}</div>;
<DeviceVersion version={device.version} />
const DeviceVersion = ({ device }) => <div>{device.version}</div>;
<DeviceVersion device={device} />
Benefits:
- Clearer component interface
- Prevents unnecessary re-renders
- Easier to identify dependencies
Prop Drilling and Identifiers
Use Redux selectors as the primary source for complete data.
For data with identifiers (like accounts, devices):
- Pass only the identifier (ID/key) as props
- Each component fetches complete data using selectors
- This prevents unnecessary re-renders and prop drilling
Good - Passing identifiers
const ParentComponent = ({ accountKey }) => {
const account = useSelector(state => selectAccountById(state, accountKey));
return <ChildComponent accountKey={accountKey} />;
};
const ChildComponent = ({ accountKey }) => {
const account = useSelector(state => selectAccountById(state, accountKey));
return <div>{account.balance}</div>;
};
Resources
Prettier Config
Official Prettier documentation
ESLint Rules
ESLint rule reference
TypeScript Guide
TypeScript-specific conventions
Component Guidelines
React component best practices