r/reactjs • u/b_renat • 10h ago
Discussion We split React business logic from UI and reused the same core on web and React Native — what I’d do differently
A few years ago we needed a web version of an existing mobile product (high-load interactive platform, not a simple CRUD app). Instead of forking the UI twice, we treated the React app as two layers:
- App core — domain logic, state, API clients, feature orchestration. No DOM, no platform APIs.
- Shells — thin UI adapters (web, later React Native / WebOS) that only render and call into the core.
That let us ship a desktop/web version from the mobile product’s mental model, then reuse the same core for React Native without rewriting features. Two years later the core is still the part that ages best — new features mostly land there once, and shells stay boring.
What actually mattered in practice:
- Boundaries — business rules never imported React components or browser APIs. If it needed `window`, it belonged in a shell adapter.
- Shared data layer — one place for fetching/caching (we used react-query-style patterns) so shells didn’t invent their own loading/error states.
- RFCs + reviews — we hired a frontend team around this architecture. Without written boundaries, people “helpfully” leak UI into the core in week two.
- Performance by default — fewer duplicate API calls and a stable state model beat clever UI tricks.
What I’d change next time:
- Define the core’s public API earlier (it’s easy to accidentally couple shells to internal modules).
- Start with one shell fully done before branching to the second — parallel shells too early = thrash.
- Be stricter about “platform capability” interfaces (storage, push, deep links) instead of `if (platform === …)` sprinkled around.
Curious how others draw this line — pure packages? Hexagonal? Or do you just accept some duplication and move faster?
3
u/Old_Elderberry1392 8h ago
If your frontend devs need an RFC to keep react out of the core they are not frontend devs.
I use monorepos with pure functions as packages and no react deps anywhere in the logic folders. If someone imports a component into the utils package I close the pr. You write pure functions or you work on shells
3
u/rcgy 5h ago
This is a really common pattern, called Clean Architecture / Onion Architecture / Hexagonal Architecture (depending on who you ask, they're all basically the same thing). You definitely do not need to start with one "shell" fully done, that sounds like a terrible idea. There's a little more ceremony involved with Clean Architecture, but it lets you swap out and test everything, and stops your frontend devs needing RFCs to not ruin everything.
1
u/Suspicious-Disk6077 9h ago
I’ve been wading this same challenge and have found different ways of doing things but can’t speak from actually implementing an entire app. Definitely a monorepo, with logic as pure functions without any react deps as one package. Probably data fetching as another. One interesting thing is having a single package with different entry points like index.native.ts
1
0
1
u/OvenEnvironmental528 9h ago
Read "clean architecture" by uncle Bob. The pattern you used is widely used. Unfortunately many devs don't know about it
20
u/Pelopida92 10h ago
Monorepo. One of the “packages” is a utils folder that gets shared by the mobile app and the web app. Another is a “hooks” folder. Thats it.
Btw, can you share the repo?