Building Reusable React Components Across Web and Mobile

TIL sharing components across a React web app and a React Native app only works if you split presentation from platform primitives from day one — retrofitting it later means rewriting most of your UI layer.

2 min read

Sabin Shrestha

Full-Stack Developer — Next.js, React & React Native

The Problem #

Pasal needed the same core UI — inventory lists, appointment cards, status badges — on both the web dashboard and the React Native app. Building each twice meant every product change had to be implemented, tested, and fixed in two places.

Context #

Early on I treated the web and mobile UIs as separate codebases that happened to solve the same problem, because that's how the project started: web first, mobile bolted on later once the business needed a driver/staff app.

What I Tried #

I started extracting "shared" components by copy-pasting the web version into the RN project and swapping div/span for View/Text, assuming the logic could stay identical.

What Went Wrong #

The components weren't actually shareable — they had DOM-specific styling (CSS classes, :hover states, box-shadow) baked into the same file as the actual list-rendering and state logic. Every "shared" component still needed a near-total rewrite per platform, which defeated the point.

The Solution #

Split every component into a platform-agnostic logic layer (data shape, derived state, event handlers) and a platform-specific presentation layer. The logic layer became plain functions/hooks with no JSX at all; each platform got its own thin presentational component that just consumed that hook.

// Shared: platform-agnostic
function useInventoryItem(item) {
  const isLowStock = item.quantity <= item.reorderThreshold;
  return { isLowStock, label: item.quantity <= 0 ? "Out of stock" : `${item.quantity} left` };
}
 
// Platform-specific: web
function InventoryBadge({ item }) {
  const { isLowStock, label } = useInventoryItem(item);
  return <span className={isLowStock ? "text-red-500" : "text-green-500"}>{label}</span>;
}

Why It Works #

Web and React Native share the JavaScript runtime and component model, but not the rendering primitives or styling system. Anything that touches div/View or CSS/StyleSheet is inherently platform-specific; anything that's pure data/logic isn't. Drawing that line explicitly is what makes sharing actually pay off.

Lessons Learned #

"Reusable component" doesn't mean "one file that runs everywhere" in a React + React Native codebase — it means one piece of logic, two thin renderers.

What I Would Do Differently #

I'd design the logic/presentation split from the first component instead of discovering it after several rounds of copy-paste-and-rewrite.

Custom hooks, headless component patterns, platform-specific file extensions (.web.tsx / .native.tsx).