DEV Community

Cover image for Boost Your React Native App Start Time: Stop Shipping Lottie JSON Incorrectly
Davyd NRB
Davyd NRB

Posted on • Edited on

Boost Your React Native App Start Time: Stop Shipping Lottie JSON Incorrectly

I've been analyzing many React Native JavaScript bundles (which you can find on Twitter: https://x.com/tell_me_mur) and have noticed a critical mistake that almost everyone is making. This error significantly impacts your application's App Start Time (AST) and memory usage.

The screenshot below illustrates how the JS bundle is overloaded with numerous i18n locales and Lottie animations (JSON files).

Do you need all locales and animations when the user launches the application? The answer is NO.

You'll only need JSON files at specific moments when using your application. Therefore, it's best to handle them as assets and load them on demand.


How to Load Lottie JSON as Assets On Demand

Move JSON files to the assets directory.

For Android, move all JSON animation files to:
android/app/src/main/assets/*.json

For iOS applications, you can reuse the same files from the Android directory for linking on iOS. To do this, run the following command:

npx react-native-asset --ios-assets android/app/src/main/assets
Enter fullscreen mode Exit fullscreen mode

The files will be successfully added and ready for use. Remember to rebuild your application if you are in development mode.

Read the asset files.

Your project might already have a library for file operations, such as react-native-fs, react-native-blob-util, or react-native-file-access. If not, install one of your choice.

Below are code examples for each of these libraries:

react-native-fs

import { Platform } from 'react-native';
import { readFileRes, readFile, MainBundlePath } from 'react-native-fs';

export async function readAsset(name: string): Promise<string | null> {
  try {
    if (Platform.OS === 'android') {
      const content = await readFileRes(name, 'utf8');
      return content;
    }
    const path = `${MainBundlePath}/${name}`;
    const content = await readFile(path, 'utf8');
    return content;
  } catch (e) {
    console.error('Error reading asset:', e);
    return null;
  }
}

// Usage example
readAsset('animation.json')
  .then(jsonStr => JSON.parse(jsonStr))
  .then(data => {
    // your logic
  });
Enter fullscreen mode Exit fullscreen mode

react-native-blob-util

import RNFetchBlob from 'react-native-blob-util';

export async function readAsset(name: string): Promise<string | null> {
  try {
    const path = RNFetchBlob.fs.asset(name);
    const content = await RNFetchBlob.fs.readFile(path, 'utf8');
    return content;
  } catch (e) {
    console.error('Error reading asset:', e);
    return null;
  }
}

// Usage example
readAsset('animation.json')
  .then(jsonStr => JSON.parse(jsonStr))
  .then(data => {
    // your logic
  });
Enter fullscreen mode Exit fullscreen mode

react-native-file-access

import { Platform } from 'react-native';
import { Dirs, FileSystem } from 'react-native-file-access';

export async function readAsset(name: string): Promise<string | null> {
  try {
    if (Platform.OS === 'android') {
      const tmpPath = Dirs.CacheDir + `/${name}`;
      await FileSystem.cpAsset(`raw/${name}`, tmpPath, 'resource');
      const content = await FileSystem.readFile(tmpPath, 'utf8');
      return content;
    }
    const path = `${Dirs.MainBundleDir}/${name}`;
    const content = await FileSystem.readFile(path, 'utf8');
    return content;
  } catch (e) {
    console.error('Error reading asset:', e);
    return null;
  }
}

// Usage example
readAsset(
  Platform.select({
    ios: 'animation.json',
    android: 'animation', // skip extension for Android
  }),
)
  .then(jsonStr => JSON.parse(jsonStr))
  .then(data => {
    // your logic
  });
Enter fullscreen mode Exit fullscreen mode

Now you understand how easy it is to read your asset files. From here, you can load these files at any time, which will significantly accelerate your app's start time and reduce memory consumption.

It's also worth mentioning that you can employ various approaches for how and when to load your assets. For instance, you might preload all necessary files at a specific initial stage. Additionally, caching the results can prevent the need to read and parse JSON multiple times.

Render lazy loaded assets

I can also share a React component example showcasing how to implement data loading using the use React hook and Suspense:

import { readAsset } from './my-fs.js';
import { MyLottie } from './my-lottie.js';
import { Suspense, use } from 'react'; // Assuming 'use' and 'Suspense' are imported.

const sourcePromise = readAsset('new-chat-anim.json').then(s => JSON.parse(s));

export default function App() {
  return (
    <Suspense fallback={/* ... */}>
      <MyLottie sourcePromise={sourcePromise} />
    </Suspense>
  );
}

// ./my-lottie.js
import LottieView from 'lottie-react-native';
// Assuming 'use' is imported from 'react' as well.

export function MyLottie({ sourcePromise }) {
  const source = use(sourcePromise);
  return <LottieView source={source} />;
}
Enter fullscreen mode Exit fullscreen mode

As an alternative solution, you can also load data over the internet. For example, Re.Pack offers remote assets functionality.

Lastly, the example involving locales JSON files will be discussed in a dedicated article, as previously detailed in 🚨 99% of React Native Apps Make This Localization (i18n) Mistake — Is Yours One of Them?

Top comments (1)

Collapse
 
bhavishya_aggarwal_2651e7 profile image
Bhavishya Aggarwal

This was a good article 🤍