Skip to content

[pull] main from expo:main#881

Merged
pull[bot] merged 18 commits into
code:mainfrom
expo:main
May 19, 2026
Merged

[pull] main from expo:main#881
pull[bot] merged 18 commits into
code:mainfrom
expo:main

Conversation

@pull

@pull pull Bot commented May 19, 2026

Copy link
Copy Markdown

See Commits and Changes for more details.


Created by pull[bot] (v2.0.0-alpha.4)

Can you help keep this open source service alive? 💖 Please sponsor : )

vonovak and others added 18 commits May 19, 2026 17:50
# Why



The Jetpack Compose `Icon` docs didn't mention the
`@expo/material-symbols` package or its `add-material-symbols` CLI. The
universal `Icon` page also linked to a non-existent repo.

# How

- Rewrote `jetpack-compose/icon.mdx` to import icons from
`@expo/material-symbols/<icon>.xml` subpaths and added a section on the
`add-material-symbols` CLI for non-default styles/axes.
- Added the `@expo/material-symbols` install hint on
`universal/icon.mdx` and fixed its broken repo link.
- Mirrored to `unversioned` and `v56.0.0`.

# Test Plan

run website locally or preview at
https://pr-45929.expo-docs.pages.dev/versions/v56.0.0/sdk/ui/jetpack-compose/icon/

<!--
Please describe the motivation for this PR, and link to relevant GitHub
issues, forums posts, or feature requests.
-->


<!--
Please describe how you tested this change and how a reviewer could
reproduce your test, especially if this PR does not include automated
tests! If possible, please also provide terminal output and/or
screenshots demonstrating your test/reproduction.
-->

# Checklist

<!--
Please check the appropriate items below if they apply to your diff.
-->

- [ ] I added a `changelog.md` entry and rebuilt the package sources
according to [this short
guide](https://github.com/expo/expo/blob/main/CONTRIBUTING.md#-before-submitting)
- [ ] This diff will work correctly for `npx expo prebuild` & EAS Build
(eg: updated a module plugin).
- [ ] Conforms with the [Documentation Writing Style
Guide](https://github.com/expo/expo/blob/main/guides/Expo%20Documentation%20Writing%20Style%20Guide.md)
…mes [EXP-23] (#45847)

## Summary

Stacked on #45831

Limits `REACT_NATIVE_OVERRIDE_HERMES_DIR` to local .env files only.

## Checklist

<!--
Please check the appropriate items below if they apply to your diff.
-->

- [x] I added a `changelog.md` entry and rebuilt the package sources
according to [this short
guide](https://github.com/expo/expo/blob/main/CONTRIBUTING.md#-before-submitting)
- [ ] This diff will work correctly for `npx expo prebuild` & EAS Build
(eg: updated a module plugin).
- [ ] Conforms with the [Documentation Writing Style
Guide](https://github.com/expo/expo/blob/main/guides/Expo%20Documentation%20Writing%20Style%20Guide.md)
…11] (#45834)

## Summary

Stacked on #45831

We should use the new API in `@expo/env` to pass the original system env
to all spawn calls, since we're otherwise "upgrading" the loaded
environment variables with the ones already loaded during startup.

## Checklist

<!--
Please check the appropriate items below if they apply to your diff.
-->

- [x] I added a `changelog.md` entry and rebuilt the package sources
according to [this short
guide](https://github.com/expo/expo/blob/main/CONTRIBUTING.md#-before-submitting)
- [ ] This diff will work correctly for `npx expo prebuild` & EAS Build
(eg: updated a module plugin).
- [ ] Conforms with the [Documentation Writing Style
Guide](https://github.com/expo/expo/blob/main/guides/Expo%20Documentation%20Writing%20Style%20Guide.md)
…#45842)

## Summary

Stacked on #45831
Related to #45833

Limits `EXPO_UNSTABLE_MCP_SERVER` raw URL value to the original system
env.

## Checklist

<!--
Please check the appropriate items below if they apply to your diff.
-->

- [x] I added a `changelog.md` entry and rebuilt the package sources
according to [this short
guide](https://github.com/expo/expo/blob/main/CONTRIBUTING.md#-before-submitting)
- [ ] This diff will work correctly for `npx expo prebuild` & EAS Build
(eg: updated a module plugin).
- [ ] Conforms with the [Documentation Writing Style
Guide](https://github.com/expo/expo/blob/main/guides/Expo%20Documentation%20Writing%20Style%20Guide.md)
…1] (#45833)

## Summary

Stacked on #45831

Limits `__UNSAFE_EXPO_HOME_DIRECTORY`, `EXPO_PACKAGER_PROXY_URL`,
`EXPO_UNIVERSE_DIR`, and `EXPO_OVERRIDE_METRO_CONFIG` to be read from
original system env only.

## Checklist

<!--
Please check the appropriate items below if they apply to your diff.
-->

- [x] I added a `changelog.md` entry and rebuilt the package sources
according to [this short
guide](https://github.com/expo/expo/blob/main/CONTRIBUTING.md#-before-submitting)
- [ ] This diff will work correctly for `npx expo prebuild` & EAS Build
(eg: updated a module plugin).
- [ ] Conforms with the [Documentation Writing Style
Guide](https://github.com/expo/expo/blob/main/guides/Expo%20Documentation%20Writing%20Style%20Guide.md)
…nal host redirection [EXP-37] (#45866)

## Summary

In one case in `expo-router` and the redirect case in `expo-server`,
it's possible for catch-all routes to generate leading `//` values,
which then causes the redirect to be interpreted with an arbitrary
hostname target.

## Checklist

<!--
Please check the appropriate items below if they apply to your diff.
-->

- [x] I added a `changelog.md` entry and rebuilt the package sources
according to [this short
guide](https://github.com/expo/expo/blob/main/CONTRIBUTING.md#-before-submitting)
- [ ] This diff will work correctly for `npx expo prebuild` & EAS Build
(eg: updated a module plugin).
- [ ] Conforms with the [Documentation Writing Style
Guide](https://github.com/expo/expo/blob/main/guides/Expo%20Documentation%20Writing%20Style%20Guide.md)
Fixes `<Host modifiers={...}>` being silently dropped on iOS.
`HostProps` (TS) already extends `CommonViewModifierProps` and
`Host/index.tsx` already forwards `modifiers` to the native view, but
`HostViewProps` (Swift) never declared the field. The prop got dropped
during deserialization and every typechecked modifier on `Host` was a
no-op no matter what you passed.

The fix is generic across the registered modifier surface. Once
`HostViewProps` accepts the field and `HostView.body` chains
`.applyModifiers(...)`, the same `ViewModifierRegistry` dispatch path
that already handles every modifier on `UIBaseView` and `TextView`
becomes available to `Host`. One field plus one chain restores the
entire registered set in a single shot. Environment propagation (`tint`,
`font`, `foregroundStyle`, `disabled`), layout (`frame`, `padding`),
chrome (`background`, `clipShape`, `border`, `shadow`), transforms
(`rotationEffect`, `scaleEffect`), filters (`blur`, `opacity`,
`grayscale`), gestures (`onTapGesture`), and iOS 26 Liquid Glass
(`glassEffect`).

```tsx
import { Host, Toggle, Slider, Button, VStack } from '@expo/ui/swift-ui';
import {
  tint, font, foregroundStyle, padding, background, clipShape, shadow,
} from '@expo/ui/swift-ui/modifiers';

<Host
  matchContents
  modifiers={[
    tint('#A78BFA'),
    font({ size: 16, weight: 'semibold', design: 'rounded' }),
    foregroundStyle('#0F172A'),
    padding({ all: 16 }),
    background('#FFF'),
    clipShape('roundedRectangle', 16),
    shadow({ radius: 12, y: 6, color: '#33' }),
  ]}>
  <VStack>
    <Toggle isOn={enabled} onIsOnChange={setEnabled} label="Notifications" />
    <Slider value={volume} onValueChange={setVolume} min={0} max={1} />
    <Button label="Save" onPress={save} />
    {/* every nested control inherits the cascade */}
  </VStack>
</Host>
```

# Test Plan

Test repro with screenshots here:
[`ramonclaudio/expo-ui-host-modifiers-45872-repro`](https://github.com/ramonclaudio/expo-ui-host-modifiers-45872-repro).
The README walks through all 15 cards so you can preview the demo
without rebuilding.

Smoke-tested on iPhone 17 Pro simulator (iOS 26.5, Xcode 26.5) via
`apps/native-component-list`:

- `pnpm build`, `pnpm lint --max-warnings 0`, and `pnpm test` (9 suites,
50 tests) all green in `packages/expo-ui`.
- `et generate-docs-api-data -p expo-ui` produces no diffs caused by
this PR. The TypeScript surface is unchanged so there's nothing to
regenerate. The only diffs it surfaced were unrelated drift from other
PRs, which I discarded.
- Modifiers screen in `apps/native-component-list` renders descendant
`Toggle`, `Picker`, `Slider`, and `Stepper` tinted in `#FF6B6B` and in
`font({ size: 16, weight: 'medium', design: 'rounded' })` once the outer
`Host` is wrapped in the two-modifier chain. Removing the `@Field var
modifiers` from `HostViewProps` and rebuilding makes both effects vanish
at once.
- Standalone repro renders 15 side-by-side cards covering every category
against an unmodified Host. Environment (`tint`, `font`,
`foregroundStyle`, `disabled`), chrome (`background`, `cornerRadius`,
`border`, `shadow` stacked), transforms (`rotationEffect`,
`scaleEffect`), filters (`blur`, `opacity`, `grayscale`), interaction
(`onTapGesture`), and iOS 26 Liquid Glass (`glassEffect`). With the
patch removed, every "WITH MODIFIER" column looks identical to the
default.
Force the mangle hook's xcodebuild invocation to use `SYMROOT=build` so
build products are emitted under ios/Pods/build, matching the directory
scanned for symbols.

# Why

`expo-brownfield` runs an `xcodebuild` step while generating iOS
mangling definitions, then scans `ios/Pods/build` for the built
products. Without an explicit build root, Xcode can emit the products
somewhere else, leaving the scanner with incomplete symbols and causing
generated mangling defines to be incorrect.

Fixes #45801

# How

Passes `SYMROOT=build` to the mangle hook’s `xcodebuild` invocation so
build products are emitted under ios/Pods/build, matching the directory
the mangle scanner already expects.

# Test Plan

- Verified in a brownfield repro that fresh pod install regenerates
mangling definitions for multiple apps.
- Verified `ios/Pods/build/Release-iphonesimulator` is populated after
the mangle build.
- Verified the host app builds and launches, and both embedded apps open
successfully

# Checklist

<!--
Please check the appropriate items below if they apply to your diff.
-->

- [x] I added a `changelog.md` entry and rebuilt the package sources
according to [this short
guide](https://github.com/expo/expo/blob/main/CONTRIBUTING.md#-before-submitting)
- [x] This diff will work correctly for `npx expo prebuild` & EAS Build
(eg: updated a module plugin).
- [x] Conforms with the [Documentation Writing Style
Guide](https://github.com/expo/expo/blob/main/guides/Expo%20Documentation%20Writing%20Style%20Guide.md)

---------

Co-authored-by: Gabriel Donadel <donadeldev@gmail.com>
… to Expo CLI [EXP-11] (#45832)

## Summary

Stacked on #45831

We should use the new API in `@expo/env` to pass the original system env
to the Expo CLI, since we're otherwise "upgrading" the loaded
environment variables with the ones already loaded in `expo-doctor`.

Additionally, this PR adds a check to ensure that `*.local` env files
aren't committed to source control, similar to the other checks for
this. This is already recommended in the docs, but not captured:
https://docs.expo.dev/guides/environment-variables/#using-multiple-env-files-to-define-separate-environments

## Set of changes

- Pass `env` as `getOriginalEnv()` to `expo` commands
- Add `EnvLocalFilesCheck`

## Checklist

<!--
Please check the appropriate items below if they apply to your diff.
-->

- [x] I added a `changelog.md` entry and rebuilt the package sources
according to [this short
guide](https://github.com/expo/expo/blob/main/CONTRIBUTING.md#-before-submitting)
- [ ] This diff will work correctly for `npx expo prebuild` & EAS Build
(eg: updated a module plugin).
- [ ] Conforms with the [Documentation Writing Style
Guide](https://github.com/expo/expo/blob/main/guides/Expo%20Documentation%20Writing%20Style%20Guide.md)
# Why

When we start adding more integrations it would be neat to have them
grouped under one object property, rather then top level one.

This PR changes the router integration API from

```ts
ExpoObserve.configure({
  disableRouterIntegration: false
});
```

to

```ts
ExpoObserve.configure({
  integrations: {
    'expo-router': true,
  },
});
```

Such API gives us more flexibility in the future allowing for per
integration configuration

```ts
ExpoObserve.configure({
  integrations: {
    'xyz': { 'a': '123' },
  },
});
```

# How

1. Change the types to reflect new API
2. Pass the whole `integrations` object to native side
3. Disable expo router by default

# Test Plan

1. CI
2. Manual testing in observe-tester

# Checklist

<!--
Please check the appropriate items below if they apply to your diff.
-->

- [ ] I added a `changelog.md` entry and rebuilt the package sources
according to [this short
guide](https://github.com/expo/expo/blob/main/CONTRIBUTING.md#-before-submitting)
- [ ] This diff will work correctly for `npx expo prebuild` & EAS Build
(eg: updated a module plugin).
- [ ] Conforms with the [Documentation Writing Style
Guide](https://github.com/expo/expo/blob/main/guides/Expo%20Documentation%20Writing%20Style%20Guide.md)
…45974)

## Why

Fixes #45292.

`packages/expo-notifications/android/build.gradle` ships a
`proguard-rules.pro` containing `-keep class
expo.modules.notifications.** {*;}`, but never declares
`consumerProguardFiles 'proguard-rules.pro'`, so the rule is **never**
applied to consumer apps' R8 pass.

In a signed release build with R8 enabled (the typical Expo setup via
`expo-build-properties.enableProguardInReleaseBuilds: true`):

- `NotificationRequest`, `NotificationContent`, and
`NotificationTrigger` subclasses get obfuscated (e.g.
`expo.modules.notifications.notifications.model.NotificationRequest` →
`V6.g`).
- `SharedPreferencesNotificationsStore` serializes these classes to
`SharedPreferences` via `ObjectOutputStream`, then reads them back via
`ObjectInputStream.readObject()`.
- `readObject()` throws `ClassNotFoundException`, which the store
silently swallows and returns `null` per entry.
- `Notifications.getAllScheduledNotificationsAsync()` therefore returns
`[]` regardless of how many notifications were scheduled.
- The `AlarmManager` broadcast also fails to deserialize on its
scheduled trigger time → **no scheduled notifications ever fire**.
- All of this is silent — no log, no error surfaced to JS.

## How

One-line addition inside the existing `defaultConfig` block, mirroring
the pattern already used by sibling packages — e.g.
[`expo-modules-core/android/build.gradle`](https://github.com/expo/expo/blob/main/packages/expo-modules-core/android/build.gradle):

```diff
 defaultConfig {
   versionCode 21
   versionName '56.0.9'
   testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
+  consumerProguardFiles 'proguard-rules.pro'
 }
```

The `proguard-rules.pro` file already exists in the package; only the
directive that wires it up was missing.

## Test plan

- [ ] Build a release AAB of an Expo app with `expo-notifications` and
`expo-build-properties.enableProguardInReleaseBuilds: true` against this
branch.
- [ ] Schedule a notification with
`Notifications.scheduleNotificationAsync({ trigger: { type: 'date',
date: Date.now() + 60_000 }, content: { title: 'X', body: 'Y' } })`.
- [ ] `Notifications.getAllScheduledNotificationsAsync()` returns the
scheduled entry (not `[]`).
- [ ] Notification fires at the scheduled time.
- [ ] Reproduce the failure mode by building the same app against `main`
without this patch — `getAllScheduledNotificationsAsync()` returns `[]`
and nothing fires.

---

cc @vonovak — you asked for a PR with a fix on the issue.
cc @a-resh — original reporter; happy to defer if you already have a PR
in progress, just let me know.

---------

Co-authored-by: Vojtech Novak <vonovak@gmail.com>
# Why
The `getFavorite()` and `setFavorite()` functions are already supported
on iOS. This PR adds support for these functions on Android.

# How
- The `MediaStore.MediaColumns.IS_FAVORITE` column on Android tells us
whether an asset is marked as a favorite. Unfortunately, it wasn't added
until Android 11, which means that third-party system gallery apps
developed their own workarounds. Consequently, marking an asset as a
favorite using the `setFavorite` method might not be visible in them.
- Extended the `AssetDelegate` interface and implemented the functions
for `AssetModernDelegate` and `AssetLegacyDelegate`.

# Test Plan
Removed the iOS-only condition from the already existing tests in
BareExpo ✅
# Why

`useObserveForRouter` had an inline guard that throws if the router
integration is toggled on/off during a screen's lifecycle. The pattern —
snapshot the initial value in a ref, compare on every render, throw on
mismatch — is reusable, and we'll want it again as more integrations
land.

# How

1. Extract a `useAssertValueDoesNotChange(value, error)` hook in
`src/useAssertValueDoesNotChange.ts` — reference-equality check against
the initial render value, throws `error` on mismatch.
2. Replace the inline ref/throw in `useObserveForRouter` with a call to
the new hook.
3. Re-export `isInitialized` as `isExpoRouterInitialized` from the
expo-router integration barrel for clarity at call sites.
4. In `useObserve` call `useObserveForRouter` conditionally only when
router is enabled
5. Add native unit tests covering: initial render, stable rerenders,
value change, reference equality on equal-shape objects, independent
sibling instances, undefined → undefined, and undefined → defined
transitions.

# Test Plan

1. CI
2. `useAssertValueDoesNotChange.test.native.tsx`

# Checklist

<!--
Please check the appropriate items below if they apply to your diff.
-->

- [ ] I added a `changelog.md` entry and rebuilt the package sources
according to [this short
guide](https://github.com/expo/expo/blob/main/CONTRIBUTING.md#-before-submitting)
- [ ] This diff will work correctly for `npx expo prebuild` & EAS Build
(eg: updated a module plugin).
- [ ] Conforms with the [Documentation Writing Style
Guide](https://github.com/expo/expo/blob/main/guides/Expo%20Documentation%20Writing%20Style%20Guide.md)
…ons on Android via the `EXPO_ROUTER_DISABLE_NATIVE_TABS_MD` environment variable (#45857)

# Why

Solves: #43614

# How

1. Add the `EXPO_ROUTER_DISABLE_NATIVE_TABS_MD` env
2. When it's set to true replace
`'expo-router/build/native-tabs/utils/materialIconConverter.android.js',`
with
`'expo-router/build/native-tabs/utils/materialIconConverter-not-implemented.js'`


# Test Plan

1. CI
2. Router e2e

`EXPO_ROUTER_DISABLE_NATIVE_TABS_MD=1 pnpm start:native-navigation
--clear`

<img height="512" alt="Screenshot_1778865586"
src="https://github.com/user-attachments/assets/3c453392-35a1-4b02-95f2-09ecbcd763d3"
/>


`pnpm start:native-navigation`

<img height="512" alt="Screenshot_1778865489"
src="https://github.com/user-attachments/assets/092f138e-0d23-4e12-a998-3907ed90f7f2"
/>

<img width="832" height="163" alt="image"
src="https://github.com/user-attachments/assets/2745ff3f-beb2-4fd9-b0a0-b4f0ab5090aa"
/>


# Checklist

<!--
Please check the appropriate items below if they apply to your diff.
-->

- [x] I added a `changelog.md` entry and rebuilt the package sources
according to [this short
guide](https://github.com/expo/expo/blob/main/CONTRIBUTING.md#-before-submitting)
- [ ] This diff will work correctly for `npx expo prebuild` & EAS Build
(eg: updated a module plugin).
- [ ] Conforms with the [Documentation Writing Style
Guide](https://github.com/expo/expo/blob/main/guides/Expo%20Documentation%20Writing%20Style%20Guide.md)
# Why

`bare-expo` (via `native-component-list` and `test-suite`) is one of the
apps we exercise with `expo-observe`, but the entry screens never called
`markInteractive()`. That means TTI/interactive metrics for those flows
were missing or inaccurate, making it harder to dogfood `expo-observe`
against a real app.

# How

1. Add `expo-observe` as a workspace dependency to
`native-component-list` and `test-suite`.
2. Call `useObserve().markInteractive()` from the screens that act as
the interactive landing surface:
   - `ComponentListScreen`
- `Screens/index` `MainScreen` (converted from a class to a function
component so it can use hooks)
   - `test-suite` `SelectScreen`

# Test Plan

1. CI
2. Manual testing in bare-expo — verified `markInteractive` fires and
shows up in `expo-observe` metrics.

# Checklist

<!--
Please check the appropriate items below if they apply to your diff.
-->

- [ ] I added a `changelog.md` entry and rebuilt the package sources
according to [this short
guide](https://github.com/expo/expo/blob/main/CONTRIBUTING.md#-before-submitting)
- [ ] This diff will work correctly for `npx expo prebuild` & EAS Build
(eg: updated a module plugin).
- [ ] Conforms with the [Documentation Writing Style
Guide](https://github.com/expo/expo/blob/main/guides/Expo%20Documentation%20Writing%20Style%20Guide.md)
…tead of `android.adaptiveIcon` (#45704)

Why:
- Closes #17969.
- When both `android.icon` and `android.adaptiveIcon.foregroundImage`
were configured, `@expo/prebuild-config` used the adaptive foreground
image for all Android launcher icon outputs. That made legacy Android
launcher icons use the inset adaptive foreground instead of the explicit
legacy `android.icon`.

How:
- Keep `android.icon` as the source for legacy `ic_launcher.webp` and
`ic_launcher_round.webp` when it is provided.
- Keep `android.adaptiveIcon.foregroundImage` as the source for adaptive
foreground assets on Android 8+.
- Preserve the existing fallback for projects that only provide adaptive
icon config by generating pseudo-adaptive legacy icons from the adaptive
assets.
- Added focused coverage that writes distinct legacy and adaptive assets
and verifies the generated legacy and adaptive files use the intended
sources.
- Rebuilt `@expo/prebuild-config` generated output.

Test Plan:
- `pnpm --filter @expo/prebuild-config test -- withAndroidIcons-test`
passed: 1 suite, 10 tests.
- `pnpm --filter @expo/prebuild-config lint` passed.
- `pnpm --filter @expo/prebuild-config build` passed.
- `pnpm --filter @expo/prebuild-config test` passed: 12 suites, 69
tests, 6 snapshots.
- `git diff --cached --check` passed before commit.

Risk:
- Source files changed: 1.
- Test files changed: 1.
- Changelog files changed: 1.
- Generated build files changed: 3.
- No native runtime or docs changes.
- Behavioral scope is limited to Android launcher icon asset generation
in `@expo/prebuild-config`.
## Summary

Accidentally inverted check from recent change. Tests caught this

## Checklist

<!--
Please check the appropriate items below if they apply to your diff.
-->

- [x] I added a `changelog.md` entry and rebuilt the package sources
according to [this short
guide](https://github.com/expo/expo/blob/main/CONTRIBUTING.md#-before-submitting)
- [ ] This diff will work correctly for `npx expo prebuild` & EAS Build
(eg: updated a module plugin).
- [ ] Conforms with the [Documentation Writing Style
Guide](https://github.com/expo/expo/blob/main/guides/Expo%20Documentation%20Writing%20Style%20Guide.md)

---------

Co-authored-by: Expo Bot <34669131+expo-bot@users.noreply.github.com>
## Summary

See: expo/spawn-async#55

## Checklist

<!--
Please check the appropriate items below if they apply to your diff.
-->

- [x] I added a `changelog.md` entry and rebuilt the package sources
according to [this short
guide](https://github.com/expo/expo/blob/main/CONTRIBUTING.md#-before-submitting)
- [ ] This diff will work correctly for `npx expo prebuild` & EAS Build
(eg: updated a module plugin).
- [ ] Conforms with the [Documentation Writing Style
Guide](https://github.com/expo/expo/blob/main/guides/Expo%20Documentation%20Writing%20Style%20Guide.md)
@pull pull Bot locked and limited conversation to collaborators May 19, 2026
@pull pull Bot added the ⤵️ pull label May 19, 2026
@pull pull Bot merged commit 4479c26 into code:main May 19, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

8 participants