Skip to content

Static assets fail to load in monorepo since 0.81 #3909

Description

@Aure77

What happened?

Since I upgrade to RN 0.81, images (assets) are not loaded in dev (with metro server).
I think there is a issue with monorepo settings (enhanceMiddleware) with RN 0.81+

Affected Package

@rnx-kit/metro-config

Version

2.2.1

Which platforms are you seeing this issue on?

  • Android
  • iOS
  • macOS
  • Windows

System Information

System:
  OS: macOS 15.6
  CPU: (8) arm64 Apple M1
  Memory: 254.28 MB / 16.00 GB
  Shell:
    version: "5.9"
    path: /bin/zsh
Binaries:
  Node:
    version: 20.19.0
    path: /private/var/folders/8k/qyy417_57vb1nd1p1985ggdr0000gp/T/xfs-632f35ae/node
  Yarn:
    version: 4.6.0
    path: /private/var/folders/8k/qyy417_57vb1nd1p1985ggdr0000gp/T/xfs-632f35ae/yarn
  npm:
    version: 10.8.2
    path: ~/.local/share/mise/installs/node/20.19.0/bin/npm
  Watchman:
    version: 2025.11.10.00
    path: /opt/homebrew/bin/watchman
Managers:
  CocoaPods:
    version: 1.15.2
    path: /Users/a389216/.gem/bin/pod
SDKs:
  iOS SDK:
    Platforms:
      - DriverKit 24.5
      - iOS 18.5
      - macOS 15.5
      - tvOS 18.5
      - visionOS 2.5
      - watchOS 11.5
  Android SDK:
    API Levels:
      - "23"
      - "30"
      - "31"
      - "33"
      - "34"
      - "35"
      - "36"
      - "36"
    Build Tools:
      - 28.0.3
      - 29.0.2
      - 30.0.3
      - 31.0.0
      - 32.0.0
      - 32.0.0
      - 33.0.1
      - 33.0.2
      - 34.0.0
      - 35.0.0
      - 35.0.1
      - 36.0.0
      - 36.1.0
    System Images:
      - android-24 | Google APIs ARM 64 v8a
      - android-30 | Wear OS 3 ARM 64 v8a
      - android-31 | Google Play ARM 64 v8a
      - android-34 | Google Play ARM 64 v8a
      - android-35 | Google Play ARM 64 v8a
      - android-36.1 | Pre-Release 16 KB Page Size Google Play ARM 64 v8a
    Android NDK: Not Found
IDEs:
  Android Studio: 2025.2 AI-252.27397.103.2522.14514259
  Xcode:
    version: 16.4/16F6
    path: /usr/bin/xcodebuild
Languages:
  Java:
    version: 17.0.13
    path: /Users/a389216/.local/share/mise/installs/java/zulu-17.54.21/bin/javac
  Ruby:
    version: 3.4.4
    path: /Users/a389216/.local/share/mise/installs/ruby/3.4.4/bin/ruby
npmPackages:
  "@react-native-community/cli": Not Found
  react: Not Found
  react-native: Not Found
  react-native-macos: Not Found
npmGlobalPackages:
  "*react-native*": Not Found
Android:
  hermesEnabled: true
  newArchEnabled: false
iOS:
  hermesEnabled: true
  newArchEnabled: false

Steps to Reproduce

Use default metro-config settings in a monorepo with RN 0.81+:

const { makeMetroConfig } = require("@rnx-kit/metro-config");

module.exports = makeMetroConfig({});

Fail silently, image are not loaded:
Image
(close image is not loaded for ex)

I mitigated issue on Android by overriding/remove enhanceMiddleware:

const projectRoot = __dirname;
const workspaceRoot = path.resolve(projectRoot, "../..");

module.exports = makeMetroConfig({
  server: {
    enhanceMiddleware: null, // override @rnx-kit/metro-config middleware (bug since RN 0.81+ ?)
  },
  projectRoot,
  watchFolders: [
    workspaceRoot
  ],
});

But path with ".." doesn't works with iOS. So monorepo support doesn't works anymore with @rnx-kit/metro-config

Code of Conduct

  • I agree to follow this project's Code of Conduct

Activity

  1. changed the title [-]Static images fail to load in monorepo since 0.81[/-] [+]Static assets fail to load in monorepo since 0.81[/+] on Jan 5, 2026
  2. tido64 commented on Jan 5, 2026

    @tido64
    Member

    Thanks for reporting this! Would you be able to verify that this fix works: #3910?

  3. Aure77 commented on Jan 5, 2026

    @Aure77
    Author

    Thanks for your quick solution, it's works !

  4. Aure77 commented on Jan 5, 2026

    @Aure77
    Author

    As enhanceMiddleware has been deprecated for a while in metro. Maybe useful to switch to rewriteRequestUrl for this use case ?

  5. tido64 commented on Jan 6, 2026

    @tido64
    Member

    Thanks for verifying. I've just noticed the deprecation as well. Added rewriteRequestUrl but kept enhanceMiddleware for backwards compatibility.

  6. Aure77 commented on Jan 6, 2026

    @Aure77
    Author

    I got something strange. Yesterday it seems worked on both platform. This morning, after removing metro cache, app cache, starting from clean node_modules with metro 0.83.3, it's only works on iOS... On Android, assets outside project root are not prefixed with public path (/assets) when handled by server:

    Metro:Server:parseBundleOptionsFromBundleRequestUrl entryFile:    ./index +13s
    Metro:Server Handling request:    /node_modules/react-native/Libraries/LogBox/UI/LogBoxImages/close.png?platform=android&hash=369745d4a4a6fa62fa0ed495f89aa964 +265ms
    Metro:Server Formatted as:    http://10.0.2.2:8081/node_modules/react-native/Libraries/LogBox/UI/LogBoxImages/close.png?platform=android&hash=369745d4a4a6fa62fa0ed495f89aa964 +1ms
    

    As there is no /assets prefix, middleware is not called... I don't understand why metro doesn't consider this png as asset resource. Any idea ?

  7. tido64 commented on Jan 6, 2026

    @tido64
    Member

    The only thing I can think of is if the config is missing png in assetExts, or added to sourceExts by accident. You can inspect your config by running node --print 'require("./metro.config.js")'.

    Image

    Is this with or without @rnx-kit/metro-config?

  8. Aure77 commented on Jan 6, 2026

    @Aure77
    Author

    Yes it was my suspicion too but I already checked that (& it's works on iOS...):

      resolver: {
        assetExts: [
          'bmp',  'gif',  'jpg',  'jpeg',
          'png',  'psd',  'svg',  'webp',
          'xml',  'm4v',  'mov',  'mp4',
          'mpeg', 'mpg',  'webm', 'aac',
          'aiff', 'caf',  'm4a',  'mp3',
          'wav',  'html', 'pdf',  'yaml',
          'yml',  'otf',  'ttf',  'zip'
        ],
        assetResolutions: [ '1', '1.5', '2', '3', '4' ],
        platforms: [ 'android', 'ios' ],
        sourceExts: [ 'js', 'jsx', 'json', 'ts', 'tsx' ],
        ...
    

    Images from project are loaded correctly. Issue only appears with assets outside project root.
    Yes it's with @rnx-kit/metro-config from your PR (with makeMetroConfig({}), so default settings).

  9. Aure77 commented on Jan 6, 2026

    @Aure77
    Author

    I found a solution with assetPlugins:

    transformer: {
      assetPlugins: [
        require.resolve("@rnx-kit/metro-config/src/assetPluginForMonorepos") // or any other path with custom asset plugin
      ],
    },

    I used similar way to update httpServerLocation with unstable_path (like in your PR):

    assetPlugin function:

    function assetPlugin(assetData) {
      assetData.httpServerLocation = assetData.httpServerLocation.replace(
        "/assets/../",
        "/assets?unstable_path=../"
      );
      return assetData;
    }

    It seems that Android use a different request url when httpServerLocation contains "..". It's modified before to be passed to middleware, so enhanceMiddleware cannot handle it as asset.
    enhanceMiddleware is unnecessary with that plugin.

  10. tido64 commented on Jan 6, 2026

    @tido64
    Member

    Thanks! I've been digging into why .. was being resolved only for Android but you were faster in finding a solution. I'll do some testing on my side and update the PR.

  11. tido64 commented on Jan 7, 2026

    @tido64
    Member

    Can you check what happens when you bundle? What does the folder structure look like? It doesn't look like Android handles this correct and saves assets under dist/assets?unstable_path=/*.

    Update: See react/react-native#55072

  12. Aure77 commented on Jan 8, 2026

    @Aure77
    Author

    Yes I got asset path like this:
    assetsunstable_path__node_modules_reactnavigation_elements_src_assets_backicon.png
    in Android generated assets res (android/app/build/generated/res/createBundleReleaseJsAndAssets).
    There is also string match of /assets?unstable_path=../../ inside android/app/build/generated/assets/createBundleReleaseJsAndAssets/index.android.bundle (Hermes bytecode is hard to analyze).

    Assets doesn't works in Release mode with this metro config...

  13. tido64 commented on Jan 8, 2026

    @tido64
    Member

    I can't figure out how to fix release mode so I've put the workaround behind an option for now. You can enable it like this:

    diff --git a/metro.config.js b/metro.config.js
    index 499dfd3db..732d2143e 100644
    --- a/metro.config.js
    +++ b/metro.config.js
    @@ -34,4 +34,5 @@ module.exports = makeMetroConfig({
         blacklistRE: blockList,
         blockList,
       },
    +  unstable_allowAssetsOutsideProjectRoot: true,
     });

    You can use an env var to enable it in your setup and hide it behind a start command.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions