Expo SDK 54

Sep 10, 2025 by

Alan Hughes

Alan Hughes

Brent Vatne

Brent Vatne

Today we're announcing the release of Expo SDK 54. SDK 54 includes React Native 0.81. Thank you to everyone who helped with beta testing.

Expo SDK 54 is released. SDK 54 includes React Native 0.81

Precompiled React Native for iOS

Starting with React Native 0.81 and Expo SDK 54, React Native on iOS and its dependencies will be shipped as precompiled XCFrameworks alongside the source.

We’ve found that [using the precompiled XCFrameworks] reduced clean build times for RNTester from about 120 seconds to 10 seconds (on an M4 Max — exact numbers will vary depending on your machine specs). While you’re unlikely to see a ~10x build time improvement in your app due to the number of other dependencies in most apps that will still need to be compiled, we expect a noticeable reduction in build times in large projects and an even more pronounced improvement in smaller projects where React Native is responsible for a greater share of the build time.

In addition to the speed benefits, this improvement brings us closer to being able to move from CocoaPods to Swift Package Manager for React Native and Expo projects.

Note that if your app uses use_frameworks! in your Podfile (or useFrameworks in expo-build-properties), it will always build from source and you won't be able to take advantage of this improvement yet. We hope to add support for this case in the near future.

Learn more in Precompiled React Native for iOS: Faster builds are coming in 0.81.

iOS 26 and Liquid Glass

Support for Liquid Glass icons and Icon Composer

SDK 54 adds support for iOS 26 Liquid Glass icons, which you can create using the new Icon Composer app. The Icon Composer app produces a .icon file, which you can reference in your app.json under the ios.icon key:

app.json
{
"ios": {
"icon": "./assets/app.icon"
},
"android": {
"adaptiveIcon": {
...
}
}
}


It’s important to note that the Icon Composer app is macOS only — if you don’t have access to a macOS machine that can run the app, then you won’t be able to take advantage of the UI for building the icons. However, the output is relatively straightforward JSON and it’s possible that a tool will emerge to handle this.

When your app using this icon format is run on an older iOS version (iOS ≤ 19), an appropriate fallback will be automatically provided by the operating system.

Using Liquid Glass views in your app

See these effects live on this YouTube short. Also notice the iOS 26 bottom tabs UI — this is available in Expo Router v6. See the Expo Router section below for more information.

Using UIKit (best choice for most Expo apps today)

You can use Liquid Glass views seamlessly in your app with the new expo-glass-effect library and its <GlassView> and <GlassContainer> views, which are built on UIVisualEffectView. Learn more about expo-glass-effect.

Code
import { StyleSheet, View, Image } from 'react-native';
import { GlassView } from 'expo-glass-effect';
export default function App() {
return (
<View style={styles.container}>
<Image
style={styles.backgroundImage}
source={{
uri: '<https://images.unsplash.com/photo-1506905925346-21bda4d32df4?w=400&h=400&fit=crop>',
}}
/>
{/* Basic Glass View */}
<GlassView style={styles.glassView} />
{/* Glass View with clear style */}
<GlassView style={styles.tintedGlassView} glassEffectStyle="clear" />
</View>
);
}

Using SwiftUI (try out our beta Expo UI library!)

Expo UI for iOS, a library that gives you SwiftUI primitives in your Expo app, is now in beta. This release includes a brand new guide in the docs in addition to support for Liquid Glass SwiftUI modifiers, button variants, and much more.

Code
import { Host, HStack, Text } from "@expo/ui/swift-ui";
import { glassEffect, padding } from '@expo/ui/swift-ui/modifiers';
// In your component
<Host matchContents>
<HStack
alignment='center'
modifiers={[
padding({
all: 16,
}),
glassEffect({
glass: {
variant: 'regular',
},
}),
]}>
<Text>Regular glass effect</Text>
</HStack>
</Host>

Xcode 26 and Liquid Glass

Compiling your project with Xcode 26 is required if you plan to take advantage of the iOS 26 features mentioned above.

EAS Build and Workflows will default to using Xcode 26 for SDK 54 projects. If you are building your project on your own machine, you can download the latest version of Xcode from the Apple Developer Releases page. Xcode 26 is currently in RC status, and the GM should be released by Apple on September 15. By building your app with Xcode 26 now, you can ensure it is ready for iOS 26 immediately on its release (also on September 15). A new version of Expo Go that is built with Xcode 26 will be available shortly.

React Native for Android now targets Android 16 / API 36

Edge-to-edge is now always enabled

With Expo SDK 54 and React Native 0.81 now targeting Android 16, edge-to-edge will be enabled in all Android apps, and cannot be disabled. Additionally, react-native-edge-to-edge is no longer a dependency of the expo package because the required functionality was built into React Native by the author of the library, Mathieu Acthernoene. If you are using the react-native-edge-to-edge config plugin to configure edge-to-edge in your project, make sure that the package is a direct dependency of your project (npx expo install react-native-edge-to-edge). If you were only using the plugin only to configure the enforceNavigationBarContrast option you can use the new androidNavigationBar.enforceContrast property in your app.json to get the same effect without additional dependencies.

Predictive back gesture available as opt-in

The Android predictive back gesture feature is disabled by default in all projects in SDK 54, and you can enable it in your project's app.json with android.predictiveBackGestureEnabled. As with edge-to-edge, we expect opt-outs will eventually be removed from a future version of Android, and we plan to enable this by default in all projects in SDK 55 or 56. Learn more.

Expo Updates & EAS Update

  • The HTTP headers Update sends, such as channel, can now be overridden at runtime with Updates.setUpdateRequestHeadersOverride(). This enables developers to easily implement patterns such as opting employees into a different update channel than end-users. Unlike Updates.setUpdateURLAndRequestHeadersOverride() , which allows you to also override the update URL and requires the disableAntiBrickingMeasures build time flag — the new method only applies to headers, is available without setting any flags, and can be used safely in production apps. Configuration set with either method will apply immediately to the currently running app. This means that you don’t need to restart the app for the new configuration to take effect, you can call Updates.fetchUpdateAsync() and Updates.reloadAsync() after you update the headers. Learn more about overriding request headers.
  • The useUpdates() hook now includes a downloadProgress property, which you can use to track the progress of asset downloads during an update. You can use this to show a progress bar when downloading an update, and possibly other creative scenarios. Learn more.
  • Updates.reloadAsync() now accepts reloadScreenOptions to give developers control over the UI that is presented while your app is reloading. This provides a much better user experience than a flash of empty content. The following example configuration shows a full screen image and fades out when the update has been applied: In development, you can test your reload screen configuration by using Updates.showReloadScreen({ reloadScreenOptions }) and Updates.hideReloadScreen().
Code
import * as Updates from 'expo-updates';
Updates.reloadAsync({
reloadScreenOptions: {
backgroundColor: '#fa0000',
image: require('./assets/images/reload.jpg'),
imageResizeMode: 'cover',
imageFullScreen: true,
fade: true
},
});
  • React Native modules that are installed as transitive dependencies will now be autolinked. While Expo Autolinking always worked this way for Expo modules, now that we take care of linking React Native modules (as of SDK 52) we were able to implement this behavior for React Native modules as well. This means that you will be able to let libraries take care of managing their dependencies, rather than copying and pasting a command to install a handful of native dependencies on their behalf (learn more). You can revert back to the previous autolinking behavior, by adding the expo.autolinking.legacy_shallowReactNativeLinking: true flag in your app’s package.json.
  • Expo and React Native modules will now link according to your app’s direct and nested dependencies, rather than the former behavior of autolinking any module in your node_modules that includes expo-module.config.json. This is primarily relevant to monorepos, where dependencies from multiple apps may be hoisted to the same node_modules directory. Now, either your app or a dependency of your app will need to contain a native module in their dependencies or peerDependencies for it to be linked. You can revert back to the previous autolinking behavior by adding your node_modules folders to expo.autolinking.searchPaths in your app’s package.json.
  • Expo Autolinking now has unified behavior across Expo and React Native modules, and so it will behave more predictably with isolated dependency installations (Bun and pnpm) and with hoisting conflicts.

Changes that impact how dependencies are handled will often lead to issues in more unique project configurations that couldn’t be anticipated. You can verify your expected native modules against the output of npx expo-modules-autolinking verify -v proactively or to troubleshoot issues if you believe they may be related to these changes. If you need to opt out of both of the changes mentioned above, add the following to your app’s package.json:

Code
{
"expo": {
"autolinking": {
"legacy_shallowReactNativeLinking": true,
"searchPaths": ["../../node_modules", "node_modules"]
}
}
}

SDK 54 is the final release to include Legacy Architecture support

In React Native 0.80, the React Native team introduced a code freeze on the Legacy Architecture. In React Native 0.82, it will no longer be possible to opt out of the New Architecture. This means that SDK 55, which will likely include React Native 0.83, only support the New Architecture.

In recent months, a growing number of libraries have started to only support the new architecture. For example: react-native-reanimated v4 and @shopify/react-native-flashlist v2. The interop layer will remain a part of React Native for the foreseeable future, in order to ensure that libraries built for the Legacy Architecture continue to work well in modern apps.

At the time of writing, 75% of SDK 53 projects built on EAS use the New Architecture. If you are still concerned about migrating, learn more about work that has been done to address the last remaining identified issues found while migrating some of the largest React Native apps, such as the primary Shopify app and their Point of Sale app, which "[serve] millions of merchants”.

Highlights

  • React Native 0.81.3 with React 19.1. Refer to the release notes for React Native 0.81 and React 19.1 changelog for detailed information. Also, learn more about the Expo SDK policy for tracking React Native versions.
  • expo-file-system/next is stable: The new expo-file-system API is now stable and exposed as a default. If you were using it prior to upgrading, you will need to update your imports from expo-file-system/nextexpo-file-system. The old API is still available under expo-file-system/legacy. Some improvements include: an object-oriented API for working with files and directories, support SAF URIs on Android and bundled assets on both iOS and Android. You can expect more information in an upcoming blog post, in the meantime you can refer to the API reference.
  • expo-sqlite now includes a drop-in implementation for the localStorage web API. If you're already familiar with this API from the web, or you would like to be able to share storage code between web and other platforms, this may be useful. Learn how to import and install the localStorage API.
  • Prebuild template now included in the expo package rather than downloaded from npm. This ensures that the template used by a given expo package version remains unchanged even if new template versions are released. Updating the expo package will also bring in a new template version. Thanks to Thibault Malbranche for suggesting this change. Note that you can still provide a custom prebuild template with the --template flag.
  • expo-sqlite added loadExtensionAsync() and loadExtensionSync() APIs to support loading SQLite extensions. The sqlite-vec extension, which supports vector data processing, is also bundled to expo-sqlite as an opt-in extension. You can use sqlite-vec for some RAG AI work. See the implementation in expo#38693, for loadExtensionAsync() API usage, the withSQLiteVecExtension config-plugin option to opt-in sqlite-vec.
  • expo-app-integrity: New package for verifying app integrity using DeviceCheck (DCAppAttestService) on iOS and the Play Integrity API on Android. This allows you to confirm that the app is installed via the App Store or Play Store and is running on a genuine, untampered device. Learn more.
  • expo/blob: New package for working with binary large objects on iOS and Android. Our implementation is consistent with the W3C specification, providing you with interfaces familiar from the web. expo-blob is now in beta, and we’re excited to get your feedback! This library is not yet included in Expo Go.
  • expo-maps: Added support for JSON and Google Cloud based map ID styling on Google Maps and Points of Interest (POI) filtering on Apple maps. Learn more.
  • Improvements to brownfield experience, and more on the way. We are continuously improving the brownfield experience, and rewriting our documentation on native app integration. We've also added support custom folder structures, which is especially useful for large codebases where moving native projects to android and ios directories isn't feasible. Learn more.
  • Expo Go is coming soon to the Meta Horizon Store. We’ll be following up soon with more details about how to get started with using Expo Go on your face, and how to build your apps for the Horizon store yourself. The Horizon operating system has made large strides and getting your app up and running on it is likely much easier than you expect!
  • expo-dev-launcher rewrite: we rebuilt the expo-dev-client UI in order to simplify the interface with React Native and improve the Hermes debugging experience. In doing so, we also made some other improvements to the look and feel — let us know what you think!

Apple and Android TV

  • Experimental expo-dev-client support for Apple TV, full support on Android TV. Support on tvOS is still early, and you can’t yet authenticate with your Expo account in the app, but give it a try if you have an Apple TV and let us know what you think.
  • Added support for Apple TV in various SDK packages: expo-sqlite, expo-background-task, expo-task-manager, expo-insights, expo-image-loader, expo-image-manipulator, and expo-video-thumbnails. See the doc pages for the individual packages for more details.
  • tvOS builds will leverage the new precompiled frameworks provided in React Native 0.81: This will significantly reduce build times for these platforms (related blog).

Expo CLI

  • Import stack traces are now enabled by default. You can now see a list of imports leading to a missing module which makes it much easier to trace broken packages. With this feature, we found agents (such as claude code) could always resolve broken imports.
  • experimentalImportSupport is now enabled by default. We’ve rebuilt Metro ESM support in Expo CLI to better support React Compiler, and tree shaking. This moves us one step closer to full ESM support in Expo.
    • You can revert to the older system by setting experimentalImportSupport: false in the metro.config.js. We plan to remove this flag altogether in the next SDK release.
    • The rebuilt support uses live bindings by default to improve compliance with the ECMAScript specification and to support a wide range of projects. If you don't want this, set EXPO_UNSTABLE_LIVE_BINDINGS=false. Disabling live bindings will cause issues with many projects, and will break circular import support.
  • Expo CLI now auto-prefixes CSS by default using the Rust-based lightningcss. You can remove autoprefixer from your postcss.config.mjs file in favor of this implementation. We’ve also added support for browserslist in the package.json for CSS prefxi. Learn more.
  • @babel/plugin-transform-class-static-block is now added to babel-preset-expo by default. This enables an even wider set of web and server libraries to work with Expo by default. Learn more about static class blocks.
  • React Compiler is now enabled in the default template. We recommend using it in your projects. The Meta team is actively fielding support for any remaining issues with React Compiler. You can see which components are memoized by pressing J in Expo CLI and going to the components panel. You will see “Experimental React Compiler is enabled.” printed in your logs when you run npx expo start — this is because it is currently in Release Candidate. However, we believe it is ready for most apps. This default may change if this proves false during the beta period. Learn more in the Expo + React Compiler docs.
  • React Native owner stacks are now enabled by default. These improve errors that occur in React components and make it easier to find/fix problems for both humans and agents.
  • Unhandled promise rejections are now logged as errors. After upgrading to this SDK, you might notice new promise rejection errors in your mobile applications. These aren't caused by the SDK itself, but are now properly surfaced as errors, which aligns with how Promises work in web browsers.
  • Experimental autolinking module resolution was added. Expo CLI is now able to apply Expo Autolinking’s linking decisions to JavaScript module resolution. This prevents mismatches between native modules and JavaScript modules, even in the presence of dependency conflicts and duplicates, and also resolves react and react-dom to a single version. Set experiments.autolinkingModuleResolution to true in app.json to test this.
  • Bumped the recommend TypeScript version to ~5.9.2
  • Reminder from SDK 53: The import.meta transform plugin is still an experimental opt-in feature, which you can turn on with the unstable_transformImportMeta option in the babel-preset-expo configuration (example). Enable this if some of your ESM dependencies rely on import.meta.

Expo Router

Expo Router v6 - Link Previews
  • Link now supports iOS view controller previews, transitions, and context menu items. These can be used to add quick actions and information to any link in your app. Learn more.
  • Beta support for native tabs on iOS and Android. Unlike the JS tabs implementation, this enables liquid glass tabs, automatic scrolling on tab press, and many other beautiful native effects. The API is still under development and subject to breaking changes until we remove the unstable- prefix from the import. Learn more.
  • Modals on web now emulate iPad and iPhone behavior instead of just being a full screen page with no modal-like attributes.
  • New experimental server middleware support. Middleware can be used to run code before requests reach a route. Learn more.
  • TextDecoderStream and TextEncoderStream are added to the native runtime to better support fetch streaming and working with AI.

Deprecations & removals

  • expo-build-properties field enableProguardInReleaseBuilds is deprecated in favor of enableMinifyInReleaseBuilds.
  • React Native’s <SafeAreaView> component has been deprecated: use react-native-safe-area-context instead if you aren’t already. It’s a much more powerful alternative, and one of those libraries that nearly every app uses.
  • The notification configuration field in app config has been deprecated in favor of expo-notifications config plugin.
  • expo-av will be removed in SDK 55. It was deprecated in SDK 53, and this will be the last SDK release where it will be a part of the SDK. Migrate to expo-audio and expo-video.

Notable breaking changes

  • First-party JSC support removed from React Native: React Native 0.81 no longer provides built-in JSC support — if you would like to continue using JSC, refer to https://github.com/react-native-community/javascriptcore. Note: this community-maintained JSC library does not yet provide a config plugin, and so you will need to either write your own or modify your native projects directly in order to use it.
  • Reanimated v4 introduces react-native-workletsand only supports the New Architecture. Refer to the Reanimated 3.x to 4.x migration guide, but skip modifying your babel.config.js (this is handled automatically by babel-preset-expo). If you need to continue using the Legacy Architecture, you can continue using Reanimated v3 — learn how to use Reanimated v3 with SDK 54.
  • Internal Metro imports have changed in metro@0.83. Importing internals from metro/src/.. is not longer supported. Internals are now only accessible via metro/private/... For most app developers, this won’t impact you. If you maintain a library or app that interacts with Metro directly, please switch to Metro’s public APIs. If you use a library that runs into an error due to src imports, open an issue on the library’s GitHub issues.
  • expo-file-system legacy API now available through expo-file-system/legacy and the default exports for the library were replaced with what was formerly expo-file-system/next . The quickest way to upgrade and have your app working the same as before is to replace all imports for expo-file-system to expo-file-system/legacy. Next, you can migrate to the new API at your own pace. If you were already using expo-file-system/next, update your imports to expo-file-system instead (the old imports will still work, but you will be warned). We plan to expo-file-system/legacy in SDK 55. Learn more about the new API.
  • expo-notifications deprecated function exports were removed expo/expo#38782.

Tool version bumps

  • Minimum Xcode bumped to 16.1. Xcode 26 is recommended.
  • Minimum Node version bumped to 20.19.4.

Known issues

  • Precompiled React Native for iOS is not compatible with use_frameworks!. When using use_frameworks!, React Native for iOS will always build from source.
  • Found an issue? Report it.

➡️ Upgrading your app

Here's how to upgrade your app to Expo SDK 54 from 53:

  • Update to the latest version of EAS CLI (if you use it):
Terminal
npm i -g eas-cli
  • Upgrade all dependencies to match SDK 53:
Terminal
npx expo install expo@^54.0.0 --fix
  • If you have any resolutions/overrides in your package.json, verify that they are still needed. For example, you should remove metro and metro-resolver overrides if you added them for expo-router in a previous SDK release. Additionally, if you previously configured your metro.config.js to work well in a monorepo, we recommend reading the updated Work with monorepos guide to see if you need to make any changes
  • Check for any possible known issues:
Terminal
npx expo-doctor@latest

Thanks to everyone who contributed to the release!

The team, in no particular order: everyone contributed one way or another, with special mentions to the engineers most directly involved in this release: Alan HughesAleksander MikuckiCedric van PuttenChristian FalchDoug LowderEvan BaconGabriel DonadelKudo ChienŁukasz KosmatyPhil PluckthunVojtech Novak, and Wojciech Dróżdż. Other contributors include Aman MittalBeto MoedanoKadi KramanKeith KurakQuin Jung, and Will Schurman. Welcome Nishan Bende, Hirbod Mirjavadi, Krystof Woldrich, Jakub Tkacz, Hassan Khan, and Wiktor Smaga!

External contributors, in no particular order: A., Abdullah Mzaien, Abdulrahman Alfawal, Abdurrahman Rajab, Adzka Fahmi, Aleksei Voronin, Alex Ott, Alex Toudic, Amaury Liet, Andrew, Andrew Coates, Andrew Smith, Aniket Jayateerth, Ankit Agarwal, Anton Hudz, Artur Gęsiarz, Bartosz Szar, Bradley Ayers, Broda Noel, Cameron Hashemi, ChrisKyle, Clark Gredoña, Dan, Daniel O’Connor, Daniel Williams, David Guerin, Dawid Matyjasik, Dominik Miskovic, Drew Radcliff, Filipe Juan, Frank Calise, Gregory Moskaliuk, Guilherme D'Alessandro, Gurneer Bedi, Gustavo Harff, Hamzat Victor Oluwabori, Hanno J. Gödecke, Henrik, Hezekiel Tamire, Hippo Shark, Hugo EXTRAT, Hyo, Hyungu Kang | Airen, Jakub Kosmydel, Jakub Tkacz, Janic Duplessis, Jonathan Mazin, Josh Stern, Julien Henrotte, Kid, Leon Horlings, Lucas Bortoli, Lucas Ferrari, Lucas Novelo, Manish Paudel, Marlin Ranasinghe, Mateo Guzmán, Mike Bifulco, Mohammad Amin, Nutcase, Ovidiu Cristescu, Parth Parmar, Pavlo Hromov, Pavlos Vinieratos, Petr Chalupa, Petr Konecny, Pflaumenbaum, Phil Bazun, Pino Kokol, Prathamesh More, Prince Mittal, Priyav K Kaneria, Radek Czemerys, Raku, Ramon, Randall71, Rebakure🔭, Roger Braunstein [Scopely], Russ Savage, Sebastian Hollington, Steven Eubank, Suleyman, Sávio Carlos Martins Costa, Ted Summer, Thibault Malbranche, Tomasz Żelawski, Tyler Scott Williams, Vadko, Xiaohan Li, Yatendra, adiktiv, charlotte ✨, desii, katayama8000, mo, sanchezg7

Beta testers: Thibault Malbranche, Patrik Duksin, Charlie, Julian Dueck, MrFunctor, Max, Kasymbekov Sultanmyrza, Delgermurun, Nitesh, Tyler Scott Williams, Vincent Fleming, Chris Zubak-Skees, Patrick Weisensee, and many more!

Additional resources

If you'd like to watch a summary and some demos from SDK 54, take a look at this YouTube video. Additionally, there will be a livestream soon where the team will walk through various features and take questions.

Join the team on a live stream about SDK 54 on September 17 at 10:00am PDT