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 7 of 14.

Disable precompiled modules via environment variable

Control precompiled module usage with the EXPO_USE_PRECOMPILED_MODULES environment variable, which is read during pod install. For local builds, export it in your shell before running pod install or npx expo run:ios (set to 0 to disable). For EAS Build, create an EAS environment variable using: eas env:set --name EXPO_USE_PRECOMPILED_MODULES --value 0 --visibility plaintext. The CLI will prompt you to select which environment(s) (development, preview, production) the variable applies to.

Troubleshooting precompiled modules: flag version mismatch

Runtime errors like "Unable to recognize flag: <NAME>" on EAS Build (but not locally) indicate that the precompiled artifact's flag list does not match your pinned package version. To fix this, use buildFromSource in package.json to force a source build for the affected packages and file an issue on GitHub if the problem persists.

Disable specific precompiled modules via Autolinking

Configure Expo Autolinking with buildFromSource in package.json to opt out of specific precompiled modules. Use ".*" to opt out of every precompiled module, or list specific package names. The same setting is available for both android and ios. Example: {"expo": {"autolinking": {"android": {"buildFromSource": [".*"]}, "ios": {"buildFromSource": [".*"]}}}}. This is typically only needed when modifying module source code.

react-native-reanimated and react-native-worklets coupling

react-native-reanimated and react-native-worklets are tightly coupled; react-native-reanimated links react-native-worklets at the native level. If you need a source build for either package, you must list both in buildFromSource. Source-building only one produces a mixed precompiled/source linkage that fails to resolve the matching framework at runtime.

Static feature flags require source build for reanimated and worklets

Feature-flag values are baked into the precompiled binary at build time, so any worklets.staticFeatureFlags or reanimated.staticFeatureFlags overrides in package.json are ignored when using precompiled modules. To apply custom feature flags, disable precompiled modules with EXPO_USE_PRECOMPILED_MODULES=0.

Third-party precompiled downloads on EAS Build vs local builds

On EAS Build, third-party libraries like react-native-reanimated and react-native-worklets are automatically downloaded as precompiled XCFrameworks. Local pod install does not fetch them by default. Those packages build from source locally and pick up any staticFeatureFlags overrides automatically, so flag and version mismatches surface primarily on EAS Build. Avoid enabling third-party precompiled downloads for local builds and keep this path scoped to EAS.

Precompiled modules enabled by default

Precompiled Expo Modules are enabled automatically in new and existing projects with a supported SDK version. On Android, enabled by default since SDK 53. On iOS, enabled by default in SDK 56 and later. In SDK 55 on iOS, enabled by default only on EAS Build; to opt in for local builds, set EXPO_USE_PRECOMPILED_MODULES=1 in your shell.

expo-contacts Contact.getAll() replaces getContactsAsync

Contact.getAll({ limit: 20, offset: 10, sortOrder: ContactsSortOrder.GivenName }) replaces the legacy Contacts.getContactsAsync({ fields: [...], pageSize: 20, pageOffset: 10, sort: ... }). The new method uses 'limit' and 'offset' parameters instead of 'pageSize' and 'pageOffset', and sortOrder uses ContactsSortOrder.GivenName enum instead of Contacts.SortTypes.FirstName.

expo-contacts Group API iOS only

Group management for iOS: Group.getAll() replaces Contacts.getGroupsAsync({}), Group.create('Family') replaces Contacts.createGroupAsync('Family'), group.addContact(contact) replaces Contacts.addExistingContactToGroupAsync(contactId, groupId), group.removeContact(contact) replaces Contacts.removeContactFromGroupAsync(contactId, groupId), group.getContacts() retrieves contacts in group, group.getName() gets name, group.setName('name') sets name, group.delete() deletes the group.

expo-contacts permissions API

Permission methods are now standalone functions instead of methods on Contacts: requestPermissionsAsync() replaces Contacts.requestPermissionsAsync(), getPermissionsAsync() replaces Contacts.getPermissionsAsync().

expo-contacts Contact class replaces function-based API

The new expo-contacts API replaces the function-based API with a Contact class. Contacts are represented as class instances that hold only the ID of the native contact. Contact properties (name, company, birthday) are now async getters and setters instead of plain object properties. Sub-records (phones, emails, addresses) are managed via dedicated add*/get*/update*/delete* methods instead of re-writing the entire array.

expo-contacts legacy API migration path

The new class-based expo-contacts API is now stable. The legacy function-based API is available from the 'expo-contacts/legacy' import. Users should migrate from the legacy import to the root 'expo-contacts' import to benefit from the new API and future fixes.

expo-contacts Container API iOS only

Container management for iOS: Container.getAll() replaces Contacts.getContainersAsync({}), Container.getDefault() replaces Contacts.getDefaultContainerIdAsync() and may return null, container.getName() gets name, container.getType() gets type, container.getGroups() gets groups, container.getContacts() gets contacts.

expo-contacts Contact.create() returns instance

Contact.create({ givenName: 'John', familyName: 'Doe' }) returns a Contact instance, not just an ID like the legacy Contacts.addContactAsync() which returned only the ID.

expo-contacts getAllDetails() returns typed field projection

Contact.getAllDetails([ContactField.FULL_NAME, ContactField.PHONES], { limit: 20, offset: 10 }) returns a strongly typed projection narrowed to the requested fields. Results include the contact ID. To call methods on contacts returned from getAllDetails, wrap them in a Contact instance using the constructor: new Contact(results[0].id).

expo-contacts patch() vs update() methods

Two update methods are available for contacts: patch() applies partial updates and only changes provided fields, while update() performs full replacement where all fields not provided will be cleared. Legacy API only had updateContactAsync() which required re-writing the whole contact.

expo-contacts scalar field access pattern

All scalar contact properties are now async getters and setters. Use get* methods to read (e.g., getGivenName(), getFamilyName(), getCompany()) and set* methods to write (e.g., setGivenName(), setFamilyName(), setCompany()). Multiple fields can be retrieved at once using contact.getDetails().

expo-contacts scalar fields reference table

Scalar contact fields with getters and setters: Given name (getGivenName/setGivenName), Family name (getFamilyName/setFamilyName), Middle name (getMiddleName/setMiddleName), Full name (getFullName/no setter), Nickname iOS only (getNickname/setNickname), Prefix (getPrefix/setPrefix), Suffix (getSuffix/setSuffix), Phonetic given name (getPhoneticGivenName/setPhoneticGivenName), Phonetic family name (getPhoneticFamilyName/setPhoneticFamilyName), Company (getCompany/setCompany), Job title (getJobTitle/setJobTitle), Department (getDepartment/setDepartment), Birthday iOS only (getBirthday/setBirthday), Note (getNote/setNote), Image (getImage/setImage), Thumbnail (getThumbnail/no setter), Favourite Android only (getIsFavourite/setIsFavourite).

expo-contacts sub-records methods reference

Sub-records are managed via dedicated methods instead of re-writing arrays: Phone numbers (addPhone, getPhones, updatePhone, deletePhone), Emails (addEmail, getEmails, updateEmail, deleteEmail), Addresses (addAddress, getAddresses, updateAddress, deleteAddress), URLs (addUrlAddress, getUrlAddresses, updateUrlAddress, deleteUrlAddress), Social profiles (addSocialProfile, getSocialProfiles, updateSocialProfile, deleteSocialProfile), IM addresses (addImAddress, getImAddresses, updateImAddress, deleteImAddress), Dates (addDate, getDates, updateDate, deleteDate), Extra names Android only (addExtraName, getExtraNames, updateExtraName, deleteExtraName).

expo-contacts native UI methods

Native UI methods: Contact.presentPicker() replaces Contacts.presentContactPickerAsync() and returns a Contact or null if user did not select. Contact.presentCreateForm(contactData) replaces Contacts.presentFormAsync(null, contactData, { isNew: true }). contact.editWithForm() replaces Contacts.presentFormAsync(contactId). Contact.presentAccessPicker() for iOS 18+ replaces Contacts.presentAccessPickerAsync().

expo-contacts semantic changes from legacy API

Breaking semantic changes: Field names follow platform convention (firstName/lastName become givenName/familyName), Field selection uses typed ContactField enum with result type narrowed to requested fields, The Async suffix is dropped as entire library is asynchronous, Contact properties are async getters/setters not plain properties, Sub-records use add*/get*/update*/delete* methods not array re-writing, Two update methods (patch and update) replace single updateContactAsync.

expo-contacts removed methods

shareContactAsync() and writeContactToFileAsync() are removed from the new API with no replacement. Calls to these functions can be safely deleted.

expo-contacts change listeners

Contact change listeners: addContactsChangeListener(() => { /* contacts changed */ }) replaces Contacts.addContactsChangeListener(), subscription.remove() unsubscribes from individual listeners, removeAllContactsChangeListeners() removes all listeners at once.

Expo Modules API guides coverage

The Expo Modules API guides explain how to add and use native modules in an app using the Expo Modules API.

Expo Router guides coverage

Expo Router guides cover navigation functionalities, a comprehensive Hooks API, Authentication, Redirects, and Testing.

Development process guides coverage

The Development process section contains in-depth information about building an app with Expo, the core development loop mental model, app config, permissions, universal links, custom native code, and web development.

Guides overview structure

The Expo guides are organized into four main sections: Development process (covering app building, config, permissions, universal links, custom native code, and web), Expo Router (navigation, hooks API, authentication, redirects, testing), Expo Modules API (native modules), and Tutorials. Additional guides cover push notifications and integrations.

Server-only modules in RSC tests

Any code imported in RSC test files runs in the server environment. Server-only modules like react-server and server-only can be imported, which is useful for determining if a library is compatible with React Server Components.

Expo universal RSC bundles support platform-specific file extensions

Expo's universal React Server Components bundles include custom server renderers for each platform, supporting platform-specific file extensions. For example, when writing Server Components for an iOS app, platform-specific extensions such as *.ios.js and *.native.ts are resolved.

React Server Components render on Node.js

React Server Components run on Node.js, which means Jest can closely emulate the server-side rendering environment without requiring a Jest preset to communicate between Node.js and a web browser, unlike client-based tests.

Running RSC tests with platform selection

RSC tests can be run with the test:rsc script. When using the multi-runner, the --selectProjects flag selects a specific project. For example, yarn test:rsc --watch --selectProjects rsc/web runs tests only for the web platform.

Custom Jest matchers for RSC testing

jest-expo for RSC adds two custom matchers to Jest's expect: toMatchFlight renders a JSX element using a pseudo-implementation of Expo CLI's render and compares it to a flight string; toMatchFlightSnapshot is the same as toMatchFlight but saves the flight string to a snapshot file. If a component fails to render, the matcher throws an error to fail the test, and the server renderer generates an E: line which is sent to the client to be thrown locally.

Using server-only and client-only modules

The server-only and client-only modules can be imported to assert that a module should not be imported on the client or server respectively. For example, import 'server-only'; at the top of a file ensures that module is server-only.

RSC test file naming and location

Tests for React Server Components should be written in a __rsc_tests__ directory to prevent Jest from running client tests on the server.

Jest-expo RSC presets for platform-specific testing

jest-expo provides four presets for testing React Server Components. jest-expo/rsc/android is an Android-only runner that uses *.android.js, *.native.js, and *.js files. jest-expo/rsc/ios is an iOS-only runner that uses *.ios.js, *.native.js, and *.js files. jest-expo/rsc/web is a web-only runner that uses *.web.js and *.js files. jest-expo/rsc is a multi-runner that combines all three platform-specific runners.

RSC Jest configuration setup

To configure Jest for React Server Components, create a jest-rsc.config.js file in the project's root directory containing module.exports = require('jest-expo/rsc/jest-preset'). Add a test:rsc script to package.json to run the tests, for example: jest --config jest-rsc.config.js.

React package exports for react-server condition

React Server Components support package exports by default. The react-server condition can be used in a package.json exports field to specify which file is imported from a module in RSC environments. For example, an exports field can have react-server pointing to index.react-server.js and default pointing to index.js.

RSC bundling with use client and use server directives

When bundling for React Server Components, all modules are bundled in React Server mode by default. The 'use client' directive can opt out and makes the module become an async reference to the client module. The 'use server' directive is not the opposite of 'use client'; it is instead used to define a React Server Functions file.

RSC example test with LinearGradient

Example RSC test using toMatchFlight matcher: ```tsx /// <reference types="jest-expo/rsc/expect" /> import { LinearGradient } from 'expo-linear-gradient'; it(`renders to RSC`, async () => { const jsx = ( <LinearGradient colors={['cyan', '#ff00ff', 'rgba(0,0,0,0)', 'rgba(0,255,255,0.5)']} testID="gradient" /> ); await expect(jsx).toMatchFlight(`1:I["src/LinearGradient.tsx",[],"LinearGradient"] 0:["$","$L1",null,{"colors":["cyan","#ff00ff","rgba(0,0,0,0)","rgba(0,255,255,0.5)"],"testID":"gradient"},null]`); }); ```

Custom metro.config.js cache store with PostCSS

If using a custom config.cacheStores in metro.config.js, extend the Expo superclass FileStore which has PostCSS support: const { FileStore } = require('@expo/metro-config/file-store'); config.cacheStores = [new FileStore({ root: '/path/to/custom/cache' })];

Tailwind CSS web-only support in Expo

Tailwind CSS supports only the web platform in Expo. For universal support across iOS, Android, and web, use compatibility libraries such as NativeWind or Uniwind which allow creating styled React Native components with Tailwind CSS.

Tailwind CSS requires Metro bundler for web

To use Tailwind CSS in an Expo project, ensure your project is configured to use Metro for web by setting web.bundler to metro in app.json.

Tailwind v3 installation and initialization

To install Tailwind v3 in Expo: run 'npx expo install tailwindcss@3 postcss autoprefixer --dev' to install Tailwind and peer dependencies, then run 'npx tailwindcss init -p' to generate tailwind.config.js and postcss.config.js files. The equivalent commands for other package managers are: yarn 'yarn expo install tailwindcss@3 postcss autoprefixer --dev' and 'yarn dlx tailwindcss init -p'; pnpm 'pnpm expo install tailwindcss@3 postcss autoprefixer --dev' and 'pnpm dlx tailwindcss init -p'; bun 'bun expo install tailwindcss@3 postcss autoprefixer --dev' and 'bunx tailwindcss init -p'.

Tailwind v4 installation

To install Tailwind v4 in Expo: run 'npx expo install tailwindcss @tailwindcss/postcss postcss --dev'. The equivalent commands for other package managers are: yarn 'yarn expo install tailwindcss @tailwindcss/postcss postcss --dev'; pnpm 'pnpm expo install tailwindcss @tailwindcss/postcss postcss --dev'; bun 'bun expo install tailwindcss @tailwindcss/postcss postcss --dev'.

Tailwind v3 content paths in tailwind.config.js

In tailwind.config.js for Tailwind v3, configure the content array to point to template files. Example: content: ['./src/app/**/*.{js,tsx,ts,jsx}']. Add paths for all directories containing template files such as src, components, hooks, or styles. If using Expo Router with a root src directory, configure paths accordingly.

Tailwind v3 global.css with directives

For Tailwind v3, create a global.css file in the root of your project containing: @tailwind base; @tailwind components; @tailwind utilities;

Tailwind v4 global.css import

For Tailwind v4, create a global.css file in the root of your project containing: @import 'tailwindcss';

Tailwind v4 PostCSS configuration

For Tailwind v4, create or update postcss.config.mjs with: export default { plugins: { '@tailwindcss/postcss': {} } };

Import global CSS in root layout only

Always import global CSS in your root _layout.tsx (if using Expo Router) or index.js file, not in nested layouts. Expo Router traverses the dependency graph starting from the root layout. Importing CSS in a nested layout such as app/blog/_layout.tsx causes node_modules CSS to load before custom styles, which breaks intended style order.

Global CSS import for DOM components

If using DOM components with the 'use dom' directive, add the global CSS file import to each module using the directive since they do not share globals.

Using Tailwind with React DOM elements

Tailwind classes can be used directly with React DOM elements via the className attribute: <div className="bg-slate-100 rounded-xl"><p className="text-lg font-medium">Welcome to Tailwind</p></div>

Using Tailwind with React Native web elements

To use Tailwind with React Native web elements, use the { $$css: true } syntax with the _ property: <View style={{ $$css: true, _: 'bg-slate-100 rounded-xl' }}><Text style={{ $$css: true, _: 'text-lg font-medium' }}>Welcome to Tailwind</Text></View>

DOM components as alternative for native Tailwind

To use Tailwind CSS on Android and iOS without a compatibility library, use DOM components with the 'use dom' directive to render Tailwind web code in a WebView on native platforms. Remember to import the global.css file in each DOM component.

CSS must be enabled in metro.config.js for Tailwind

Ensure isCSSEnabled is not set to false in metro.config.js when using Tailwind CSS. CSS support must be enabled for Tailwind to work properly.

Expo CLI respects module side-effects

Expo CLI respects module side-effects according to the Webpack system. Side-effects are used for defining global variables or modifying prototypes and prevent the removal of unused modules and disable module inlining to ensure code runs in the expected order. You can mark modules with side-effects in package.json using the sideEffects field (e.g., '"sideEffects": ["./src/*.js"]'). Side-effects will be removed if they are empty or contain only comments and directives like "use strict" or "use client".

Restructure development-only code with ESM for tree shaking

With Expo tree shaking enabled, restructure development-only code that previously wrapped imports in conditional blocks. Instead of using 'if (process.env.NODE_ENV === "development") { require("./dev-only").doSomething(); }', use ESM imports: 'import { doSomething } from "./dev-only"; if (process.env.NODE_ENV === "development") { doSomething(); }'. In both cases, the entire module will be empty in production bundles, but ESM provides better TypeScript support and accurate static analysis.

Tree shaking requires entire graph creation before optimization

Expo tree shaking requires some transformation to be delayed until after the entire bundle has been created, unlike Metro's default behavior of bundling everything on-demand and lazily. This means less code can be cached, which is generally acceptable because tree shaking is a production-only feature and production bundles often do not use transform caches.

Barrel file optimization with Expo Atlas inspection

When using Expo tree shaking, you can use Expo Atlas to inspect the expanded exports from barrel files. This is useful when the star export pulls in ambiguous exports that prevent expansion, allowing you to see which exports will be removed from the production bundle.

Experimental tree shaking of unused imports and exports (SDK 52+)

Tree shaking to automatically remove unused imports and exports across modules is experimentally available in SDK 52 and later. This optimization only runs in production bundles and can only run on modules using import and export syntax. Files using module.exports and require will not be tree-shaken. Avoid adding Babel plugins like @babel/plugin-transform-modules-commonjs that convert import/export to CommonJS, as this breaks tree-shaking. Modules marked as side-effects will not be removed. All modules in the Expo SDK are shipped as ESM and can be exhaustively tree-shaken.

babel-preset-expo optimizes react-native-web barrel file imports

babel-preset-expo provides a built-in optimization for react-native-web barrel file imports. If you import react-native directly using ESM (static import syntax), the barrel file will be removed from the production bundle and replaced with direct imports to individual modules (e.g., 'import View from "react-native-web/dist/exports/View"'). If you import using require(), the barrel file will be left as-is in the production bundle.

Give your agent this brain