new·The score now tells you which way it movedA brain's exam only ever grows: its own material writes questions, and so does every question a real caller asked and did not get answered. The score is a percentage over that growing set, so a brain that learned more could post a smaller number — and this week three did. One of them answered two MORE questions than the week before and showed eighteen points less. Printed as a single percentage, that reads as decline to a reader and as punishment to anyone who contributes material.all news →
mozg.beta
Sign in

Expo & React Native · all subjects

guides

835 notes in this subject, read out of this brain and free to use. This is page 5 of 14.

Terser is the default minifier in Expo CLI

Terser is the default minifier used by Expo CLI starting with Metro@0.73.0.

Example: metro.config.js with drop_console to remove all console logs

const { getDefaultConfig } = require('expo/metro-config'); const config = getDefaultConfig(__dirname); config.transformer.minifierConfig = { compress: { drop_console: true, }, }; module.exports = config;

Example: Minification removes comments and collapses strings

Input: // This comment will be stripped\nconsole.log('a' + ' ' + 'long' + ' string' + ' to ' + 'collapse');\n\nOutput after minification: console.log('a long string to collapse');

Remove console logs in production with drop_console

To remove console logs from production builds, set the drop_console option in the Terser minifier config under config.transformer.minifierConfig.compress. You can pass true to remove all console logs, or an array of console types like ['log', 'info'] to remove only specific types while preserving others like console.warn and console.error.

Minification removes unnecessary code characters in production builds

Minification is an optimization build step that removes unnecessary characters such as whitespace, comments, and shortens static operations from source code. This reduces final size and improves load times. In Expo CLI, minification is performed on JavaScript files during production export when npx expo export, npx expo export:embed, eas build, and similar commands run.

Configure Uglify minifier in metro.config.js

To use uglify-es as the minifier, install metro-minify-uglify (ensuring its version matches the metro version in your project), then set config.transformer.minifierPath to 'metro-minify-uglify' and pass uglify options to config.transformer.minifierConfig in metro.config.js. Install with npm install --save-dev metro-minify-uglify (or yarn add --dev metro-minify-uglify, pnpm add --save-dev metro-minify-uglify, or bun add --dev metro-minify-uglify).

esbuild minifier for faster compression

esbuild is available as an alternative minifier that minifies exponentially faster than uglify-es and Terser. It is configured via metro-minify-esbuild package.

Preserve comments with @preserve directive

Comments can be preserved during minification by using the /** @preserve */ directive within the comment.

Unsafe Terser compression options for additional optimization

Terser supports unsafe compress options that provide additional compression but may not work in all JavaScript engines. These include: unsafe, unsafe_arrows, unsafe_comps, unsafe_Function, unsafe_math, unsafe_symbols, unsafe_methods, unsafe_proto, unsafe_regexp, unsafe_undefined, and unused. These can be enabled in config.transformer.minifierConfig.compress.

Example: metro.config.js with Uglify configuration

const { getDefaultConfig } = require('expo/metro-config'); const config = getDefaultConfig(__dirname); config.transformer.minifierPath = 'metro-minify-uglify'; config.transformer.minifierConfig = { // Options: https://github.com/mishoo/UglifyJS#compress-options }; module.exports = config;

Configure Terser minifier in metro.config.js

To configure Terser as the minifier, set config.transformer.minifierPath to 'metro-minify-terser' and pass terser options to config.transformer.minifierConfig in metro.config.js. First install the package with npm install --save-dev metro-minify-terser (or yarn add --dev metro-minify-terser, pnpm add --save-dev metro-minify-terser, or bun add --dev metro-minify-terser).

Example: metro.config.js with unsafe Terser compression options

const { getDefaultConfig } = require('expo/metro-config'); const config = getDefaultConfig(__dirname); config.transformer.minifierPath = 'metro-minify-terser'; config.transformer.minifierConfig = { compress: { unsafe: true, unsafe_arrows: true, unsafe_comps: true, unsafe_Function: true, unsafe_math: true, unsafe_symbols: true, unsafe_methods: true, unsafe_proto: true, unsafe_regexp: true, unsafe_undefined: true, unused: true, }, }; module.exports = config;

pnpm isolated dependencies troubleshooting

If you encounter issues with isolated installations with pnpm, switch to the hoisted installation strategy by changing the nodeLinker setting in pnpm-workspace.yaml to: nodeLinker: hoisted

SDK 54+ isolated dependencies support

From SDK 54, Expo supports isolated dependencies and isolated installations. Bun and pnpm have first-class support for isolated installs. With isolated dependencies, package managers create a central directory with Node modules and create links instead of hoisting packages. This enforces that packages may only access explicitly declared dependencies.

Workspace dependency syntax

To add a monorepo package as a dependency, use "package-name": "*" in package.json. Bun, npm, and pnpm also support "workspace:*" syntax, which ensures the workspace package never resolves a published package of the same name from the npm registry.

Creating an Expo app in monorepo

To create a new Expo app in a monorepo, use npm: npx create-expo-app@latest --template default@sdk-57 apps/cool-app; yarn: yarn create expo-app --template default@sdk-57 apps/cool-app; pnpm: pnpm create expo-app --template default@sdk-57 apps/cool-app; bun: bun create expo --template default@sdk-57 apps/cool-app. After creating the app, install dependencies from the root directory of the monorepo.

pnpm workspaces configuration

For pnpm, create a pnpm-workspace.yaml file in the root of the repository instead of using a workspaces property in package.json. Example content: packages:\n - 'apps/*'\n - 'packages/*'

npm, Yarn, and Bun workspaces configuration

For npm, Yarn, and Bun, add a workspaces property to the root package.json file that specifies glob patterns for all workspaces in the monorepo. Example: {"workspaces": ["apps/*", "packages/*"]}

Automatic Metro configuration for monorepos in SDK 52+

Expo configures Metro automatically for monorepos starting with SDK 52. You do not have to manually configure Metro when using monorepos if you use expo/metro-config. If you previously configured Metro manually and have a metro.config.js that modifies watchFolders, resolver.nodeModulesPath, resolver.extraNodeModules, or resolver.disableHierarchicalLookup, you should delete these properties and run npx expo start --clear once to erase the outdated Metro cache.

Monorepo structure conventions

A basic monorepo structure typically includes: apps directory containing multiple projects including Expo apps; packages directory containing different packages used by apps; and a root package.json file. The root package.json is the main configuration for monorepos and may contain tools installed for all projects in the repository.

Monorepo first-class support

Expo has first-class support for monorepos managed with package managers supporting workspaces: Bun, npm, pnpm, and Yarn (v1 Classic and Berry). Expo automatically detects monorepos and configures new app projects added to a monorepo based on the workspace configuration.

Monorepo complexity tradeoff

Monorepos are not for every project. They are useful if multiple apps live in a single repository and share code, or can be helpful to colocate native modules with apps. However, the tradeoff is increased complexity when setting up and configuring tooling. Before setting up a monorepo, check whether your tools and libraries work well within a monorepo.

Dynamic native module path resolution for monorepos

To avoid hardcoded paths that fail in monorepos due to hoisting, use Node's require.resolve() to find package locations dynamically. Android example: apply from: new File(["node", "--print", "require.resolve('react-native/package.json')"].execute(null, rootDir).text.trim(), "../react.gradle") iOS example: require File.join(File.dirname(`node --print "require.resolve('react-native/package.json')"`), "scripts/react_native_pods") This approach explicitly refers to package.json to find the root location of the package.

Monorepo with TV projects dependencies

When a TV project is contained in a monorepo with other Expo projects, see the Building for TV dependencies section for special react-native dependency requirements.

Dependency resolution using resolutions and overrides

To force a single version of a package across the monorepo when not directly resolvable, use the resolutions property in the root package.json for npm, Yarn, and Bun. For npm specifically, use the overrides property instead. Example: {"resolutions": {"react": "^19.0.0"}}

Duplicate native packages not supported

Duplicate React Native versions in a single monorepo are not supported. Duplicate React versions in a single app will cause runtime errors. Duplicate versions of Turbo and Expo modules may cause runtime or build errors.

Check for duplicate packages in monorepo

Use your package manager to check for multiple versions of packages. npm: npm why react-native; yarn: yarn why react-native; pnpm: pnpm why --depth=10 react-native; bun: bun pm why react-native. Look for multiple versions of the same package (e.g., react-native@0.79.5 and react-native@0.81.0) in the output.

React Native community package replacements for New Architecture

For New Architecture compatibility, replace: @react-native-community/masked-view with @react-native-masked-view/masked-view; @react-native-community/clipboard with @react-native-clipboard/clipboard; rn-fetch-blob with react-native-blob-util; react-native-fs with expo-file-system or a fork of react-native-fs; react-native-geolocation-service with expo-location; react-native-datepicker with react-native-date-picker or @react-native-community/datetimepicker.

react-native-maps New Architecture support

react-native-maps version 1.20.x (default for SDK 53) supports the New Architecture with the interop layer for most features. Version 1.21.0 is a New Architecture-first version still stabilizing. Alternatively, use expo-maps if your app can require iOS 17 minimum or does not need iOS maps support.

Stripe React Native New Architecture support

@stripe/react-native supports the New Architecture starting with version 0.45.0, which is the default for SDK 53.

Existing React Native project disable New Architecture SDK 54 and earlier

For existing React Native projects on SDK 54 and earlier: Android requires setting newArchEnabled=false in gradle.properties. iOS requires setting newArchEnabled to 'false' in Podfile.properties.json if it exists, or follow React Native New Architecture working group docs otherwise.

React Native 0.74+ Interop Layers for New Architecture

Since React Native 0.74, various Interop Layers are enabled by default, allowing many libraries built for the old architecture to work on the New Architecture without changes. However, the interop is not perfect and libraries shipping or depending on third-party native code may need updates.

Testing New Architecture with incompatible libraries

You can test the New Architecture in your app even if some libraries aren't supported by temporarily removing those incompatible libraries. Create a new branch, remove incompatible libraries until your app runs, then report issues to library authors or switch to compatible alternatives found on React Native Directory.

New React features only on New Architecture

New React and React Native features are coming to the New Architecture only. Examples include full support for Suspense and new CSS features for layouts, sizing, and blending that are not implemented in the legacy architecture.

Expo Go only supports New Architecture

Expo Go only supports the New Architecture; the legacy architecture is not supported in Expo Go.

Enable New Architecture in SDK 52

To enable the New Architecture on SDK 52, set newArchEnabled to true at the root of the expo object in app.json, optionally per-platform like 'android': { 'newArchEnabled': true }. Then create a new build using expo prebuild --clean with expo run:android/run:ios or eas build -p android/ios.

Default New Architecture in new projects from SDK 52

As of SDK 52, all new projects created with create-expo-app are initialized with the New Architecture enabled by default.

Configure React Native Directory check in package.json

The reactNativeDirectoryCheck can be configured in the package.json expo.doctor object. Available options are: enabled (boolean, default true in SDK 52+, false earlier; can be overridden with EXPO_DOCTOR_ENABLE_DIRECTORY_CHECK environment variable where 0=false, 1=true), exclude (array of exact package names and regex patterns to skip), and listUnknownPackages (boolean, default true, warns if packages missing from directory).

Validate dependencies with expo-doctor

Run npx expo-doctor (or yarn dlx, pnpm dlx, bunx with appropriate package manager) to check your dependencies against React Native Directory data to learn which libraries are unmaintained or incompatible/untested with the New Architecture.

EAS Build New Architecture adoption

As of January 2026, approximately 83% of SDK 54 projects built with EAS Build use the New Architecture.

Expo SDK support for New Architecture

As of SDK 53, all expo-* packages in the Expo SDK support the New Architecture, including bridgeless mode.

Expo Modules API supports New Architecture by default

All modules written using the Expo Modules API support the New Architecture by default. No additional work is needed to use custom native modules built with this API with the New Architecture.

Legacy architecture frozen in June 2025

The legacy React Native architecture was frozen in June 2025, meaning no new features or bugfixes are being developed for it.

React Native version for SDK 55

SDK 55 uses React Native 0.83, which inherits the behavior of React Native 0.82 being the first version to run entirely on the New Architecture with no option to disable it.

New Architecture definition in React Native

The New Architecture is a complete refactoring of the internals of React Native designed to solve limitations of the original React Native architecture discovered over years of usage in production at Meta and other companies.

SDK 55 and later require New Architecture

SDK 55 and later run entirely on the New Architecture. The New Architecture is always enabled and cannot be disabled. If you need to use the legacy architecture, you must use SDK 54 or earlier.

No known New Architecture issues in Expo libraries

There are no known issues specific to the New Architecture in Expo libraries.

Native apps recommended over PWAs for offline support

Expo recommends building native apps whenever possible as they have the best offline support. PWAs are a good option for desktop users but lack the update capabilities of native apps—native apps can be updated through the app store to clear cached experiences, while PWAs with service workers may force users to manually clear the cache.

Inspect service worker in Chrome DevTools

To inspect a service worker after hosting your website, open Chrome DevTools and navigate to Application > Service Workers to view the registered service worker.

Build script for Expo web with service workers

In package.json, create a build script that runs both Expo export and Workbox generation in order: {"scripts": {"build:web": "expo export -p web && npx workbox-cli generateSW workbox-config.js"}}

Workbox CLI wizard configuration for Expo

When running 'npx workbox-cli wizard' for an Expo web app, use these responses: root directory is 'dist/', precache file types are 'js, html, ttf, ico, json', service worker file location is 'dist/sw.js', configuration save location is 'workbox-config.js', and answer 'No' to whether the manifest includes search parameters other than 'utm_' or 'fbclid'.

Register service worker in static/server rendered app

For static or server rendering, add service worker registration in src/app/+html.tsx using dangerouslySetInnerHTML. The script checks if 'serviceWorker' in navigator, then on the 'load' event, calls navigator.serviceWorker.register('/sw.js') with then/catch handlers to log success or errors.

Register service worker in single-page app HTML

In public/index.html for single-page apps, add a service worker registration script to the <head> tag: <script>if ('serviceWorker' in navigator) { window.addEventListener('load', () => { navigator.serviceWorker.register('/sw.js').then(registration => { console.log('Service Worker registered with scope:', registration.scope); }).catch(error => { console.error('Service Worker registration failed:', error); }); }); }</script>

Service workers with Workbox for offline support

Use Google's Workbox to add service workers for offline support. Follow the Workbox CLI guide at https://developer.chrome.com/docs/workbox/modules/workbox-cli/, but use 'npx expo export -p web' instead of the default build script. Be cautious with service workers as they can cause unexpected behavior and aggressive caching can prevent users from requesting updates easily.

Link PWA manifest for static and server rendered apps

For static or server rendering (web.output set to 'static' or 'server'), create src/app/+html.tsx and add <link rel="manifest" href="/manifest.json" /> to the <head> component. This file runs in Node.js environments during static rendering, not in the browser.

Link PWA manifest for single-page apps

For single-page apps (web.output set to 'single'), create a template HTML file at public/index.html using 'npx expo customize public/index.html', then add <link rel="manifest" href="/manifest.json" /> to the <head> tag.

PWA manifest.json structure and location

Create a PWA manifest file at public/manifest.json with the following fields: short_name (string), name (string), icons (array of objects with src, sizes, and type properties), start_url (string, typically "."), display (string, typically "standalone"), theme_color (hex color string), and background_color (hex color string). Icons should include favicon.ico (64x64 32x32 24x24 16x16), logo192.png (192x192), and logo512.png (512x512).

PWA favicon configuration in app.json

Expo CLI automatically generates the favicon.ico file based on the web.favicon field in app.json. For example: {"web": {"favicon": "./assets/favicon.png"}}. Alternatively, you can manually create a favicon.ico file in the public directory to specify the icon.

React Compiler Babel configuration options

React Compiler accepts additional settings via the react-compiler object in Babel configuration, including compilationMode and panicThreshold. Web-specific settings can be configured separately under web.react-compiler.

React Compiler incremental adoption example

Example babel.config.js configuration for incremental adoption: module.exports = function (api) { api.cache(true); return { presets: [['babel-preset-expo', {'react-compiler': {sources: filename => { return filename.includes('src/path/to/dir'); }}}]] }; };

Give your agent this brain