Markets

Shrink Your Lottie Animations by 80% with the .lottie Format

Animations are everywhere in today’s consumer apps. Companies use them to grab attention, guide users, and create that memorable, branded feel whether it’s a delightful splash screen, a smooth loading indicator, or festive banners during special seasons. As a React Native developer, we know how much impact good animation can have on the flow / product.

Over the last several years, Lottie animations (exported as .json files) have become hugely popular. They make it easy to add rich, scalable animations without impacting performance. But there’s one thing most of us have run into is lottie JSON files can get quite large. Even simple-looking animations can bloat to hundreds of kilobytes or more, especially if they have many animated layers. For apps showcasing several animations like onboarding steps, dynamic banners, achievement badges, and so on, this leads to real challenges.

The .lottie Format: Smaller and Lighter

Lottie supports an optimized format called .lottie. Instead of a raw JSON file, a .lottie animation is a zipped archive that shrinks file size by 70-80% compared to plain JSON – so our 1 MB animation might become 200-300 KB. It also packages all assets (including images) together, reducing missing asset issues. It is ideal for dynamic apps where animation content can change via configs which is great for marketing teams, seasonal banners, or user personalization.

We typically have two options to include lottie animations in our app

  1. Bundling Animations in the App
  2. This method bundles all your Lottie JSONs within the app bundle-ensuring fast, offline playback. But as your add more animations, your app size increases, and every small animation tweak means a new app release.
  3. Loading Animations Over the Network
  4. Alternatively, you can host your animations on a remote server or CDN (like S3). This keeps your app download lightweight and allows for more flexible updates. The downside? Each time an animation is shown, your app fetches it again. Users with slow or unreliable connections may get stuck waiting-or see failed/missing animations. Plus, it wastes mobile data.

So whether you bundle or load over the network, switching to .lottie already gives you size benefits. But there is scope for better experience, with just a few small tweaks you can make the whole experience feel a lot smoother and more reliable.

How can we make the .lottie experience even better?

Since .lottie files are just zipped JSON with (optional) images, we can treat them exactly like any standard zip archive: extract them, process the animation JSON however we like, and store a ready-to-use, all-in-one JSON and cache them locally for our app’s next use. This gives us smooth, flexible animation playback-on any device, in any network conditions, every time. Here is a simple workflow we can follow

After these steps, you can load and play the JSON just like any regular bundled Lottie. So whenever you get a .lottie file, first check if a processed json version already exists – if it does, use it. If not, download it, process as json, save it, and reuse it next time.

1. ⬇️ Download the Animation File

First, download the .lottie and store it locally using a unique name.

const name = extractNameFromUrl(url)
const extension = url.match(/\.(json|lottie)$/)?.[1] ?? 'json';
const lottiePath = RNFB.fs.dirs.DocumentDir + `/lottie/${name}.${extension}`;
await RNFB.config({
  path: lottiePath,
}).fetch('GET', url, headers);

2. 📦 Unzip the .lottie File

If the animation is in .lottie format, we have to unzip it. This will extract the JSON animation data and any images it references.

if (extension === 'lottie') {
  const extractPath = `${RNFB.fs.dirs.DocumentDir}/${name}_unzipped`;
  await unzip(lottiePath, extractPath);
}

3. 🧩 Extract and Process the Animation JSON

Inside the unzipped folder, look for an animations folder. Find the animation JSON file:

const animationsPath = `${extractPath}/animations`;
const files = await RNFB.fs.ls(animationsPath);
const animFile = files.find(file => file.endsWith('.json'));
if (!animFile) throw new Error('No animation JSON found after unzip');
const animPath = `${animationsPath}/${animFile}`;

4. 🖼️ Embed Images Inside the Animation JSON

Sometimes, the lottie uses separate images (say, backgrounds, icons, main assets). To make rendering fast, we can embedd these images right into the JSON file. This means converting image files to base64, then updating the JSON so all images are packed inline. Basically if you inspect the json before embedding it looks like something on the left, now we are finding the actual image with the u and p keys in the json and then converting them to base64 and updating the keys with these values to get our final json. Here u specifies the image folder or directory path and p specifies image filename.

Below is the pseudo code for this process.

function embedImagesInLottieJSON(animationJsonPath, imagesFolderPath):

    // Read the animation JSON as text
    animationJsonText = readFile(animationJsonPath, asText)
    animationJson = parse(animationJsonText)

    // If there are no assets, just return original JSON
    if animationJson.assets is not an array:
        return animationJsonText

    for each asset in animationJson.assets:
        if asset.u exists AND asset.p exists AND "images" in asset.u AND asset.e == 0:
            // Get the image file path
            imagePath = imagesFolderPath + "/" + asset.p

            // If the image exists
            if fileExists(imagePath):
                // Read image as base64
                imageBase64 = readFile(imagePath, asBase64)

                // Find out file type
                extension = getFileExtension(asset.p)
                mimeType = (extension in ["jpg", "jpeg"]) ? "image/jpeg" : "image/png"

                // Replace asset path with base64 data URI
                asset.u = ""
                asset.p = "data:" + mimeType + ";base64," + imageBase64
                asset.e = 1 // marking it as embedded

    return stringify(animationJson)

All referenced image are now packed right into the json file. After this embedding, save your processed JSON to a “processed” folder for instant re-use:

const processedDir = `${extractPath}/processed`;
const processedDirExists = await RNFB.fs.exists(processedDir);
if (!processedDirExists) {
  await RNFB.fs.mkdir(processedDir);
}
const processedJsonPath = `${extractPath}/processed/embedded.json`;
await RNFB.fs.writeFile(processedJsonPath, embeddedJSON, 'utf8');

How Do You Know a Lottie Is Downloaded and Ready?

It’s better to show the relevant screen or entry only after the Lottie is fully processed-especially if your animation is an essential part of the user flow. Before showing the animation, check if the processed JSON file (embedded.json) exists. If it does, you can play it right away. Otherwise, do the download-unzip-embed steps first.

const isLottieProcessed = async (animationName) => {
  const processedJsonPath = `${RNFB.fs.dirs.DocumentDir}/${animationName}_unzipped/processed/embedded.json`;
  return await RNFB.fs.exists(processedJsonPath);
};

Why This Approach is Better

  1. Faster experience: Animations load instantly after the first download-no repeated network or data use.
  2. Reliable offline: All assets are embedded; nothing breaks when the user is offline or has bad connectivity.
  3. Remote control: Marketing and product teams can swap out or update animations on the fly, without shipping a new app.
  4. Lean apps: Your APK or IPA size stays small and agile, even with dozens of beautiful Lottie animations.

One small catch in this approach is when we want to update or change something in a Lottie animation-we usually end up saving it with a new name. That starts the whole download, unzip, and embed process again, which can quickly lead to duplicate animations piling up on the device, all with slightly different content or versions.

The simple fix here is to use versioning in our filenames-like naming our file first-v1.lottiefirst-v2.lottie etc. This way, whenever we make any changes, just bump the version in the file name. Then, in our extract or caching logic, we can easily clear out any older versions and keep only the latest one on the device. This saves the storage and makes sure users always get the right, up-to-date animation without unnecessary duplicates.

If you haven’t tried out .lottie yet, you’re probably missing out on some easy wins, this approach might be worth exploring.

Related Articles

Leave a Reply

Your email address will not be published. Required fields are marked *

Back to top button