Software

Cross-Platform Development: Shared Code Strategies

Architecture patterns to maximize code reuse across iOS, Android, and web without sacrificing platform-specific polish.

All articles
SoftwareNexaEx TeamNovember 1, 2025 8 min read
Cross-Platform Development: Shared Code Strategies

Maximizing Code Reuse Across Platforms

The promise of cross-platform development is code reuse. The reality is more nuanced—some code is highly reusable; some must be platform-specific.

The Reuse Pyramid

Tier 1 - Highly Reusable: Business logic, API communication, state management. A JSON decoder works the same on iOS, Android, and web. Put these in shared libraries.

Tier 2 - Partially Reusable: Validation, formatting, calculations. Mostly platform-agnostic; some platform-specific edge cases.

Tier 3 - Platform-Specific: UI, navigation, device integration. Write these per-platform, but use shared interfaces so business logic doesn't know the difference.

Shared Library Architecture

Monorepo approach: Single Git repo, shared libraries in /packages/shared/, platform-specific code in /ios/, /android/, /web/.

Example structure:

packages/
  shared/           # Reusable business logic
    api/            # API client
    models/         # Data models
    state/          # State management
  react-native/     # React Native app
  ios/              # iOS-specific code
  android/          # Android-specific code
  web/              # Web app

Dependency management: Shared packages have zero platform-specific dependencies. Platform code depends on shared code, never vice versa.

API Layer Abstraction

Create platform-agnostic API clients. Shared code calls ApiClient.get("/users/123"), platform code implements HTTP underneath.

interface IApiClient {
  get(url: string, options?: RequestOptions): Promise<Response>
  post(url: string, body: unknown): Promise<Response>
}

Each platform implements this interface. Unit tests run against shared code using a mock client.

State Management Reuse

Redux, Riverpod, or similar stores are platform-agnostic. Define actions, reducers, and selectors in shared code. Platform UI consumes store state.

// Shared reducer
const userReducer = (state, action) => {
  if (action.type === 'SET_USER') return {...state, user: action.payload}
}

// Platform-specific UI
// Android: observes store updates
// iOS: observes store updates
// Web: observes store updates

Model and Type Definitions

Data models should be unified. In TypeScript, define interfaces in shared code. Generate types for other platforms using tools like Swagger Codegen.

// shared/models.ts
interface User {
  id: string
  email: string
  createdAt: Date
}

Serialize consistently (ISO dates, string enums) so deserialization works across platforms.

Testing Strategy

Unit tests: Run against shared code exclusively. 100% of business logic covered.

Integration tests: Test API interaction on one platform; mirror results on others.

Platform tests: UI and navigation tested per-platform. Reuse test scenarios, not test code.

When NOT to Share

Forcing code reuse where it doesn't belong creates fragile architectures. Each platform has conventions:

  • Navigation: React Navigation, Jetpack Compose, SwiftUI. Don't share navigation code.
  • UI: Custom designs per platform. Shared components often look "off."
  • System integration: File access, contacts, notifications. Implement per-platform.

Real-World Example

A fintech app shares authentication, transaction models, and API communication across platforms. Platform teams build platform-optimized UI and navigation using shared business logic. Shared code is 40% of the codebase; 60% is platform-specific. Slower than web-only, faster than three separate codebases.

Frequently asked questions

Should I reuse UI components across platforms?

Usually not. Platforms have different design conventions and user expectations. Reuse business logic and state management; write platform-specific UI.

What's a monorepo and why use one for cross-platform development?

A monorepo is a single Git repository containing multiple packages/apps. It enables easy sharing of code via internal packages and simplifies dependency management.

How do I structure shared code to avoid dependencies on platform-specific libraries?

Define interfaces in shared code. Platform-specific code implements those interfaces. Shared code depends on abstractions, never on platform libraries.

Let's build your next idea

One conversation to scope the work, meet the team, and get a proposal — usually within two business days.