All skills

Complete React Native and Expo optimization guide combining Callstack profiling with Vercel code patterns. Covers FPS, TTI, bundle size, memory, lists, animations, state, UI, and React Compiler. Use for any React Native performance, debugging, or best practices task.

Use this Skill: https://skilld.dev/gh/gigs-slc/react-native-skills/react-native

This session only. Nothing lands on disk.

referencesbundle-barrel-exports.md

≈1.3k 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

Metro includes all exports even if you use one:

// 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';

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

2 alerts6mo4 checks · Risk HIGH
  • Gen Agent Trust Hub7mo

    The skill is a technical React Native guide that includes a high-risk remote code execution pattern for installing a performance tool.

  • Socket6mo

    No alerts

  • Snyk7mo

    Risk: MEDIUM · No issues

  • Runlayer7mo

    7/67 files flagged

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

Last checked against GitHub 2 days ago.

Dormantupdated 8 months ago
Other metadata
metadata
{
  "author": "Callstack + Vercel",
  "version": "1.0.0",
  "tags": "react-native, expo, performance, optimization, patterns"
}

README badge

README badge for gigs-slc/react-native-skills/react-native