This project follows the Airbnb JavaScript Style Guide as its baseline. The rules below extend and override it where necessary.
- Functions and methods start with an imperative verb:
showMessage(),updateUser(),runMethod(). Exception:callback. - Acronyms follow standard camelCase rules (no all-caps):
parseJson,jsonToYaml,isUiReady.
Boolean variables and component props must always start with a modal verb:
is* — isVisible, isUiReady
has* — hasChanged
are* — areMessagesLoaded (use `are` for plurals, never `is`)
should* — shouldRedirect
can* — canComment
will* — willChange
Exception: the argument force.
Optional boolean function arguments and component props must default to false. This means if you need a prop that hides a user avatar, name it noAvatar rather than passing hasAvatar={false}.
| Short | Full | Context |
|---|---|---|
e |
event |
Event handler argument |
err |
error |
catch block argument |
cb |
callback |
Callback-accepting functions |
Single-letter element names are acceptable in one-line collection lambdas:
users.map(u => u.name);Avoid all other abbreviations.
Static constants must not appear inside function bodies. Hoist them to the top of the module with a descriptive UPPER_SNAKE_CASE name.
Bad:
function buildLayout() {
const extraPadding = 16; // 1 rem
width += 16 + 8;
}Good:
const LAYOUT_EXTRA_PADDING = 16; // 1 rem
const LAYOUT_EXTRA_MARGIN = 8; // 0.5 rem
function buildLayout() {
width += LAYOUT_EXTRA_PADDING + LAYOUT_EXTRA_MARGIN;
}Prefer function declarations over function expressions, except when you need to bind this via an arrow function.
Functions are ordered top-down by call hierarchy: high-level functions at the top, low-level helpers at the bottom. ESLint rule:
"@typescript-eslint/no-use-before-define": ["error", { "functions": false }]If a pure function is called more than once within the same scope, store its result in a variable instead of calling it repeatedly.
If a function cannot or should not run when an argument is absent, declare that argument as required and check for its presence at the call site, not inside the function.
Bad:
function someFunc(value?: string) {
if (!value) return;
// ...
}
someFunc(x);Good:
function someFunc(value: string) {
// ...
}
if (x) {
someFunc(x);
}Prefer early returns (guard clauses) over large or deeply nested conditional blocks.
Bad:
function someFunc() {
// ...
if (condition) {
// Large block of code
}
}Good:
function someFunc() {
// ...
if (!condition) {
return;
}
// Large block of code
}- Comments start with a capital letter.
- Single-sentence comments have no trailing period.
- Multi-sentence comments end each sentence with a period.
- Code entities referenced in comments are wrapped in backticks
`. - Comments are direct assertions about current behavior. Do not frame them as bug history, change history, or contrast with a previous state. Git history is the record of what changed; comments explain what is.
- A comment must state a non-obvious constraint the code cannot express. If the code is self-evident, omit the comment - even a "why" rationale is redundant when the reasoning is apparent from the code itself.
Bad (changes-history-in-comments anti-pattern):
// We no longer fetch this lazily — preloads on mount since #4321 broke scrolling.
// Previously this used `Math.abs`, but losses should sink, so we keep the sign.
// Slice GLOBALLY rather than per-wallet, so a tiny jetton can't push out a large position.Good:
// Preloads on mount so the scroll position is stable on first paint.
// Sort is signed so losing positions sink below zeros.
// Cap applies to the user's full asset set across all wallets.Do not keep unused code, "just in case" code, or speculative library-style utilities. If an object is not used outside its own module, it must not be exported.
When a variable is guaranteed to exist at runtime but TypeScript cannot infer that, use the non-null assertion operator ! instead of a conditional check.
// Correct
func(a!);
// Incorrect — do not guard when the value is guaranteed
if (a) func(a);Minimize the number of global-connected containers. Never connect components that render inside loops — this creates N extra listeners for every global state change. Instead, pass the necessary props down from a parent container.
Loops inside mapStateToProps slow down the overall container evaluation on every global state change. Remove them.
Never return array literals, object literals (including empty ones) from mapStateToProps. This breaks the shallow-equality check and causes unnecessary re-renders.
Use useLastCallback instead of useCallback. It avoids unwanted effect triggers and speeds up rendering by eliminating dependency comparison overhead.
useMemo should be used only when:
- The computation contains loops or expensive operations, or
- It produces a complex object passed as a prop to a child
memocomponent.
Wrap components in memo(), but only if none of their props are inherently non-memoizable (e.g. children).
- When adding a new required section to
GlobalState, always add a corresponding entry inmigrateCache. - When changing types in global state or its nested objects, verify the migration path from the current
masterbranch.
- Avoid nested selectors and tag-based selectors. Every styled element must have its own class.
- Use
reminstead ofpx. Conversion formula:N px = N / 16 rem.
Follow this pattern for PR titles and commit messages:
[Tag] Component / Area: Imperative description
- Tag (optional):
[Refactoring],[Perf],[Size],[Dev],[SEO],[CI],[Security], or a platform tag such as[Classic],[iOS],[Android],[Electron]. - Use
[Classic]for changes insidesrc/(excludingsrc/api/), i.e. the classic web/extension UI layer. - Component or domain area — capitalized.
- Colon, followed by an imperative-mood description starting with a capital letter.
- Prefer plain text over code entities, but use backticks (
`) when referencing programmatic names. - Each sentence starts with a capital letter.
- If a commit contains more than one task, separate them with a semicolon.
- No trailing period or semicolon.
Video Player: Hide download button in fullscreen
Message / Round Video: Fix progressive loading
PWA: Support system sharing menu
[SEO] Replace `meta[noindex]` with `link[canonical]`
[iOS] Startup: Add logs and signposts
[Refactoring] Fix @typescript-eslint/await-thenable errors
[Classic] Accounts: Allow view-only accounts to connect to dapps
Add Closes #<issue_number> to the PR description to link it to the corresponding issue.