Skip to main content

react-native-rendering

Optimizes React Native component rendering through virtualization, memoization, image optimization, and efficient component architecture for mobile performance.

Ir a la instalación

Datos de origen

Repositorio
paulpas/agent-skill-router
Última actividad en el origen
9 de junio de 2026 a las 16:28
Idioma detectado de SKILL.md
inglés
Estrellas
4
Forks
1

Opciones de instalación

De forma predeterminada está seleccionado el prompt que primero revisa el origen. Puedes cambiar a un comando directo o descargar una copia local.

Revisa los archivos de origen

Lee SKILL.md y los archivos complementarios que muestra SkillsMP antes de decidir si quieres instalarlo.

Mostrando SKILL.md

SKILL.md
Instrucciones de origen · Vista previa de solo lectura
name
react-native-rendering
description
Optimizes React Native component rendering through virtualization, memoization, image optimization, and efficient component architecture for mobile performance.
license
MIT
compatibility
opencode
archetypes
["tactical","generation"]
anti_triggers
["brainstorming","vague ideation","long-form architecture"]
response_profile
{"verbosity":"low","directive_strength":"high","abstraction_level":"operational"}
metadata
{"version":"1.0.0","domain":"coding","triggers":"react native rendering, component optimization, native performance, image optimization, react memo, view nesting, flipper profiling","role":"implementation","scope":"implementation","output-format":"code","content-types":["code","guidance","do-dont","examples"],"related-skills":"react-native-state-management, vercel-optimize","author":"https://github.com/vercel-labs","source":"https://github.com/vercel-labs/agent-skills"}
# React Native Rendering Optimization A senior React Native performance engineer who optimizes component rendering through virtualization, memoization, image caching, view hierarchy flattening, and deferred rendering — targeting 60fps on low-end devices. ## TL;DR Checklist - [ ] Profile with React DevTools or Flipper before optimizing — measure, don't guess - [ ] Wrap pure presentational components with React.memo and specify arePropsEqual if needed - [ ] Use FastImage for remote image caching (never Image for remote URLs) - [ ] Pre-size images with Image.getSize to prevent layout shift - [ ] Replace deep View nesting with Fragments or flattened layouts - [ ] Defer non-critical rendering with InteractionManager.runAfterInteractions - [ ] Use FlashList or RecyclerViewList for long scrollable lists - [ ] Create all styles outside render using StyleSheet.create() --- ## When to Use Use this skill when: - A screen takes too long to render or navigates with a visible delay - A FlatList stutters during fast scrolling - Remote images cause layout jumps or flicker on slow connections - Profiling with React DevTools shows excessive re-renders of the same component tree - A component tree has more than 20 nested View elements - Animations drop frames on mid-to-low-end Android devices - The app bundle includes heavy screens that are rarely visited --- ## When NOT to Use Avoid this skill for: - Initial app prototyping where premature optimization slows iteration - Simple screens with fewer than 5 components and no images — React Native is already fast enough - Apps targeting only recent flagship devices with ample memory - Before profiling — always measure first to identify the real bottleneck --- ## Core Workflow 1. **Profile to Find Bottlenecks** — Use React DevTools Profiler or Flipper to identify: - Components that re-render when their props haven't changed - Long render commits (>16ms) on the JS thread - Images causing layout reflow - Deep view hierarchies (measure with `Why Did You Render` or custom metrics) **Checkpoint:** Record a baseline FPS and render commit duration before making any changes. 2. **Memoize Pure Components** — Wrap components that render the same output for the same props: - Use `React.memo()` for functional components - Provide a custom `arePropsEqual` only if default shallow comparison is too loose - Never memoize components that depend on context or frequently-changing global state **Checkpoint:** Verify with the profiler that the component no longer re-renders on unrelated state changes. 3. **Optimize Images** — Address the most common performance killer in React Native: - Replace `Image` with `FastImage` for remote URLs (disk caching, priority, preloading) - Pre-size images using `Image.getSize()` to prevent layout shifts - Resize images server-side to the maximum display size — never download full-resolution photos - Use WebP format on Android (enabled by default in FastImage) **Checkpoint:** Check that image-heavy screens load without layout jumps and cached images appear instantly on revisit. 4. **Flatten the View Hierarchy** — Reduce rendering overhead: - Replace `<View>` wrappers with `<>` Fragments where possible - Inline simple conditional rendering with `{condition && <Component/>}` instead of wrapping in View - Use `PointerEvent`-free containers where touch handling is not needed - Keep View nesting below 25 levels on any single screen **Checkpoint:** Measure the resulting view tree depth with the Flipper layout inspector. 5. **Defer Non-Critical Rendering** — Prioritize what the user sees first: - Use `InteractionManager.runAfterInteractions()` for analytics tracking, logging, prefetching - Lazy load heavy screens with `React.lazy()` or dynamic imports - Implement progressive loading: skeleton/placeholder → critical content → secondary content **Checkpoint:** Verify that screen transitions feel instant (under 300ms) even with heavy content below the fold. --- ## Implementation Patterns ### Pattern 1: React.memo with Custom Comparison ```tsx import React, { memo } from 'react'; import { View, Text, StyleSheet } from 'react-native'; interface CryptoRowProps { symbol: string; price: number; change24h: number; volume: number; } // ❌ BAD: Renders on every parent re-render even if props haven't changed function CryptoRow({ symbol, price, change24h, volume }: CryptoRowProps) { return ( <View style={styles.row}> <Text style={styles.symbol}>{symbol}</Text> <Text style={styles.price}>${price.toFixed(2)}</Text> <Text style={[styles.change, { color: change24h >= 0 ? 'green' : 'red' }]}> {change24h.toFixed(2)}% </Text> </View> ); } // ✅ GOOD: Memoized with custom equality — only re-renders when displayed data changes const MemoizedCryptoRow = memo(CryptoRow, (prev, next) => { return ( prev.symbol === next.symbol && prev.price === next.price && prev.change24h === next.change24h // volume is omitted — we don't re-render when only volume changes ); }); // ✅ GOOD: For simple props, default shallow comparison suffices const SimpleRow = memo(({ label, value }: { label: string; value: string }) => ( <View style={styles.row}> <Text>{label}</Text> <Text>{value}</Text> </View> )); const styles = StyleSheet.create({ row: { flexDirection: 'row', padding: 12, alignItems: 'center' }, symbol: { fontWeight: 'bold', fontSize: 16, flex: 1 }, price: { fontSize: 16, flex: 1, textAlign: 'right' }, change: { fontSize: 14, flex: 0.5, textAlign: 'right' }, }); ``` ### Pattern 2: Image Optimization with FastImage ```tsx // ❌ BAD: Using built-in Image for remote URLs — no caching, no preloading, layout shift import { Image, ImageStyle } from 'react-native'; function Avatar({ uri, size }: { uri: string; size: number }) { return ( <Image source={{ uri }} style={{ width: size, height: size, borderRadius: size / 2 } as ImageStyle} /> ); } // ✅ GOOD: FastImage with disk caching, priority, and pre-sizing import FastImage, { FastImageProps, Source, Priority } from 'react-native-fast-image'; function OptimizedAvatar({ uri, size }: { uri: string; size: number }) { return ( <FastImage style={{ width: size, height: size, borderRadius: size / 2 }} source={{ uri, priority: Priority.normal, cache: FastImage.cacheControl.immutable, // Never re-fetch — avatar URLs are unique } as Source} resizeMode={FastImage.resizeMode.cover} /> ); } // ✅ GOOD: Pre-size images to prevent layout shift import { useState, useEffect } from 'react'; import { View, Dimensions } from 'react-native'; const SCREEN_WIDTH = Dimensions.get('window').width; function SizedImage({ uri }: { uri: string }) { const [dimensions, setDimensions] = useState<{ width: number; height: number } | null>(null); useEffect(() => { // Get remote image dimensions before rendering Image.getSize( uri, (width, height) => { const scaledWidth = SCREEN_WIDTH - 32; // 16px padding on each side const scaledHeight = (height / width) * scaledWidth; setDimensions({ width: scaledWidth, height: scaledHeight }); }, () => { // Fallback aspect ratio on error setDimensions({ width: SCREEN_WIDTH - 32, height: (SCREEN_WIDTH - 32) * 0.75 }); } ); }, [uri]); if (!dimensions) { // Show placeholder while dimensions load return <View style={{ width: SCREEN_WIDTH - 32, height: 200, backgroundColor: '#e0e0e0' }} />; } return ( <FastImage style={{ width: dimensions.width, height: dimensions.height }} source={{ uri, priority: Priority.high }} resizeMode={FastImage.resizeMode.contain} /> ); } ``` ### Pattern 3: InteractionManager for Deferred Rendering ```tsx import { InteractionManager } from 'react-native'; // ❌ BAD: Blocking the navigation transition with heavy non-critical work function HeavyDashboard() { const [analytics, setAnalytics] = useState<AnalyticsData | null>(null); useEffect(() => { // This runs synchronously, delaying the screen transition loadAnalytics().then(setAnalytics); }, []); // ... } // ✅ GOOD: Defer non-critical work until after navigation transition function OptimizedDashboard() { const [analytics, setAnalytics] = useState<AnalyticsData | null>(null); useEffect(() => { // Wait for navigation transition and user-perceivable rendering to complete const task = InteractionManager.runAfterInteractions(() => { loadAnalytics().then(setAnalytics); }); return () => task.cancel(); }, []); // ... } // ✅ GOOD: Prioritize rendering — show skeleton, then content, then secondary function ProgressiveDashboard() { const [phase, setPhase] = useState<'skeleton' | 'content' | 'secondary'>('skeleton'); useEffect(() => { // Phase 1: Show skeleton immediately (instant render) requestAnimationFrame(() => setPhase('content')); }, []); useEffect(() => { if (phase === 'content') { // Phase 2: After content renders, defer secondary data loading InteractionManager.runAfterInteractions(() => { setPhase('secondary'); }); } }, [phase]); if (phase === 'skeleton') return <SkeletonLoader />; if (phase === 'content') return <MainFeed />; return <FullDashboard />; // Includes analytics, recommendations, etc. } ``` ### Pattern 4: View Nesting Anti-Pattern ```tsx // ❌ BAD: Deep view nesting (6 levels for a simple avatar row) function BadAvatarRow({ name, avatarUrl }: { name: string; avatarUrl: string }) { return ( <View style={outerStyles.container}> <View style={outerStyles.avatarWrap}> <View style={outerStyles.avatarInner}> <FastImage source={{ uri: avatarUrl }} style={outerStyles.img} /> </View> </View> <View style={outerStyles.textWrap}> <View style={outerStyles.nameWrap}> <Text style={outerStyles.name}>{name}</Text> </View> </View> </View> ); } // ✅ GOOD: Flattened hierarchy — 2 levels instead of 6 function OptimizedAvatarRow({ name, avatarUrl }: { name: string; avatarUrl: string }) { return ( <View style={flatStyles.container}> <FastImage source={{ uri: avatarUrl }} style={flatStyles.avatar} /> <Text style={flatStyles.name}>{name}</Text> </View> ); } const flatStyles = StyleSheet.create({ container: { flexDirection: 'row', alignItems: 'center', padding: 12, }, avatar: { width: 44, height: 44, borderRadius: 22, }, name: { fontSize: 16, marginLeft: 12, flex: 1, }, }); ``` --- ## Constraints ### MUST DO - Profile with React DevTools or Flipper before and after every optimization — verify improvement - Use `React.memo()` for all pure presentational components in lists - Use `FastImage` for all remote image URLs — never use built-in `Image` for external sources - Pre-size images with `Image.getSize()` or aspect ratio metadata to prevent layout shifts - Use `StyleSheet.create()` outside the render function — never inline style objects - Use `InteractionManager.runAfterInteractions()` for non-critical work (analytics, logging, prefetching) - Use `Fragment` (`<>...</>`) instead of `<View>` wrappers where no styling is needed - Use `FlashList` from `@shopify/flash-list` instead of `FlatList` for lists with 50+ items ### MUST NOT DO - Use inline styles in the render function (creates new object instances every render) - Nest Views more than 30 levels deep — flatten the hierarchy - Re-render an entire screen for a small, localized state change — use local component state - Download full-resolution images from servers — resize server-side to max display size - Block navigation transitions with synchronous data loading - Use `React.memo` on components that depend on context or frequently-changing global state - Prematurely optimize without profiling — measure the actual bottleneck first --- ## Related Skills | Skill | Purpose | |---|---| | `react-native-state-management` | Complements rendering optimization by reducing re-renders from global state | | `vercel-optimize` | Extends optimization to the backend — CDN caching, cold starts, bundle analysis | --- ## Live References > Authoritative documentation links for React Native rendering optimization. - [React Native Performance Overview](https://reactnative.dev/docs/performance) — Official performance guide - [React DevTools Profiler](https://react.dev/learn/react-developer-tools) — Profile component re-renders - [FastImage GitHub](https://github.com/DylanVann/react-native-fast-image) — Performant image caching for React Native - [FlashList by Shopify](https://shopify.github.io/flash-list/) — High-performance list replacement for FlatList - [InteractionManager Docs](https://reactnative.dev/docs/interactionmanager) — Defer work after interactions - [Flipper Mobile Debugger](https://fbflipper.com/) — Profile layout, network, and JS thread performance - [React Native StyleSheet.create()](https://reactnative.dev/docs/stylesheet) — Optimized style creation - [Why Did You Render](https://github.com/welldone-software/why-did-you-render) — Detect unnecessary re-renders
Ver en GitHub