Zalando — Accelerating Mobile App Development at Zalando with Rendering Engine and React Native¶
Summary¶
Zalando is migrating its mobile app (previously two separate
codebases — a native iOS app and a native Android app, 90+
screens total) to React Native, reusing the existing web
Rendering Engine
framework as the application-layer foundation. The post
canonicalises three architectural decisions: (1) React
Native as a package — a brownfield-integration architecture
that packages the RN runtime into an npm Entry Point
consumed by both a standalone Developer App (greenfield
RN environment for web engineers unfamiliar with
Xcode/Android Studio) and a native Framework SDK embedded
in the legacy app; (2) cross-platform UI via
react-strict-dom + StyleX — writing an HTML-subset that
Metro maps to React Native primitives on mobile and to plain
HTML/CSS on web, with escape hatches for platform-specific
implementations through Foo.native.ts file resolution and
react-strict-dom's compat API; (3) Turbo Module +
dependency-injection contract — a three-language API
contract (TypeScript + Swift + Kotlin) that lets the RN layer
and the legacy native app communicate (e.g. wishlist badge
increment) while preserving isolation. Zalando has migrated
several screens including Discovery Feed, the new front
screen featured in Q2 2025 results, proving the architecture
handles media-heavy content at consumer-facing scale
(52M+ customers).
It is the wiki's first canonical instance of brownfield React Native integration into an existing native codebase at consumer-app scale, a contrast-pair to Shopify's five-year RN retrospective (greenfield-friendly, team-by-team adoption) and the Shopify new-architecture migration (sources/2025-09-12-shopify-migrating-to-react-natives-new-architecture, about dual-architecture compat across a mature RN codebase). Zalando's posture is different: a still-native-first app adding RN screen by screen, with the Rendering Engine reused across web and mobile as the shared application-layer framework.
It is also the first wiki source that stretches Rendering Engine across web + mobile (axis 7 extension — previously only web). The same Renderer abstraction (supercharged React components with built-in observability, data-fetching, state, and analytics) now powers Zalando's cross-platform customer-experience unification across the website and the mobile apps, with react-strict-dom + StyleX supplying the UI substrate.
Key takeaways¶
- Three pillars driving the RN bet: build-and-ship faster, progressive adoption, include web. Zalando's RN decision is motivated by three explicit requirements (quoted verbatim from the post):
- "Build & Ship faster" — experiment with new customer experiences for 52M+ fashion, beauty, and lifestyle customers; need features buildable on all platforms with minimal effort.
- "Progressive adoption" — rebuilding the entire app at once is out of scope; migrating 90+ screens at once is not an option; technology choice must be adoptable screen by screen.
- "Including Web" — website and apps had diverged technically; must preserve and build upon web-era capabilities (backend-steered UI, composable components) across platforms.
These are the forcing functions behind the architecture. Each maps to a specific design decision: RN reduces per-platform build cost; React Native as a package enables per-screen adoption; react-strict-dom + Rendering Engine carries web-era investment forward.
- React Native as a package is a brownfield-integration architecture, not a greenfield RN app. Zalando started with a proof-of-concept app built as a standard greenfield RN app (react-navigation, react-native-reanimated, react-native-video, custom turbo modules). Proving technical viability wasn't enough; to ship, they had to embed RN into the existing large native codebase. Three problems surfaced:
- Native-dependency conflicts — RN or community packages pulling native packages in different versions than the main app used.
- No clear separation — git submodules and similar approaches don't enforce strict separation between the RN code and the legacy app code.
- Bad developer experience — building the full native app is slow despite caches; requiring every RN contributor to build the full iOS/Android app (including setting up Xcode / Android Studio) is a friction barrier for web engineers who are expected to contribute to RN.
The solution — React Native as a package — packages
the React Root Component + initialisation logic into an
npm package called the "Entry Point". Two consumers:
- The standalone Developer App — consumes the Entry
Point with a standard RN environment. Web engineers
get "all the benefits of React Native, as in any
greenfield app, with full isolation from the legacy
architecture." Custom dev menu on top of RN's default
(accessed via shake-device gesture) lets developers
switch between JS bundles (released versions, PR builds,
local builds) and other dev-experience utilities.
- The native Framework SDK — a native library (iOS
+ Android) containing the full RN stack behind a
simple-to-use interface: ReactNativeViewFactory with
initialize() and loadView(deepLinkProps, launchOptions)
on iOS; ReactNativeViewFactory with initialize(),
createViewHostedInActivity(activity, screenParameters),
and createViewHostedInFragment(fragment, screenParameters)
on Android. The legacy app consumes the SDK the same
way it consumes any other native framework.
The post explicitly credits callstack/react-native-brownfield as the open-source package that generalises this work.
- Turbo Module + dependency-injection contract is the legacy-app interop mechanism. Some features span the RN-native boundary — the post's example: adding a product to the wishlist from RN must increment the badge displayed by the legacy native app. The contract flow:
- Define a turbo module with TypeScript interface —
e.g.
Spec extends TurboModuledeclaringaddProduct(sku, shouldShowNotification?): Promise<void>andonProductChange: EventEmitter<ProductChangeEvent>. - Define a compatible native protocol (Swift + Kotlin)
— e.g.
WishlistProtocolwith matching methods + callbacks — plus an injection point (WishlistConfig.delegate: TurboWishlistProtocol?) where the legacy app registers its implementation. - Legacy app implements the protocol and injects itself into the Framework SDK at startup.
- Standalone Developer App implements the same protocol with mocked versions, so wishlist-dependent features can be tested without the full native app.
The key property: "clear boundaries and contracts". This is a three-language API-first workflow — the TypeScript type, Swift protocol, and Kotlin interface must all align before implementation begins. Zalando explicitly calls out the consequence: "when combining three environments into one (TypeScript, Swift and Kotlin) it's crucial to first properly define these API contracts and ensure that all involved environments are compatible with this contract as early as possible. Otherwise, you run into challenges where the API design might not be feasible on all platforms, requiring you to undo work that has already been done."
- Cross-platform UI: react-strict-dom + StyleX, chosen over react-native-web. Two candidate approaches for cross-platform UI on top of RN:
- react-native-web — write normal RN components
(
<View />,<ScrollView />,<Text />, etc.) and let the library translate them to HTML for the web. - react-strict-dom — write an HTML subset (
html.div,html.button, etc.) and let the library map HTML to RN primitives on mobile, to plain HTML on web.
Zalando chose react-strict-dom for two reasons: - Future-proof language choice. HTML and CSS have evolved over decades and are unlikely to be displaced. RN-specific abstractions "could potentially become obsolete at any point." - Zero runtime cost on web. react-strict-dom's build step removes the abstraction layers on web; the output is plain HTML + CSS with no adapter overhead.
Zalando pairs react-strict-dom with StyleX (Meta's CSS library) for styling. StyleX works hand-in-hand with react-strict-dom to support theming via tokens (font sizes, colours, borders) that behave like CSS variables across all platforms and are transformed to regular CSS on web.
- Metro's platform-specific imports are the per-platform escape hatch. When a component's behaviour must diverge between platforms, Zalando uses Metro's file-extension-based resolution:
Foo.ts— default (for web, when no platform-specific variant exists).Foo.native.ts— both iOS and Android RN platforms.Foo.ios.ts/Foo.android.ts— specific platform if iOS and Android need to diverge further.- A separate types file — so multiple implementations can have safe type-checking against a single contract.
When a consumer writes import "./Foo", Metro picks the
correct file based on target. The consumer doesn't know
or care which implementation it got.
- react-strict-dom's compat API is the
extend-or-override escape hatch. Even with HTML
mapping, sometimes the underlying native component needs
extra props. react-strict-dom provides a
compat.nativeAPI that lets the developer inject specific props into the mapped native component:
<compat.native
{...props}
aria-label="label"
as="span"
>
{(nativeProps) => <Text {...nativeProps} />}
</compat.native>
This is the ejection point when the HTML subset doesn't cover a platform-specific requirement — the developer hand-codes the native component and passes props through.
-
The cross-platform code share is a balancing act, not a maximum. Zalando explicitly rejects the "maximise code share" framing: "Writing cross-platform code is a balancing act between saving development time and limiting yourself to cross-platform constraints. It's important to accept that having 100% code shared between all platforms or even between iOS and Android, is not the goal, just like writing everything in JavaScript and avoiding native code is not the goal, and that's totally fine." This is the same architectural posture Shopify canonicalised in sources/2025-01-13-shopify-five-years-of-react-native-at-shopify (the "think native and React Native, not native or React Native" rule). Zalando arrives at it independently for the cross-platform UI axis (HTML vs native) on top of the mobile-vs-web share axis — see patterns/mixed-native-plus-cross-platform-mobile-stack.
-
Launch early on a simple, low-traffic screen. Zalando's first migrated screen was "a low-traffic and very simple screen". Quoted rationale: "even in this simplest scenario, we learned a lot. It provided opportunities not just to test the technology early without breaking a major feature, but also to build proper observability based on real customer experience." This is a de-risking pattern — the purpose of the first RN screen is not to deliver business value but to exercise the full pipeline (dev-app loop, deploy, crash reporting, analytics, performance observation) against real production traffic at low blast radius. The follow-up evidence: Zalando has since migrated Discovery Feed, the new front screen featured in Q2 2025 results — a media-heavy, high-traffic surface that validates the architecture at load and scope.
Systems mentioned¶
- React Native — chosen cross-platform framework.
- Rendering Engine — Zalando's in-house React application framework; the "supercharged React components with observability, metrics, traces, data fetching, caching, state management, and analytics built in" primitive, now reused on mobile. Stated as having allowed a production-ready RN prototype in "just a few weeks" by integrating the web-era framework into RN.
- Interface Framework (IF) — the web-era framework Rendering Engine is part of.
- react-strict-dom — Facebook's library that maps HTML-subset components to React Native primitives on mobile and plain HTML on web.
- StyleX — Meta's CSS library for cross-platform theming.
- Metro — RN's JS bundler;
supplies platform-specific file resolution
(
.native.ts,.ios.ts,.android.ts). - react-navigation — used in the proof-of-concept.
- react-native-reanimated — used for animation in the proof-of-concept.
- react-native-video — used for video playback in the proof-of-concept.
- callstack/react-native-brownfield — open-source package that generalises the "RN as a package" pattern, produced by Zalando's partners at Callstack.
- Zalando Mobile Framework SDK — native library wrapping the RN stack behind a simple interface, consumed by the legacy native app.
- Zalando Mobile Developer App — standalone greenfield RN app, used for RN development without the legacy-app build cost.
- Discovery Feed — the media-heavy front screen migrated to RN; featured in Q2 2025 results.
- Zalando Design System tokens — cross-platform styling variables (colours, font sizes, borders) consumed via StyleX.
Concepts surfaced (new to wiki)¶
- concepts/react-native-as-a-package — the architectural abstraction: RN root component + init logic packaged into an npm Entry Point, consumed both by a greenfield dev app and a native framework SDK.
- concepts/brownfield-rn-integration — the problem space: integrating RN into an existing large native codebase (dependency conflicts, isolation, dev-experience friction).
- concepts/turbo-module-di-contract — the three-language contract (TypeScript + Swift + Kotlin) that defines the RN ↔ native-app communication boundary with DI-based legacy-app implementation injection.
- concepts/platform-specific-import-resolution — Metro's
.native.ts/.ios.ts/.android.tsfile-based resolution that picks the right implementation per platform. - concepts/css-subset-cross-platform-ui — writing an HTML
- CSS subset that maps to RN primitives on mobile and plain HTML on web (via react-strict-dom + StyleX).
- concepts/cross-platform-ui-subset-tradeoff — the rule that any cross-platform UI layer constrains expressiveness to the intersection of what all target platforms support; and that escape hatches must exist for the non-intersection.
- concepts/progressive-screen-level-rn-migration — the adoption mode for a brownfield RN rollout: migrate screen by screen, each on its own risk budget, while the legacy app continues to ship.
- concepts/design-token-cross-platform-theming — using StyleX tokens (font sizes, colours, borders) as the platform-agnostic styling variable, with the build step producing CSS variables on web and platform-native values on mobile.
- concepts/greenfield-developer-app-for-hybrid-engineers — the standalone RN app that gives web engineers a greenfield RN experience without requiring mastery of the full native app build / tooling.
Concepts surfaced (existing, reused)¶
- concepts/shared-mobile-foundations — Zalando's shared observability / metrics / tracking / state management / design system layer, now reused across web + iOS + Android (via Rendering Engine's Renderer abstraction).
- concepts/cross-platform-client-library — the whole-app-framework altitude canonicalised by Shopify is here restated as Zalando's RN-with-Rendering-Engine bet.
- concepts/micro-frontends — the architectural ancestor in Zalando's frontend lineage; Rendering Engine (and its Renderers) is the successor framework to Project Mosaic.
Patterns surfaced¶
New:
- patterns/rn-as-consumable-npm-entry-point — package the RN root + init logic as an npm package consumed by (a) a greenfield dev app and (b) a native framework SDK.
- patterns/standalone-developer-app-for-rn — ship a parallel greenfield RN app so contributors (especially web engineers) can iterate without building the full legacy-native app.
- patterns/turbo-module-plus-di-contract-for-native-interop — turbo module + Swift/Kotlin protocol + DI injection point as the three-language contract for RN↔native-app calls.
- patterns/platform-specific-ts-file-resolution — use
bundler-level file resolution (
.native.ts,.ios.ts,.android.ts) to ship different implementations behind a single import path. - patterns/html-subset-to-native-ui-mapping — write HTML
- CSS subset, let the library map to RN on mobile and to plain HTML on web (react-strict-dom + StyleX).
- patterns/compat-native-escape-hatch — for the subset
gap: use react-strict-dom's
compat.nativeto pass per-platform props through to the mapped native component. - patterns/screen-by-screen-rn-migration — adopt RN one screen at a time, each on its own risk budget, with the legacy app continuing to ship.
- patterns/early-launch-on-low-traffic-screen — first RN screen exists to exercise the pipeline (observability, deploy, crash reporting, analytics) at low blast radius, not to deliver business value.
- patterns/api-contract-first-across-three-languages — TypeScript, Swift, and Kotlin interfaces defined concurrently before implementation starts, to avoid redo when one platform can't honour the contract.
- patterns/design-system-component-library-cross-platform — one design-system component library with typed props, different implementations per platform selected by Metro file resolution, backed by StyleX tokens.
Existing, reused:
- patterns/mixed-native-plus-cross-platform-mobile-stack — "100% RN" is not the goal; "100% cross-platform code share" is not the goal. Both Zalando and Shopify canonicalise the balance-point posture.
Numbers / scale¶
- 52M+ fashion, beauty, and lifestyle customers — stated as the scale the RN bet must serve.
- 90+ screens — size of the existing native app surface that cannot be rebuilt in one pass.
- "Just a few weeks" — time from integrating the web-era Rendering Engine into the RN proof-of-concept to a production-ready setup with live data access.
- Two (iOS + Android) — number of separate codebases
- architectures the migration replaces.
- Three (TypeScript + Swift + Kotlin) — language axes the API contract must satisfy.
- Discovery Feed — named load-bearing migrated screen, featured in Zalando's Q2 2025 financial results.
Caveats / gaps¶
- No P50/P75 render-time or crash-free-session numbers. Unlike Shopify's <500ms P75 / >99.9% crash-free disclosure, Zalando does not publish screen-load or stability metrics for the migrated screens.
- No old-vs-new-architecture posture stated. Post does not clarify whether the Framework SDK uses RN's New Architecture (Fabric + TurboModules) exclusively or straddles both. Given the explicit use of "turbo module" terminology, New Architecture is implied but not confirmed.
- Dev-experience timing numbers absent. No disclosure of how long a Developer App cold build takes vs the full legacy-app build, even though developer-experience was one of the three forcing functions.
- Migration progress not quantified. "A few screens, ranging from major to minor" is the only disclosure of how far the migration has progressed against 90+ screens.
- Team-structure implications not discussed. The post doesn't describe how Zalando is staffing the RN effort, whether there's a dedicated platform team building the Framework SDK / Developer App separate from feature teams consuming it. Compare to Shopify's explicit rotating framework-upgrade team in sources/2025-09-12-shopify-migrating-to-react-natives-new-architecture.
- No interop call-volume or latency numbers. Wishlist- example interop flow is described architecturally; the frequency of RN↔native calls and their latency overhead are not disclosed.
- No discussion of code-push / OTA updates. The Developer App is shown switching between JS bundles (released versions, PR builds, local builds), but the post does not describe whether production end-users receive RN JS-bundle updates out-of-band from native app releases, or whether each RN update ships as a native binary update.
- No crash-handling-across-RN-native-boundary discussion. If a JS crash occurs in RN, does the legacy app continue, crash, or show a fallback? Not stated.
- Rendering Engine's mobile adaptation unspecified. The
post says Rendering Engine integrated into RN "in just a
few weeks" but does not walk through which RE concepts
(
tile,.withQueries,.withProcessDependencies,.withRender) map unchanged vs which were adapted to the mobile runtime.
Contrast pairs on the wiki¶
- vs sources/2025-01-13-shopify-five-years-of-react-native-at-shopify (Shopify, Jan 2025) — Shopify migrated team-by-team over 5 years in a greenfield-friendly posture, with the RN framework as the default and native as the escape hatch. Zalando's posture is inverted: native is the substrate, RN is added into it screen-by-screen. Shopify's disclosure focuses on P75 load + crash-free metrics; Zalando's focuses on the integration architecture (RN-as-a-package) that makes screen-by-screen adoption tractable in a large legacy codebase. Both land on the same mixed-stack rule: 100% code-share is not the goal.
- vs sources/2025-09-12-shopify-migrating-to-react-natives-new-architecture (Shopify, Sep 2025) — Shopify's 2025-09 post is about moving an already-RN codebase to RN's New Architecture (Fabric/TurboModules); it canonicalises concepts/dual-architecture-compatibility as the gated-PR discipline. Zalando's 2025-10 post is about moving a not-yet-RN codebase to RN at all. Both describe brownfield migrations but at orthogonal altitudes of "brownfield."
- vs sources/2021-03-10-zalando-micro-frontends-from-fragments-to-renderers-part-1 + sources/2021-09-08-zalando-micro-frontends-from-fragments-to-renderers-part-2 — the Rendering Engine origin story. The 2025-10 post is the mobile extension of that same Rendering Engine framework, with react-strict-dom + StyleX added as the cross-platform-UI substrate. Axis 7 of the Zalando canon (Rendering Engine / Renderers) previously anchored web-only; this post extends it to mobile.
- vs sources/2023-07-10-zalando-rendering-engine-tales-road-to-concurrent-react — the web-side Rendering Engine post about React 18 concurrent rendering. The 2025-10 post is that framework's mobile port, reusing its Renderer abstraction on RN.
Source¶
- Original: https://engineering.zalando.com/posts/2025/10/accelerating-mobile-app-development-at-zalando-with-rendering-engine-and-react-native.html
- Raw markdown:
raw/zalando/2025-10-02-accelerating-mobile-app-development-at-zalando-with-renderin-5c0888af.md - React Universe Conf talk by the same author: YouTube
Related¶
- systems/react-native — chosen framework.
- systems/zalando-rendering-engine — reused web framework, now cross-platform.
- systems/react-strict-dom — HTML-subset → RN mapping.
- systems/callstack-react-native-brownfield — open-source generalisation.
- concepts/react-native-as-a-package — the architectural abstraction.
- concepts/brownfield-rn-integration — the problem space.
- patterns/rn-as-consumable-npm-entry-point — the packaging pattern.
- patterns/turbo-module-plus-di-contract-for-native-interop — the interop pattern.
- companies/zalando — axis 21 anchor.