All skills
callstackincubator avatar

/react-native-best-practices

@fa0bad0 official

Provides React Native performance optimization guidelines for FPS, TTI, bundle size, memory leaks, re-renders, and animations. Applies to tasks involving Hermes optimization, JS thread blocking, bridge overhead, FlashList, native modules, or debugging jank and frame drops.

Use this Skill: https://skilld.dev/gh/callstackincubator/agent-skills/react-native-best-practices

This session only. Nothing lands on disk.

referencesbundle-barrel-exports.md

≈1.4k tokens on demand. Your agent reads this file only when SKILL.md points to it.

Skill: Avoid Barrel Exports

Refactor barrel imports (index files) to reduce bundle size and improve startup time.

Quick Pattern

Incorrect:

import { Button } from './components';
// Loads ALL exports from components/index.ts

Correct:

import Button from './components/Button';
// Loads only Button

When to Use

  • Bundle contains unused code from libraries
  • Circular dependency warnings in Metro
  • Hot Module Replacement (HMR) breaks frequently
  • TTI is slow due to module evaluation

What Are Barrel Exports?

// components/index.ts (barrel file)
export { Button } from './Button';
export { Card } from './Card';
export { Modal } from './Modal';
export { Sidebar } from './Sidebar';

// Usage (barrel import)
import { Button } from './components';

Problems with Barrel Imports

1. Bundle Size Overhead

Without effective tree shaking or a library-specific Babel plugin, barrel imports can make all re-exported modules reachable and evaluated even when only one export is used:

// Only need Button, but entire barrel is bundled
import { Button } from './components';
// Card, Modal, Sidebar also included!

2. Runtime Overhead

All modules evaluate before returning your import:

import { Button } from './components';
// JavaScript must evaluate:
// - Button.tsx
// - Card.tsx
// - Modal.tsx  
// - Sidebar.tsx
// Even though you only use Button

3. Circular Dependencies

Barrel files make cycles easier to create accidentally:

Warning: Require cycle:
  components/index.ts -> Button.tsx -> utils/index.ts -> components/index.ts

Breaks HMR, causes unpredictable behavior.

Solution 1: Direct Imports

Replace barrel imports with direct paths:

// BEFORE: Barrel import
import { Button, Card } from './components';

// AFTER: Direct imports
import Button from './components/Button';
import Card from './components/Card';

Enforce with ESLint

npm install -D eslint-plugin-no-barrel-files
// eslint.config.js
import noBarrelFiles from 'eslint-plugin-no-barrel-files';

export default [
  {
    plugins: { 'no-barrel-files': noBarrelFiles },
    rules: {
      'no-barrel-files/no-barrel-files': 'error',
    },
  },
];

Solution 2: Tree Shaking (Automatic)

Enable tree shaking to automatically remove unused barrel exports.

Expo SDK 52+

// metro.config.js
const { getDefaultConfig } = require('expo/metro-config');
const config = getDefaultConfig(__dirname);

config.transformer.getTransformOptions = async () => ({
  transform: {
    experimentalImportSupport: true,
  },
});

module.exports = config;
# .env
EXPO_UNSTABLE_METRO_OPTIMIZE_GRAPH=1
EXPO_UNSTABLE_TREE_SHAKING=1

metro-serializer-esbuild

npm install @rnx-kit/metro-serializer-esbuild

Re.Pack (Webpack/Rspack)

Tree shaking built-in.

Real-World Example: date-fns

// BAD: Imports entire library
import { format, addDays, isToday } from 'date-fns';

// GOOD: Direct imports
import format from 'date-fns/format';
import addDays from 'date-fns/addDays';
import isToday from 'date-fns/isToday';

If the project uses a bundler/configuration with working tree shaking, top-level ESM imports from libraries such as date-fns may be optimized automatically. Without that, submodule imports are still the safer pattern. Measure with bundle analysis.

Library-Specific Solutions

Some libraries provide Babel plugins:

React Native Paper

// babel.config.js
module.exports = {
  plugins: [
    'react-native-paper/babel',  // Auto-transforms imports
  ],
};

Transforms:

import { Button } from 'react-native-paper';
// Into:
import Button from 'react-native-paper/lib/module/components/Button';

Refactoring Strategy

Step 1: Identify Barrel Files

Look for index.ts files with multiple exports:

grep -r "export \* from" src/
grep -r "export { .* } from" src/

Step 2: Update Imports

// Find all usages
// VS Code: Cmd+Shift+F for "from './components'"

// Replace each with direct import
import Button from './components/Button';

Step 3: (Optional) Keep Barrel for External API

If your package is consumed by others:

// Keep index.ts for package API
// components/index.ts
export { Button } from './Button';

// Internal code uses direct imports
// src/screens/Home.tsx
import Button from '../components/Button';

Migration Script Example

# Use codemod or search-replace
# Find: import { (\w+) } from '\.\/components';
# Replace: import $1 from './components/$1';

Verification

After refactoring:

  1. Run bundle analysis (see bundle-analyze-js.md)
  2. Compare sizes before/after
  3. Check for circular dependency warnings

Common Pitfalls

  • Breaking external consumers: If publishing a library, keep barrel for public API
  • IDE auto-imports: Configure IDE to prefer direct imports
  • Inconsistent patterns: Enforce with ESLint across team

Related Skills

Source: SKILL.md on GitHub

1 alert17d5 checks · Risk SAFE
  • Gen Agent Trust Hub17d

    This skill provides a comprehensive performance optimization guide for React Native applications. It contains standard developer workflows using command-line tools and patterns for advanced techniques like remote code splitting, which include detailed security hardening advice.

  • Socket17d

    No alerts

  • Snyk17d

    Risk: LOW · No issues

  • Runlayer7mo

    11/31 files flagged

  • ZeroLeaks5mo

    Score: 93/100 · 2 sections analyzed

Signed by skilld at fa0bad0. This ties the file your Agent reads to that commit on GitHub. It does not review the instructions.

Last checked against GitHub 2 weeks ago.

Activeupdated 2 months ago
  • Performance
  • react-native
  • optimization
  • profiling
  • fps
  • bundle-size
  • memory
  • hermes
  • flatlist
  • animations

README badge

README badge for callstackincubator/agent-skills/react-native-best-practices

Provides React Native performance optimization guidelines covering FPS, TTI, bundle size, memory leaks, re-renders, and animations. Includes reference documentation for Hermes, FlashList, Turbo Modules, React Compiler, Reanimated, and profiling with Xcode and Android Studio.

Generated from the current SKILL.md.

Does this skill cover Expo apps?
Yes. The guidelines apply to both bare React Native and Expo projects. Expo SDK 52+ supports tree shaking and Re.Pack code splitting.
What performance metrics should I measure first?
Start with FPS (use React Native DevTools), Time To Interactive (TTI), bundle size, and memory growth. The skill emphasizes measuring baseline metrics before and after optimization to validate improvements.
Does this cover Hermes optimization?
Yes. The skill includes Hermes-specific guidance such as disabling JS bundle compression on Android to enable mmap, and checking Hermes API coverage before removing polyfills.
What if I'm using a list library other than FlatList or FlashList?
The skill prioritizes FlatList and FlashList as virtualized replacements for ScrollView. It includes library version checks (e.g., FlashList v2 API changes) but focuses on these two libraries.
Does this include native profiling guidance?
Yes. The skill covers profiling with Xcode Instruments (iOS) and Android Studio CPU Profiler, plus guidance on native threading, Turbo Modules, and C++ performance-critical code.

Generated from the current SKILL.md. These answers refresh after source changes.