website/blog/2025-01-21-version-0.77.md
Today we are excited to release React Native 0.77!
This release ships several features: new styling capabilities such as support for display: contents, boxSizing, mixBlendMode, and outline-related properties to provide a more powerful layout options; Android 16KB page support to be compatible with the newer Android devices. We are also modernizing the community template by migrating it to Swift, while continuing to support and maintain compatibility with Objective-C for developers who prefer it.
React Native 0.77 furthers our goal of aligning React Native with the web. We've added support for new CSS properties to give you more control over your app's layout, sizing, and blending. These changes can help simplify complex layouts, add texture, and make your app more accessible.
:::info All these new features are available only for the New Architecture. :::
display: contentsThe display: contents prop allows an element to disappear from the layout structure while its children are still rendered as if they were direct children of the parent element. It can be useful for styling purposes where you want to apply styles to child elements without affecting the layout, when building wrapper components which must handle events, or if you need to interact with the ShadowTree.
Technically speaking, display: contents renders an element without generating a layout box, but it preserves the layout boxes of the element’s children. The element with display: contents is effectively flattened out of the view hierarchy.
Let’s have a look at this example where we want to display an alert when a widget is pressed. We have a red Widget inside of a container view:
function Container() {
return (
<View style={styles.container}>
<Widget />
</View>
);
}
Now, let's build a new Alerting wrapper component, with the goal of alerting the user when a component beneath it is pressed, using experimental pointer events. For clarity, the background of this component is made blue. That might look something like the component below:
function Alerting({children}) {
return (
<View
style={{backgroundColor: 'blue'}}
onPointerDown={() => alert('Hello World!')}>
{children}
</View>
}
function Container() {
return (
<View style={styles.container}>
// highlight-next-line
<Alerting>
<Widget />
</Alerting>
</View>
);
}
This doesn’t do quite what we want it to. Alerting adds a new layout box, with its own bounds, separate from the child Widget. Depending on the styling of the element it is wrapping, this may result in significant visual and functional changes. In this example, the blue background responds to taps with an alert when we want for only the red "Hello World" box to alert when tapped.
If we try this again, while setting display: contents on the View wrapper of Alerting, we only see alerts when the user presses within the original bounds of the Widget. This is because Alerting no longer adds its own box, but can still observe the pointer events bubbled from Widget.
function Alerting({children}) {
return (
<View
// highlight-next-line
style={{display: 'contents'}}
onPointerDown={() => alert('Hello World!')}>
{children}
</View>
);
}
// ... function Container ...
The boxSizing prop defines how the element's various sizing props (width, height, minWidth, minHeight, etc.) are computed. If boxSizing is border-box, these sizes apply to the border box of the element. If it is content-box they apply to the content box of the element. The default value is border-box, this is different from the default value on the web. The web documentation is a good source of information if you wish to learn more about how this prop works.
:::warning
border-box has been the default forever at this point, and has been the only boxSizing value up until we added content-box. Changing the default would have been a breaking change that would suddenly break several layouts. We decided to keep border-box as default value to ensure backward compatibility.
:::
To understand the difference between border-box and content-box, have a look at these example, where both Views have padding: 20 and borderWidth: 10. When using border-box, we consider border and padding for the sizing; when using content-box, we consider only the content for the sizing.
The mixBlendMode prop lets you control how an element blends its colors with the other elements in its stacking context. Check out the MDN documentation for a full overview of each blending function.
To help have more granular control about what is blending together, we also added the isolation property. Setting isolation: isolate on a View will force it to form a stacking context. So, you can set this on some ancestor View to ensure that some descendent View with mixBlendMode does not blend beyond the isolated View.
normal: The element is drawn on top of its background without blending.multiply: The source color is multiplied by the destination color and replaces the destination.screen: Multiplies the complements of the backdrop and source color values, then complements the result.overlay: Multiplies or screens the colors, depending on the backdrop color value.darken: Selects the darker of the backdrop and source colors.lighten: Selects the lighter of the backdrop and source colors.color-dodge: Brightens the backdrop color to reflect the source color. Painting with black produces no changes.color-burn: Darkens the backdrop color to reflect the source color. Painting with white produces no change.hard-light: Multiplies or screens the colors, depending on the source color value. The effect is similar to shining a harsh spotlight on the backdrop.soft-light: Darkens or lightens the colors, depending on the source color value. The effect is similar to shining a diffused spotlight on the backdrop.difference: Subtracts the darker of the two constituent colors from the lighter color.exclusion: Produces an effect similar to that of the Difference mode but lower in contrast.hue: Creates a color with the hue of the source color and the saturation and luminosity of the backdrop color.saturation: Creates a color with the saturation of the source color and the hue and luminosity of the backdrop color.color: Creates a color with the hue and saturation of the source color and the luminosity of the backdrop color. This preserves the gray levels of the backdrop and is useful for coloring monochrome images or tinting color images.luminosity: Creates a color with the luminosity of the source color and the hue and saturation of the backdrop color. This produces an inverse effect to that of the Color mode.We’ve also introduced outlineWidth, outlineStyle, outlineSpread and outlineColor. These outline props work very similar to the respective border props, but it is rendered around the border box as opposed to around the padding box. These props allow to highlight elements by drawing their outline without affecting their layout.
Check out the MDN documentation for more details.
We’ve already done some work to support Android 15 on the prior release. One of the noticeable changes in Android 15 is forced edge-to-edge display when you build apps with targetSdk 35.
If you have not looked into this yet, please refer to our prior recommendation on how this should be handled as ignoring this can potentially break your UI in the app.
:::note
If you are using the react-native-safe-area-context in your app, the library is already handling the forced edge-to-edge for you.
:::
Android 15 introduces support for 16KB memory page size enabling performance improvements for apps and more, but making previous 4KB-based apps potentially incompatible on future devices; it's currently an opt-in feature for developers to test on select devices to prepare for 16 KB page size being the OS default.
With the 0.77 release, React Native is ready to fully support 16 KB page size and developers will be able to test and ship apps for 16 KB devices using it.
Please refer to the official Android Developers site for further information on 16 KB support.
This version fully completes the deprecation of the react-native init command that was introduced in React Native 0.75.
As a reminder, you won’t be able to use the react-native init command anymore, but you’ll have to either:
npx create-expo-appnpx @react-native-community/cli initIn this version, we removed the ‘a’ and ‘i’ keyboard shortcuts from Metro. Those shortcuts were used to invoke the run-android & run-ios community CLI commands.
Those keyboard shortcuts provided worse developer experience and were rarely used. Moreover, we believe that frameworks are better suited to orchestrate the terminal outputs.
You can read more about this change in this dedicated post.
:::info Projects using Expo should not be affected by this change. :::
This change let us slim down the community template by replacing three files (main.m, AppDelegate.h and AppDelegate.mm) with a single, new AppDelegate.swift.
This is technically a breaking change: you’ll see the change from Objective-C to Swift in the upgrade helper like this:
You don’t have to migrate to Swift: the Objective-C++ variant of the iOS community template is still supported (note that you still need to integrate the RCTAppDependencyProvider). New projects will be generated by using Swift as the iOS app language, although you can always migrate back to Objective-C if you need to.
If your app has some local modules that are written in C++, you would not be able to register them in Swift as shown in this guide.
If your app falls in this category, please skip the migration of the AppDelegate to Swift, and keep using Objective-C++ for your app.
React Native core is mostly developed using C++ to encourage code sharing between iOS and Android and other platforms. The interoperability between Swift and C++ is not mature nor stable, yet. We are looking into ways to fill this gap and let you migrate to Swift too.
React Native 0.77 slightly changes how the app loads third party dependencies. This is a new line in the community template that, if missed, can cause some runtime issues. Make sure to add it to your app.
The equivalent Objective-C lines are the following:
#import "AppDelegate.h"
#import <React/RCTBundleURLProvider.h>
// highlight-next-line
#import <ReactAppDependencyProvider/RCTAppDependencyProvider.h>
@implementation AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
self.moduleName = @"<Your app Name>";
// highlight-next-line
self.dependencyProvider = [RCTAppDependencyProvider new];
// You can add your custom initial props in the dictionary below.
// They will be passed down to the ViewController used by React Native.
self.initialProps = @{};
return [super application:application didFinishLaunchingWithOptions:launchOptions];
}
// remaining of the AppDelegate
console.log() streaming in MetroWe want every aspect of React Native debugging to behave reliably and to match the functionality of modern browser tooling. To meet this quality bar, log forwarding via Metro, originally deprecated in 0.76, is removed in 0.77.
This integration relied on a custom approach to communicate with the debugging target on device. With this change, we are moving exclusively to the Chrome DevTools Protocol (CDP).
For more info, see Why are JavaScript logs leaving Metro?
position of sticky headers on ScrollView will now be taken into account.ReactFabricInternals module
NativeModules object can now be used to load turbomodules in JS .
serverBaseUrl relative to the middleware hostuseConcurrentRoot from AppRegistry, as it was already ignoredrefs property from NativeMethods TypeScript definition.ReadableArray are now correctly typed as optionalReactHost.createSurface() method non nullableDevSupportManagerBase.getCurrentContext() to DevSupportManagerBase.getCurrentReactContext()Additionally, several APIs have been removed or restricted in visibility, so they can’t be accessed anymore. Those APIs were internal and not needed to React Native developers directly. You can find the full list below:
<details> <summary>List of Removed Android APIs:</summary>The following packages are now internal and can’t be accessed anymore:
com.facebook.react.views.progressbarcom.facebook.react.views.safeareaviewcom.facebook.react.modules.accessibilityinfocom.facebook.react.modules.appstatecom.facebook.react.modules.clipboardcom.facebook.react.modules.devmodulecom.facebook.react.modules.reactdevtoolssettingscom.facebook.react.views.unimplementedviewThe following classes are now either internal or have been removed, so can’t be accessed anymore:
BackHandler.removeEventListenerBaseViewManagerInterfaceBindingImplCompositeReactPackageDebugOverlayTagscreate() from DefaultDevSupportManagerFactoryDevToolsReactPerfLoggerFabricComponentsImageStoreManagerInteropModuleRegistryNativeModulePerfLoggerNoopPrinterNotThreadSafeViewHierarchyUpdateDebugListenerOkHttpCallUtilPrinterHolderPrinterReactDebugOverlayTagsReactNativeFlipperReactViewBackgroundManagerReactViewGroup.getBackgroundColor()ReactVirtualTextShadowNodeReactVirtualTextViewManagerSimpleSettableFutureSwipeRefreshLayoutManagerTaskCompletionSourcejsBundleLoader from DefaultReactHost.getDefaultReactHost()RCTConstants.RCTGetMemoryPressureUnloadLevelpartialBatchDidFlushRCTRuntimeExecutorUseNativeViewConfigsInBridgelessMode
UseTurboModuleInteropForAllTurboModules
CGColorRef with UIColorRCTAppDelegate now requires to use the RCTDependencyProvider to load third party dependenciesReact 19 was released the 6th of December 2024. At the time, we already cut the branch for React Native 0.77 and we already released three RCs for React Native 0.77. It was too late in the release of React Native 0.77 to introduce React 19 in this release.
React 19 will be shipped in React Native 0.78, and we already cut the branch for this version. You can try it by creating a new app with the command:
npx @react-native-community/cli init YourReact19App --version 0.78.0-rc.0
React Native 0.77 contains over 1061 commits from 161 contributors. Thanks for all your hard work!
Thanks to all the additional authors that worked on documenting features in this release post:
display: contents featurereact-native init deprecation cycle contentconsole.log from metroPlease use the React Native Upgrade Helper to view code changes between React Native versions for existing projects, in addition to the Upgrading docs.
To create a new project:
npx @react-native-community/cli@latest init MyProject --version latest
If you use Expo, React Native 0.77 will be supported in Expo SDK 52 (instructions on how to update React Native inside your Expo project to 0.77.0 will be available in a separate Expo blog post in the near future).
:::info 0.77 is now the latest stable version of React Native and 0.74.x moves to unsupported. For more information see React Native's support policy. We aim to publish a final end-of-life update of 0.74 in the near future. :::