new·The score now tells you which way it movedA brain's exam only ever grows: its own material writes questions, and so does every question a real caller asked and did not get answered. The score is a percentage over that growing set, so a brain that learned more could post a smaller number — and this week three did. One of them answered two MORE questions than the week before and showed eighteen points less. Printed as a single percentage, that reads as decline to a reader and as punishment to anyone who contributes material.all news →
mozg.beta
Sign in

Expo · Router · all subjects

advanced-patterns

175 notes in this subject, read out of this brain and free to use. This is page 3 of 3.

warm_ttr metric definition

warm_ttr measures the same as cold_ttr, but for screens that were already rendered before focus, typically because they were preloaded via <Link prefetch /> or the user navigated back to them.

warm_ttr event parameters

warm_ttr event includes: routeName (string, route pattern like /(tabs)/sessions/[sessionId]), url (string, resolved pathname for the navigation), urlHidden (boolean, present as true when url is omitted because a parameter was filtered), routeParams (object, resolved route params like {sessionId: 'abc'}).

tti (time to interactive) metric definition

tti measures time from when a navigation action is dispatched to when markInteractive() is called on the destination screen. Only the first call per navigation is recorded, so it is safe to call markInteractive() multiple times.

tti event parameters

tti event includes: routeName (string, route pattern like /(tabs)/sessions/[sessionId]), url (string, resolved pathname), urlHidden (boolean, present as true when url is omitted because a parameter was filtered), routeParams (object, resolved route params), and any custom params passed via markInteractive({ params: { ... } }).

routeName is a pattern, not resolved URL

routeName is a pattern (like /(tabs)/sessions/[sessionId]), not a resolved URL (like /sessions/abc). This keeps metrics stable across distinct param values so the dashboard buckets them together. Resolved values are still available on the event via url and routeParams.

router.prefetch does not emit cold_ttr or warm_ttr

Calls to router.prefetch() do not count as a user navigation and never seed a cold_ttr or warm_ttr measurement. The next user-driven navigation to that route emits warm_ttr because the screen has already rendered.

markInteractive outside screen component error

If markInteractive() logs 'Calling markInteractive on unmounted screen' or 'No metadata available for the current screen', the call ran outside a screen component or after unmount. Move the call into a useEffect inside the screen component.

TSDoc comments should use third-person declarative style

Write TSDoc comments using third-person declarative mood (e.g., 'Gets...', 'Returns...', 'Checks...'), not imperative mood (e.g., 'Get...', 'Return...'). The first sentence should describe what the function does. Use periods when writing multiple sentences, but leave off the trailing period for single-phrase descriptions.

Document APIs alongside implementation, not as afterthought

TSDoc comments should be written when implementing new features, not retroactively. Documentation should be created as code is written, not deferred to later.

Explain the iceberg in API documentation

API documentation should cover failure modes, side effects, and concurrency behavior, not just parameters and return values. Document important behavior and edge cases beyond the basic interface.

TSDoc supported tags reference

Supported TSDoc tags and their purposes: @param (parameter description), @return/@returns (return value description), @default (default value, rendered as inline code), @platform (platform availability: android, ios, web, expo), @example (code example at bottom of description), @deprecated (deprecation notice, auto-formatted as warning), @experimental (experimental API label), @hidden/@internal/@private (hide from generated docs), @header (group methods under custom headers), @needsAudit (mark for security/API audit as comment), @hideType (hide generated Type callout for constants).

@platform tag usage rules

Do NOT use @platform tags when all platforms are supported—only add them when limiting availability. Use multiple @platform tags for multiple platforms, one per line. Can specify minimum version (e.g., @platform ios 11+). Available platforms: android, ios, web, expo (Expo Go).

Code examples in docblocks must be wrapped in triple backticks

Code examples in @example tags must always be wrapped in triple backticks with a language tag (ts, tsx, js, json, swift, kotlin). Example: ```ts\nconst result = await someFunction();\n```

Blockquote formatting in TSDoc comments

Use > blockquotes for important callouts in TSDoc. Format: > **Note:** for informational content, > **warning** (lowercase 'warning') for cautions. Multi-line notes use > on each line with blank > between paragraphs.

Return value language for promises

In @returns/@return tags, use 'resolves to' following MDN's convention. Preferred: '@returns A promise that resolves to a CameraPhoto object.' Also acceptable: '@returns A promise fulfilled with a CameraPhoto object.' In inline prose, 'resolves with' is acceptable.

Types must be exported from entry point for docs generation

Types must be exported from the entry point file (index.ts or MainModule.ts) for the docs generation system to pick them up. Use direct re-export from types file or re-export after import. The GenerateDocsAPIData script processes the entry point specified in package mapping and extracts all publicly exported symbols.

Parameter documentation with blockquote notes

Parameters can include Markdown formatting (links, emphasis, lists) and blockquotes for important notes. Format: @param paramName Description starting with capital letter. Blockquotes can follow the description to provide additional context about constraints or permissions.

Type and interface property documentation

Document each property in a type or interface individually with JSDoc comments. Use @default tag (no markdown, rendered as inline code) to specify default values. Descriptions should teach something useful about the property, not just restate the name.

Constant documentation format

Document constants by explaining what they represent. Example: '`true` if the app is running on a real device and `false` if running in a simulator or emulator.'

Enum documentation with platform-specific values

Document the enum with an overall description, then document individual enum values. Platform-specific values can have @platform tags on individual value entries.

Quality over quantity in API documentation

Write only useful documentation. No documentation is better than useless documentation like 'The width' for a width property. Aim to teach something meaningful about each API.

@link tag is not supported in TSDoc for Expo APIs

The @link tag is not supported in Expo's TSDoc implementation. Use standard Markdown links instead for cross-references.

Code block format for documentation examples

Code blocks in .mdx documentation should include language tag (ts, tsx, js, json, swift, kotlin) and file path label when showing where code goes. Example: ```ts app/(tabs)/index.tsx

Conditional mutation with guard that silently skips is a logic defect

A conditional mutation whose guard can silently skip it while leaving an invariant the code itself states is a defect. For example: if let i = xs.firstIndex(of: key) { ... }, if let x = dict[k] { ... }, or a guard around a reorder, insert, or normalize step where the nil branch does nothing. If a comment says the result must hold a property but the code only sometimes ensures it, that is a defect. Ask what the collection contains when the key is absent and whether the caller can tell.

Cached index or offset into collection with changing length is a data race

Index and offset arithmetic over a collection whose length can change between computation and use is a defect. A cached index into a playlist, queue, or cursor that a concurrent edit invalidates will access invalid memory.

Zero or negative limit parameter must not be treated as unset

A limit, count, or range parameter with a meaningful zero or negative value must not be treated as unset. For example, limit(0) should not return every asset; it should respect the zero limit.

Recursion without base case or skippable base case causes infinite loops

Recursion with no base case, or a base case an input can skip, will infinite loop. This is especially dangerous in encoders, converters, and type coercion.

String operations assuming ASCII or single-byte characters cause truncation bugs

String and byte handling that assumes ASCII, single-byte characters, or a fixed width is a defect. Using count as a byte length, using utf8 length as a character count, or using a fixed buffer for a name causes truncation of non-ASCII characters.

deletingPathExtension() removes everything after final dot, not just extensions

The path method deletingPathExtension() strips whatever follows the last dot even when it is not an extension. For example, 'Q3 Report.v2' becomes 'Q3 Report'. Prefer appending to lastPathComponent over replacing.

Geometry arithmetic on unrounded values causes pixel offset errors

Geometry and layout arithmetic on unrounded or unscaled values is a defect when the result feeds a pixel offset or frame. Values must be rounded before use in layout calculations.

Stale state when active instance changes is a defect

A registry, now-playing info, or shared controller updated on activate but not cleared on switch is a defect. The next instance will inherit the previous instance's values.

AVQueuePlayer advances forward only, cannot replay ended items

AVQueuePlayer advances forward only. An item played to its end will not replay if re-inserted, so an edit at or before the current index requires rebuilding the queue rather than splicing.

NotificationCenter observations are global, not scoped to the observer instance

NotificationCenter observations are global, not scoped to the instance that registered them. Code assuming a notification concerns 'my player' or 'my view' needs to check the object.

UIActivityViewController completion fires on dismissal, not when consumer finishes

UIActivityViewController.completionWithItemsHandler completion handler fires on dismissal rather than on the consumer finishing. Mail, Files, and AirDrop keep reading the source URL after the sheet closes, so deleting a staged file in that handler truncates the share.

PhotoKit resource selection by array index is unreliable

PhotoKit resource selection by array index rather than by mediaType is unreliable. Apple does not guarantee resource ordering, and resources.first can return a .pairedVideo on a Live Photo.

Screen geometry and display scale must use SceneGeometry, never UIScreen.main or static

Screen geometry must not be read from process-global APIs like UIScreen.main.bounds, UIScreen.main.scale, UIApplication.shared.windows.first, or UIWindowScene.interfaceOrientation, and display scale must never be cached in a static or stored property. Display scale differs per scene and is not constant. Use SceneGeometry.bounds(for:) / displayScale(for:) / keyWindow(for:) instead.

Private API class or selector must not be a plain string literal

A private-API class or selector written as a plain string literal will be caught by static analysis on App Store submission. Split the literal (for example: "_UI".appending("ContextMenuContainerView")) when the usage is deliberate.

AsyncFunction Promise must be settled in all code paths

An AsyncFunction taking a Promise must settle it (resolve or reject) in every code path. Early returns, guards, catches, and callback branches must not leave the promise unsettled.

Promise must not be settled from deinit without atomicity

A promise must not be settled from deinit. Correctness depends on a retain graph you do not own: over-retention hangs the promise forever, and an unrelated release reports a misleading error. If a deinit backstop is needed, the settled flag must have a lock or atomic operation; a plain Bool read from deinit and written from a queue is a data race.

Response body or stream must settle exactly once in all terminal branches

A response body, stream, or reader must settle exactly once. Every terminal branch—success, HTTP error, transport error, cancellation—must settle the response exactly once. Stopping settlement after mid-stream failure is a defect.

Work scheduled from module using captured runtime will crash on reload

Work scheduled from a module that can outlive a JavaScript runtime reload will crash if it uses a captured runtime, JavaScriptObject, or promise resolver after reload. Check the diff registers for teardown.

Availability guard below deployment target or above with no fallback is dead code

An #available guard whose version is below the 16.4 deployment target creates a dead branch. A guard that checks above the deployment target with no fallback for supported versions is also a defect.

Availability guard must check all platforms the type is compiled for

A guard that checks fewer platforms than the type is compiled for is incomplete. An if #available(iOS 17.4, *) on a symbol that also ships to macOS or tvOS needs those versions named.

Behavior changes in beta OS require fallback for shipping versions

New code relying on behavior Apple changed or deprecated in a shipping or beta OS needs a fallback. Deprecation alone is not a defect, but a behavior change with no fallback is.

SharedObject must release OS resources in sharedObjectWillRelease, not deinit

A SharedObject or SharedRef subclass that acquires an OS resource—a NotificationCenter or KVO observer, Timer, CADisplayLink, AVPlayer time observer, open file handle, socket, or pixel-buffer pool—must release it in sharedObjectWillRelease(), not deinit.

AppContext must be stored weakly, never strongly

AppContext must not be stored strongly. Do not use a stored let/var appContext, do not capture [appContext] in an escaping closure or Task, and do not hold a static containing one. Use weak and bail out when nil.

Staged files must use appContext-scoped cache directory, not global temp

A file must be staged into the appContext-scoped cache directory, not the global FileManager.default.temporaryDirectory. Use FileSystemUtilities.generatePathInCache(appContext, in:extension:), which is the convention (~10 packages follow it). Staging outside it places readable content outside the experience sandbox, which is how Expo Go scopes storage per experience.

sendEvent or emit call with name not in Events declaration is a contract defect

A sendEvent("name", ...) or emit(event:) call where "name" is absent from that module's Events(...) declaration is a defect. Also flag removing a name from Events(...) while sendEvent calls for it remain.

Swift Record @Field with Swift default and no .required is silent defaulting

A Swift Record @Field representing input JavaScript must supply must not be declared non-optional with a Swift default and no .required option. Silent defaulting hides caller bugs.

Convertible Swift type as return must override convertResult

A Swift type conforming to Convertible used as a return type, or inside a returned Record, must override convertResult. The default implementation converts the value to undefined and logs a warning.

Thrown error must be named Exception subclass with stable code

A plain Swift Error, NSError, or bare Exception() thrown where JavaScript branches on the failure is a defect. Throw a named Exception subclass with a stable code instead.

Error message must state what, why, and how

An error message stating only what failed is incomplete. Repo guidance is what / why / how — especially for an error raised from a path users hit for unrelated causes.

expo-ui SwiftUI ViewModifier Record must have matching ViewModifierRegistry entry

In expo-ui, a new or renamed SwiftUI ViewModifier Record must have a matching register("<jsModifierName>") entry in ViewModifierRegistry.swift, and the registered key must agree with the TypeScript name. Also flag an ExpoSwiftUIView props declaration missing @ObservedObject.

expo-ui modifier should compose from JavaScript-side modifiers, not add native ones

A modifier implemented natively in expo-ui that could compose from existing JavaScript-side modifiers is a code organization issue. The repo prefers extending packages/expo-ui/src/swift-ui/modifiers/ over adding native modifiers.

Precedence chain returning early on resolvable but unusable value is a defect

A precedence chain that returns early on a value that resolves but is not usable is a defect. For example, UTType("public.image") resolves fine and has no preferredFilenameExtension, so a perfectly good mimeType fallback never runs. Select on usability, not mere resolvability.

Give your agent this brain