Nativescript Skill
NativeScript Best Practices
Code Style and Structure
-
Organize code using modular components and services for maintainability.
-
Use platform-specific files (.ios.ts , .android.ts ) when code exceeds 20 platform-specific lines.
-
When creating custom native code, use a folder structure like custom-native/index.ios.ts , custom-native/index.android.ts , custom-native/common.ts , custom-native/index.d.ts to keep platform-specific code organized and easy to import with single import elsewhere, replacing custom-native with the name of the custom code.
Naming Conventions
-
Prefix platform-specific variables with ios or android (e.g., iosButtonStyle ).
-
Name custom components and styles descriptively (primaryButtonStyle , userProfileView ).
Usage
-
Use @NativeClass() when extending native classes when needed
-
For iOS, when extending native classes, always use static ObjCProtocols = [AnyUIKitDelegate]; to declare custom delegates if a delegate is required or used.
-
For iOS, always retain custom delegate instances to prevent garbage collection. For example, let delegate = MyCustomDelegate.new() as MyCustomDelegate , and ensure it is retained in the class scope.
-
Favor ANDROID and APPLE for conditional platform code with tree-shaking.
-
Track and clean up all timers (setTimeout , setInterval ) to avoid memory leaks.
UI and Styling
-
Always TailwindCSS as the CSS Framework using "@nativescript/tailwind": "^2.1.0" for consistent styling paired with "tailwindcss": "~3.4.0" .
-
Add ios: and android: style variants for platform-specific styling, addVariant('android', '.ns-android &'), addVariant('ios', '.ns-ios &');
-
darkMode: ['class', '.ns-dark']
-
Leverage GridLayout or StackLayout for flexible, responsive layouts. Place more emphasis on proper GridLayout usage for complex layouts but use StackLayout for simpler, linear arrangements.
-
Use visibility: 'hidden' for elements that should not affect layout when hidden.
Performance Optimization
-
Try to avoid deeply nesting layout containers but instead use GridLayout wisely to setup complex layouts.
-
Avoid direct manipulation of the visual tree during runtime to minimize rendering overhead.
-
Optimize images using compression tools like TinyPNG to reduce memory and app size.
-
Clean the project (ns clean ) after modifying files in App_Resources or package.json .
Key Conventions
-
Reuse components and styles to avoid duplication.
-
Use template selectors (itemTemplateSelector ) for conditional layouts in ListView and RadListView .
-
Minimize heavy computations in UI bindings or methods.
-
Only if using plain xml bindings, use Observable or ObservableArray properties to reflect state changes efficiently.
-
When using Angular, React, Solid, Svelte or Vue, always leverage their respective state management, lifecycle hooks, rendering optimizations and reactive bindings for optimal performance.
Iron Laws
-
ALWAYS use platform-specific files (.ios.ts , .android.ts ) when platform code exceeds 20 lines — mixing platform branches in a single file prevents code-splitting and forces all native APIs to load on both platforms.
-
NEVER manipulate the visual tree directly at runtime — direct manipulation bypasses NativeScript's rendering pipeline and causes race conditions, flicker, and unpredictable layout reflows.
-
ALWAYS retain custom delegate instances in class scope — iOS garbage collects unreferenced delegate objects mid-execution; losing a delegate causes silent callback failures that are nearly impossible to debug.
-
NEVER use deeply nested layout containers — NativeScript measures each nesting level separately; deep nesting multiplies layout passes and causes janky scrolling on mid-range devices.
-
ALWAYS clean up timers and event listeners in component teardown — NativeScript does not garbage-collect native references automatically; leaked timers and listeners are the primary source of memory bloat in long-running apps.
Anti-Patterns
Anti-Pattern Why It Fails Correct Approach
Mixing platform branches in a single file Cannot code-split by platform; all native APIs loaded on both platforms Use .ios.ts and .android.ts files when platform-specific code exceeds 20 lines
Direct visual tree manipulation at runtime Bypasses rendering pipeline; causes race conditions and layout flicker Use data-binding and reactive state to drive layout updates
Unreferenced delegate instances iOS GC collects them mid-execution; silent callback failures Always retain delegates in class scope: this.delegate = MyDelegate.new()
Deeply nested layout containers Each level adds layout passes; janky scrolling on mid-range devices Use GridLayout for complex layouts; StackLayout only for simple linear arrangements
Leaking timers and event listeners NativeScript does not GC native references; leaks accumulate across navigation Always cancel timers and remove listeners in component teardown lifecycle
Memory Protocol (MANDATORY)
Before starting:
cat .claude/context/memory/learnings.md
After completing: Record any new patterns or exceptions discovered.
ASSUME INTERRUPTION: Your context may reset. If it's not in memory, it didn't happen.