Skip to content

Instantly share code, notes, and snippets.

@hirbod
Last active January 10, 2024 09:11
Show Gist options
  • Save hirbod/07c6641970c9406ff35a7271dda1f01c to your computer and use it in GitHub Desktop.
Save hirbod/07c6641970c9406ff35a7271dda1f01c to your computer and use it in GitHub Desktop.
expo-config-plugin: animated webP support for Expo SDK44+ and Custom Dev Client (with FastImage) support

I recommend to not use this anymore. Switch over to expo-image

If anybody needs animated webP support with Expo (Custom Dev Client) for the native <Image /> and <FastImage /> (read comments):

Android

// create a file like plugins/withAnimatedWebPSupport.js -> this is for the native <Image />

const {
    createRunOncePlugin,
    withGradleProperties
} = require('@expo/config-plugins');

const withAnimatedWebPSupport = (config) => {

    const propertyToModify = {
        type: 'property',
        key: 'expo.webp.animated',
        value: true,
    };

    return withGradleProperties(config, (config) => {
        config.modResults = config.modResults.filter(
            (item) => !(item.type === propertyToModify.type && item.key === propertyToModify.key)
        );

        config.modResults.push(propertyToModify);

        return config;
    });
};

module.exports = createRunOncePlugin(withAnimatedWebPSupport, 'animated-webp-support', '1.0.0');
// create a plugins/withFastImageWebPSupportAndroid.js - This is for `<FastImage />` Support
// note that this will need withAnimatedWebPSupport, because I've replaced it in that if-condition. Feel free to customize if you only want webP with FastImage

const {
    withAppBuildGradle,
} = require("@expo/config-plugins");


const withCustomAppBuildGradle = (config) => {
    const insertString = `implementation "com.github.zjupure:webpdecoder:2.0.4.12.0"`;
    return withAppBuildGradle(config, (config) => {
        if (
            config.modResults.contents.includes(
                insertString
            )
        ) {
            return config;
        }

        config.modResults.contents = config.modResults.contents.replace(
            `if (isWebpAnimatedEnabled) {`,
            `if (isWebpAnimatedEnabled) {
            ${insertString}`
        );
        return config;
    });
};

module.exports = function withFastImageWebPSupportAndroid(config) {
    config = withCustomAppBuildGradle(config);
    return config;
};

iOS

// create a plugins/withFastImageWebPSupportIOS.js
const {
    WarningAggregator,
    withAppDelegate,
    createRunOncePlugin
} = require('@expo/config-plugins');


const RNFI_EXPO_WEBP_IMPORT = `#import "AppDelegate.h"
// expo-config-plugin fast-image webp animated support
#import "SDImageCodersManager.h"
#import <SDWebImageWebPCoder/SDImageWebPCoder.h>
// end config plugin
`

const RNFI_EXPO_WEBP_DID_FINISH_LAUNCHING_IDENTIFIER = `- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{`

const RNFI_EXPO_WEBP_DID_FINISH_LAUNCHING_CODE = `
  // start expo-config-plugin fast-image webp animated support
  [SDImageCodersManager.sharedManager addCoder: SDImageWebPCoder.sharedCoder];
  // end expo-config-plugin fast-image webp animated support
`

function modifyAppDelegate(appDelegate) {
    if (!appDelegate.includes(RNFI_EXPO_WEBP_IMPORT)) {
        appDelegate = appDelegate.replace(
            /#import "AppDelegate.h"/g,
            RNFI_EXPO_WEBP_IMPORT,
        );
    }
    if (appDelegate.includes(RNFI_EXPO_WEBP_DID_FINISH_LAUNCHING_IDENTIFIER)) {
        if (!appDelegate.includes(RNFI_EXPO_WEBP_DID_FINISH_LAUNCHING_CODE)) {
            const block = RNFI_EXPO_WEBP_DID_FINISH_LAUNCHING_IDENTIFIER + RNFI_EXPO_WEBP_DID_FINISH_LAUNCHING_CODE
            appDelegate = appDelegate.replace(RNFI_EXPO_WEBP_DID_FINISH_LAUNCHING_IDENTIFIER, block)
        } else {
            WarningAggregator.addWarningIOS('withFastImageWebPSupportIOSAppDelegate', `FastImage webP support already setup in AppDelegate`)
        }
    } else {
        throw new Error('Failed to detect didFinishLaunchingWithOptions in AppDelegate')
    }
    return appDelegate
}

const withFastImageWebPSupportIOS = (config) => {
    return withAppDelegate(config, (config) => {
        if (['objc', 'objcpp'].includes(config.modResults.language)) {
            config.modResults.contents = modifyAppDelegate(config.modResults.contents)
        } else {
            WarningAggregator.addWarningIOS('withFastImageWebPSupportIOSAppDelegate', `Cannot setup FastImage webP support, the project AppDelegate is not a supported language: ${config.modResults.language}`)
        }
        return config
    })
}

module.exports = createRunOncePlugin(withFastImageWebPSupportIOS, 'rnfi-expo-animated-webp-support', '1.0.0');

Install plugin in your app.json / app.config.js (expo.plugins)

    ['./plugins/withAnimatedWebPSupport'],
    ['./plugins/withFastImageWebPSupportIOS.js'],
    ['./plugins/withFastImageWebPSupportAndroid.js'],

Credits:

Based on other config plugins by @GollyJer, @wodin, @barthap, @EvanBacon and @nandorojo Thanks guys!

Native <Image> component by RN

In case you only need native <Image /> support, just install this package: https://github.com/Aleksefo/react-native-webp-format

and only use the withAnimatedWebPSupport Plugin for Android.

@GollyJer
Copy link

GollyJer commented Jul 1, 2022

@hirbod Finally got upgraded to v45. The iOS changes worked great. Thanks!

@mikob
Copy link

mikob commented Aug 24, 2022

Works in SDK 46, thank you!

@lxndr-rl
Copy link

Thanks for this :D

@hirbod
Copy link
Author

hirbod commented Jul 25, 2023

Don't use it. Just switch over to expo-image

@KishorJena
Copy link

can't we use it for bare react native . i am not using expo

@hirbod
Copy link
Author

hirbod commented Jul 25, 2023

Yes, all expo modules work without expo. The Expo modules core just adds 150KB of overhead, so its really not harming

@KishorJena
Copy link

and how to use it with bare react native?

@hirbod
Copy link
Author

hirbod commented Jul 25, 2023

Just install it :D

yarn add expo-image
npx pod-install

And for bare, just follow these extra instructions:
https://docs.expo.dev/bare/installing-expo-modules/

@KishorJena
Copy link

KishorJena commented Jul 26, 2023

ahh but I cant use expo :(
I used implementation("com.github.zjupure:webpdecoder:2.1.4.12.0") in react-native-fast-image build gradle.
I noticed that if I put of RN beside then some webp works fine. remove component disables webp in . i.e. images becomes static.

btw is too laggy while playing animated webp and gifs thats why I want to use fastimage.

@hirbod
Copy link
Author

hirbod commented Jul 26, 2023

As said, you're not using Expo when you install expo-modules or any expo-package. It works with every RN install.

@KishorJena
Copy link

for some reason expo-module installation did not go well. created new RN project and added
implementation "com.github.zjupure:webpdecoder:2.2.${glideVersion}"
as you suggested in your answer on stackoverflow. and it worked
earlier it was not working. but clean project might removed any conflicts. because I had tried to use fresco in root project as well then removed and added glide. This time I have added the glide webdecoder directly without touching anything else :D

@GollyJer
Copy link

@hirbod Have you switched over to expo-image?

I'm trying but animated webp and svg aren't rendering. Animated gifs work fine.
As far as I can tell it should work by simply adding the expo-image package and doing a new eas build. No need for any plugins.

@hirbod
Copy link
Author

hirbod commented Jan 10, 2024

See the statement in the main post. We’re using expo-image and I don’t recommended anybody using FastImage anymore!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment