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-code-splitting.md

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

Skill: Remote Code Loading

Set up code splitting with Re.Pack for on-demand bundle loading.

Quick Pattern

Before (static import):

import SettingsScreen from './screens/SettingsScreen';

After (lazy loaded chunk):

const SettingsScreen = React.lazy(() =>
  import(/* webpackChunkName: "settings" */ './screens/SettingsScreen')
);

<Suspense fallback={<Loading />}>
  <SettingsScreen />
</Suspense>

When to Use

Consider code splitting when:

  • Not using Hermes (JSC/V8 benefits more)
  • App size exceeds 200 MB (Play Store limit)
  • Building micro-frontend architecture
  • Loading features based on user permissions
  • Other optimizations exhausted

Note: Hermes already uses memory mapping for efficient bundle reading. Benefits of code splitting are minimal with Hermes or even counterproductive in some cases.

Prerequisites

  • Re.Pack installed (replaces Metro)
npx @callstack/repack-init

Step-by-Step Instructions

1. Initialize Re.Pack

npx @callstack/repack-init

Follow prompts to migrate from Metro. Check migration guide.

2. Create Split Point with React.lazy

// BEFORE: Static import
import SettingsScreen from './screens/SettingsScreen';

// AFTER: Dynamic import (creates split point)
const SettingsScreen = React.lazy(() =>
  import(/* webpackChunkName: "settings" */ './screens/SettingsScreen')
);

3. Wrap with Suspense

import React, { Suspense } from 'react';

const App = () => {
  return (
    <Suspense fallback={<LoadingSpinner />}>
      <SettingsScreen />
    </Suspense>
  );
};

4. Configure Chunk Loading

// index.js (before AppRegistry)
import { ScriptManager, Script } from '@callstack/repack/client';

ScriptManager.shared.addResolver((scriptId) => ({
  url: __DEV__
    ? Script.getDevServerURL(scriptId)  // Dev server
    : `https://my-cdn.com/assets/${scriptId}`,  // Production CDN
}));

AppRegistry.registerComponent(appName, () => App);

5. Build and Deploy Chunks

Build generates:

  • index.bundle - Main bundle
  • settings.chunk.bundle - Lazy-loaded chunk

Deploy chunks to your CDN at configured URL.

Complete Example

// App.tsx
import React, { Suspense, useState } from 'react';
import { Button, View, ActivityIndicator } from 'react-native';

// Lazy load heavy feature
const HeavyFeature = React.lazy(() =>
  import(/* webpackChunkName: "heavy-feature" */ './HeavyFeature')
);

const App = () => {
  const [showFeature, setShowFeature] = useState(false);
  
  return (
    <View>
      <Button 
        title="Load Feature" 
        onPress={() => setShowFeature(true)} 
      />
      
      {showFeature && (
        <Suspense fallback={<ActivityIndicator />}>
          <HeavyFeature />
        </Suspense>
      )}
    </View>
  );
};

Module Federation (Advanced)

For micro-frontend architecture:

// Host app loads remote module
const RemoteModule = React.lazy(() =>
  import('remote-app/Module')
);

Enables:

  • Independent team deployments
  • Shared dependencies
  • Runtime composition

Complexity warning: Only use when organizational benefits outweigh overhead.

Version Management

Consider Zephyr Cloud for:

  • Sub-second deployments
  • Version management
  • Re.Pack integration

Caching Strategy

ScriptManager.shared.addResolver((scriptId) => ({
  url: `https://my-cdn.com/${scriptId}`,
  cache: {
    // Enable caching
    enabled: true,
    // Cache location
    path: `${FileSystem.cacheDirectory}/chunks/`,
  },
}));

When NOT to Use

Scenario Why Not
Using Hermes mmap already efficient
Small app Overhead not worth it
Simple navigation Native navigation better
Quick iteration needed Added complexity

Hermes Memory Mapping

Hermes reads bytecode lazily via mmap:

  • Only loads executed code into memory
  • No parse step needed
  • Code splitting provides marginal benefit

Verification

// Check if chunk loaded correctly
ScriptManager.shared.on('loading', (scriptId) => {
  console.log(`Loading: ${scriptId}`);
});

ScriptManager.shared.on('loaded', (scriptId) => {
  console.log(`Loaded: ${scriptId}`);
});

ScriptManager.shared.on('error', (scriptId, error) => {
  console.error(`Failed: ${scriptId}`, error);
});

Common Pitfalls

  • Forgetting Suspense: Lazy components need fallback
  • Wrong CDN path: Chunks 404 in production
  • No caching: Re-downloads on every load
  • Too many chunks: Network overhead exceeds savings

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