Documentation Index
Fetch the complete documentation index at: https://mintlify.com/software-mansion/react-native-executorch/llms.txt
Use this file to discover all available pages before exploring further.
Overview
Model loading is the process of downloading resources, preparing native modules, and loading ExecuTorch .pte model files into memory. This guide covers the complete loading lifecycle from resource fetching to ready-for-inference state.
Loading Lifecycle
Basic Loading Pattern
All modules follow this pattern:
import { ExecutorchModule } from 'react-native-executorch';
const module = new ExecutorchModule();
// Load model with progress tracking
await module.load(
'https://example.com/model.pte',
(progress) => {
console.log(`Loading: ${Math.round(progress * 100)}%`);
}
);
// Module is now ready for inference
const output = await module.forward(inputTensors);
// Clean up when done
module.delete();
Module-Specific Loading
Different modules require different resources:
ExecutorchModule (Generic)
Simplest case - just a model binary:
import { ExecutorchModule } from 'react-native-executorch';
const module = new ExecutorchModule();
await module.load(
modelSource,
(progress) => console.log(progress)
);
Location: ~/workspace/source/packages/react-native-executorch/src/modules/general/ExecutorchModule.ts:22-42
Implementation:
async load(
modelSource: ResourceSource,
onDownloadProgressCallback: (progress: number) => void = () => {}
): Promise<void> {
try {
// Fetch model file
const paths = await ResourceFetcher.fetch(
onDownloadProgressCallback,
modelSource
);
if (!paths?.[0]) {
throw new RnExecutorchError(
RnExecutorchErrorCode.DownloadInterrupted,
'Download was interrupted. Please retry.'
);
}
// Load into native memory via JSI
this.nativeModule = global.loadExecutorchModule(paths[0]);
} catch (error) {
Logger.error('Load failed:', error);
throw parseUnknownError(error);
}
}
VisionModule (Image Models)
Vision models need only the model file:
import { ClassificationModule } from 'react-native-executorch';
const classifier = new ClassificationModule();
await classifier.load(
'https://example.com/mobilenet_v3.pte',
(progress) => setProgress(progress)
);
// Ready for inference
const result = await classifier.forward('file:///path/to/image.jpg');
LLMModule (Language Models)
LLMs require multiple resources:
import { LLMModule } from 'react-native-executorch';
const llm = new LLMModule({
tokenCallback: (token) => console.log(token),
messageHistoryCallback: (history) => setMessages(history)
});
await llm.load(
{
modelSource: 'https://example.com/model.pte',
tokenizerSource: 'https://example.com/tokenizer.json',
tokenizerConfigSource: 'https://example.com/tokenizer_config.json'
},
(progress) => setProgress(progress)
);
Location: ~/workspace/source/packages/react-native-executorch/src/modules/natural_language_processing/LLMModule.ts:49-66
OCRModule (Text Recognition)
OCR needs detector, recognizer, and symbols:
import { OCRModule } from 'react-native-executorch';
import { OCRLatinSymbols } from 'react-native-executorch';
const ocr = new OCRModule();
await ocr.load(
{
detectorSource: 'https://example.com/detector.pte',
recognizerSource: 'https://example.com/recognizer.pte',
symbols: OCRLatinSymbols
},
(progress) => setProgress(progress)
);
TextToImageModule (Stable Diffusion)
Most complex - multiple models:
import { TextToImageModule } from 'react-native-executorch';
const tti = new TextToImageModule();
await tti.load(
{
tokenizerSource: 'https://example.com/tokenizer.json',
encoderSource: 'https://example.com/text_encoder.pte',
unetSource: 'https://example.com/unet.pte',
decoderSource: 'https://example.com/vae_decoder.pte',
schedulerConfig: {
betaStart: 0.00085,
betaEnd: 0.012,
numTrainTimesteps: 1000,
stepsOffset: 1
}
},
(progress) => setProgress(progress)
);
Download Progress Tracking
Single Resource
Progress from 0 to 1:
const [progress, setProgress] = useState(0);
const [status, setStatus] = useState('Starting...');
await module.load(
modelSource,
(downloadProgress) => {
setProgress(downloadProgress);
if (downloadProgress === 0) {
setStatus('Connecting...');
} else if (downloadProgress < 1) {
setStatus(`Downloading ${Math.round(downloadProgress * 100)}%`);
} else {
setStatus('Initializing model...');
}
}
);
setStatus('Ready!');
Multiple Resources
Progress is weighted by file size:
// Example: LLM with 3 files
// - model.pte: 1GB (80% of total)
// - tokenizer.json: 200MB (16% of total)
// - config.json: 50MB (4% of total)
await llm.load(
{
modelSource: largeModel, // 1GB
tokenizerSource: tokenizer, // 200MB
tokenizerConfigSource: config // 50MB
},
(progress) => {
// Progress breakdown:
// 0.0 - 0.8: Downloading model.pte
// 0.8 - 0.96: Downloading tokenizer.json
// 0.96 - 1.0: Downloading config.json
console.log(`Overall: ${Math.round(progress * 100)}%`);
}
);
Implementation in ResourceFetcherUtils:
Location: ~/workspace/source/packages/react-native-executorch/src/utils/ResourceFetcherUtils.ts:155-181
export function calculateDownloadProgress(
totalLength: number,
previousFilesTotalLength: number,
currentFileLength: number,
setProgress: (downloadProgress: number) => void
) {
return (progress: number) => {
if (progress === 1 &&
previousFilesTotalLength === totalLength - currentFileLength) {
setProgress(1);
return;
}
if (totalLength === 0) {
setProgress(0);
return;
}
const baseProgress = previousFilesTotalLength / totalLength;
const scaledProgress = progress * (currentFileLength / totalLength);
const updatedProgress = baseProgress + scaledProgress;
setProgress(updatedProgress);
};
}
Native Module Initialization
After resources are downloaded, native modules are created via JSI:
Global JSI Functions
These functions are installed at app startup:
declare global {
// Generic model
var loadExecutorchModule: (source: string) => any;
// Vision models
var loadClassification: (source: string) => any;
var loadObjectDetection: (
source: string,
normMean: Triple<number> | [],
normStd: Triple<number> | [],
labelNames: string[]
) => any;
var loadSemanticSegmentation: (
source: string,
normMean: Triple<number> | [],
normStd: Triple<number> | [],
allClasses: string[]
) => any;
// NLP models
var loadLLM: (
modelSource: string,
tokenizerSource: string
) => any;
var loadTokenizerModule: (source: string) => any;
// ... more model loaders
}
Location: ~/workspace/source/packages/react-native-executorch/src/index.ts:36-92
Installation
JSI functions are installed by the native module:
if (
global.loadExecutorchModule == null ||
global.loadClassification == null ||
// ... check all functions
) {
if (!ETInstallerNativeModule) {
throw new Error(
`Failed to install react-native-executorch: Native module not found.`
);
}
ETInstallerNativeModule.install();
}
Location: ~/workspace/source/packages/react-native-executorch/src/index.ts:94-117
Caching Behavior
Automatic Caching
Downloaded files are cached in the app’s document directory:
{DocumentDirectory}/react-native-executorch/
├── example.com_model.pte
├── example.com_tokenizer.json
├── huggingface.co_model_config.json
└── {hash}.json (for object sources)
Cache Check
Before downloading, the fetcher checks if the file exists:
// Pseudo-code from ResourceFetcher
const filename = getFilenameFromUri(uri);
const localPath = `${RNEDirectory}${filename}`;
if (await fileExists(localPath)) {
// Use cached file, skip download
return localPath;
}
// File not found, download it
const downloadedPath = await downloadFile(uri, localPath);
return downloadedPath;
Subsequent Loads
After first load, subsequent loads are instant:
// First load: Downloads from network (slow)
await module.load(
'https://example.com/model.pte',
(progress) => console.log(progress)
);
// Takes 30 seconds for 1GB model
module.delete();
// Second load: Uses cached file (fast)
const module2 = new ExecutorchModule();
await module2.load(
'https://example.com/model.pte',
(progress) => console.log(progress)
);
// Takes ~2 seconds to load from disk into memory
Error Handling
Common Loading Errors
import { RnExecutorchErrorCode, RnExecutorchError } from 'react-native-executorch';
try {
await module.load(modelSource, (p) => setProgress(p));
} catch (error) {
if (error instanceof RnExecutorchError) {
switch (error.code) {
case RnExecutorchErrorCode.ResourceFetcherAdapterNotInitialized:
console.error('Did you call initExecutorch()?');
break;
case RnExecutorchErrorCode.ResourceFetcherDownloadFailed:
console.error('Download failed:', error.message);
// Retry or show error to user
break;
case RnExecutorchErrorCode.DownloadInterrupted:
console.error('Download was cancelled or paused');
break;
case RnExecutorchErrorCode.InvalidProgram:
console.error('Invalid model file - corrupted or wrong format');
break;
case RnExecutorchErrorCode.MemoryAllocationFailed:
console.error('Not enough memory to load model');
break;
default:
console.error('Unexpected error:', error.code, error.message);
}
}
}
Download Interruption
If download is cancelled or paused, load() throws:
const paths = await ResourceFetcher.fetch(
onDownloadProgressCallback,
modelSource
);
if (!paths?.[0]) {
throw new RnExecutorchError(
RnExecutorchErrorCode.DownloadInterrupted,
'The download has been interrupted. Please retry the download.'
);
}
Location: ~/workspace/source/packages/react-native-executorch/src/modules/general/ExecutorchModule.ts:31-35
Memory Management
Model in Memory
Loaded models reside in native heap:
// Model loaded into native memory
await module.load(modelSource);
// Check memory usage (iOS)
// Settings > App > Memory: +500MB
// JavaScript references native object
console.log(module.nativeModule); // JSI HostObject
Cleanup
Always call delete() when done:
class MyComponent extends React.Component {
module = new ExecutorchModule();
async componentDidMount() {
await this.module.load(modelSource);
}
componentWillUnmount() {
// IMPORTANT: Release native memory
this.module.delete();
}
render() {
// ...
}
}
Or with hooks:
function useModel(modelSource: ResourceSource) {
const moduleRef = useRef<ExecutorchModule | null>(null);
const [isLoaded, setIsLoaded] = useState(false);
useEffect(() => {
const module = new ExecutorchModule();
moduleRef.current = module;
module.load(modelSource).then(() => {
setIsLoaded(true);
});
return () => {
// Cleanup on unmount
module.delete();
};
}, [modelSource]);
return { module: moduleRef.current, isLoaded };
}
Multiple Models
You can load multiple models simultaneously:
const classifier = new ClassificationModule();
const detector = new ObjectDetectionModule();
// Load both in parallel
await Promise.all([
classifier.load(classifierModel),
detector.load(detectorModel)
]);
// Use both
const classification = await classifier.forward(image);
const detections = await detector.forward(image);
// Clean up both
classifier.delete();
detector.delete();
Note: Be mindful of memory constraints on mobile devices.
Preloading Strategies
App Startup
Load critical models at startup:
import { initExecutorch } from 'react-native-executorch';
import { ExpoResourceFetcher } from '@react-native-executorch/expo-resource-fetcher';
export default function App() {
const [isReady, setIsReady] = useState(false);
useEffect(() => {
async function prepare() {
// Initialize resource fetcher
initExecutorch({ resourceFetcher: ExpoResourceFetcher });
// Preload model
const module = new ClassificationModule();
await module.load(CRITICAL_MODEL);
// Store in global state or context
AppState.classifierModule = module;
setIsReady(true);
}
prepare();
}, []);
if (!isReady) {
return <SplashScreen />;
}
return <MainApp />;
}
Background Download
Download large models in background:
import { ResourceFetcher } from 'react-native-executorch';
function useBackgroundModelDownload(modelUrl: string) {
const [progress, setProgress] = useState(0);
const [isDownloaded, setIsDownloaded] = useState(false);
useEffect(() => {
// Start download
ResourceFetcher.fetch(
(p) => setProgress(p),
modelUrl
).then((paths) => {
if (paths) {
setIsDownloaded(true);
}
});
}, [modelUrl]);
return { progress, isDownloaded };
}
// In your component
function ModelDownloader() {
const { progress, isDownloaded } = useBackgroundModelDownload(MODEL_URL);
return (
<View>
{isDownloaded ? (
<Text>Model ready!</Text>
) : (
<ProgressBar value={progress} />
)}
</View>
);
}
Lazy Loading
Load models on-demand:
function Camera() {
const [detector, setDetector] = useState<ObjectDetectionModule | null>(null);
async function enableObjectDetection() {
const module = new ObjectDetectionModule();
await module.load(DETECTOR_MODEL);
setDetector(module);
}
useEffect(() => {
return () => {
detector?.delete();
};
}, [detector]);
return (
<View>
{detector ? (
<CameraWithDetection detector={detector} />
) : (
<Button onPress={enableObjectDetection} title="Enable AI" />
)}
</View>
);
}
Best Practices
- Initialize early: Call
initExecutorch() before any model loading
- Show progress: Always provide progress callbacks for better UX
- Handle errors: Catch and display loading errors appropriately
- Clean up: Call
delete() in cleanup functions (componentWillUnmount, useEffect return)
- Cache awareness: Subsequent loads are fast - don’t block UI unnecessarily
- Memory constraints: Monitor memory usage on low-end devices
- Preload critical models: Load frequently-used models at startup
- Lazy load optional models: Load feature-specific models on-demand
Next Steps