Summoning builders…Compiling dreams…Polishing pixels…Aligning the stars to your roadmap…Brewing the backlog…Forging gold from ideas…Charting the constellations…Waking the render spirits…

    Insights/Software Engineering/Cross-platform apps with React Native and Expo in 2023

    Cross-platform apps with React Native and Expo in 2023

    Marco Lopes
    October 14, 2023
    4 views
    Software Engineering
    React Native
    Expo
    Mobile Development
    Flutter
    Cross-platform
    Cross-platform apps with React Native and Expo in 2023

    Cross-platform apps with React Native and Expo in 2023

    By Marco Lopes · October 14, 2023 · Topic: Mobile & Flutter

    Building for iOS and Android does not have to mean maintaining two entirely separate products. With React Native and Expo, we can share substantial application logic, move quickly through prototypes, and still reach native platform capabilities when a product needs them.

    At Kordspace, we work as a remote collaborative studio across software, AI, Web3, hardware, and design. That combination makes mobile technology decisions particularly interesting: an app might need a straightforward account flow today, then device connectivity or an AI-powered feature tomorrow.

    Our goal is not to force every requirement into one framework. It is to choose a foundation that supports the product without making everyday development unnecessarily complicated. Here is how we approach React Native and Expo in October 2023.

    Why React Native and Expo belong together

    React Native lets us build mobile interfaces with React and JavaScript or TypeScript while using native platform views. We are not simply wrapping a website in a mobile container. However, sharing a language with the web does not make mobile development identical to browser development.

    Expo adds a framework and tooling around React Native: libraries for common device capabilities, development workflows, configuration, and services for building and distributing applications.

    As of this publication, Expo SDK 49 is the current stable SDK and targets React Native 0.72. We generally start with the versions supported by the chosen Expo SDK rather than independently upgrading core dependencies. Compatibility matters more than collecting the newest package versions.

    Choose the stack around the product

    React Native is a strong candidate when we need iOS and Android applications, expect considerable shared functionality, and have React or TypeScript experience available. It also fits teams that want mobile and web engineers to collaborate on domain logic and API contracts.

    We investigate more carefully when the core experience depends on intensive graphics, unusual background execution, or a specialist native SDK. These requirements do not automatically rule React Native out, but they deserve an early technical spike.

    For us, the most useful prototype proves the hardest integration, not just the welcome screen. If a product depends on Bluetooth communication, we test an actual device connection before polishing navigation.

    Where Flutter fits

    Although this article focuses on React Native, Flutter belongs in the same evaluation. Flutter uses Dart and its own rendering approach, offering a different route to consistent cross-platform interfaces.

    We would consider Flutter when its UI model, available packages, and the team's experience align better with the product. We would lean toward React Native when React expertise, JavaScript ecosystem integration, and collaboration with an existing web team provide meaningful advantages.

    Neither choice eliminates platform-specific work. Accessibility, permissions, notifications, signing, and store submissions still need attention. We recommend comparing both against real product requirements rather than choosing a winner in the abstract.

    Start with a small, maintainable foundation

    We usually begin with TypeScript, consistent formatting, linting, and a repository structure that separates screens, reusable interface elements, data access, and domain logic.

    The initial application should include one complete vertical slice: a screen, an API request, loading and error states, and a basic test. This reveals architectural problems earlier than creating dozens of empty screens.

    We also establish development, staging, and production environments immediately. Each should have deliberate backend endpoints and application identifiers where appropriate. A staging build should be visibly distinguishable from production, particularly when payments, connected hardware, or destructive actions are involved.

    Understand Expo Go, development builds, and native projects

    Expo Go is convenient for learning and early experiments. It includes a predefined set of native capabilities, so it cannot load every native library a project might require.

    For production-oriented development, we often move to a development build: our own application binary with the native dependencies and development tools the project needs. That gives us a more representative environment without abandoning Expo's tooling.

    Expo Prebuild can generate iOS and Android projects from application configuration. Config plugins can apply repeatable native configuration changes. If we edit generated files manually, we must understand whether those changes will survive regeneration.

    The important distinction is that Expo is not an all-or-nothing sandbox. Native customization is possible, but it introduces responsibilities we should plan for explicitly.

    Design shared experiences, not identical screenshots

    A shared codebase should express a coherent product, not erase the differences between iOS and Android.

    We account for safe areas, keyboard behavior, Android back navigation, platform typography, and permission prompts. We test small screens and larger text settings instead of assuming a design works because it looks good on one simulator.

    A lightweight design system helps us share colors, spacing, component states, and interaction patterns. Platform-specific components or files remain useful when behavior genuinely differs.

    Accessibility belongs in those shared components from the beginning. We check labels, touch targets, contrast, focus behavior, and screen-reader output on real devices.

    Keep navigation and state understandable

    React Navigation remains a practical navigation foundation. Expo Router, including the version available with SDK 49, offers file-based routing built on React Navigation and can simplify route organization and deep linking.

    Whichever approach we choose, we define authentication boundaries, nested navigation, and incoming links early. Password-reset links and notification destinations are product behavior, not finishing touches.

    For state, we distinguish local interface state from server data and persistent preferences. We avoid placing everything in one global store. A server-state library can manage caching and retries, while component state often handles temporary interactions perfectly well.

    We also define what happens when cached data becomes stale or a session expires.

    Build the backend boundary carefully

    Google Cloud is our preferred starting point when it suits the project. Cloud Run can host APIs, while Firebase services can support needs such as authentication, data storage, and notifications. The right combination depends on access patterns, operational requirements, and budget.

    We also work with AWS and Azure when an existing environment, procurement requirement, or specific service makes either a better fit.

    Regardless of provider, we enforce authorization on the server. A hidden button is not an access-control mechanism, and a mobile application cannot safely keep privileged credentials secret.

    Environment variables embedded in the app, including Expo's public variables, are configuration, not a secret store. We keep service credentials on the backend and store appropriate device-side tokens using secure platform storage.

    Treat AI, Web3, and hardware as integration work

    For AI features, we generally place provider access behind our backend. That lets us protect credentials, validate requests, control spending, and apply product-specific data-handling rules. We design for slow responses and unavailable services rather than assuming every request succeeds instantly.

    For Web3, wallet connections, transaction approval, and deep-link behavior need testing on both platforms. We make network selection and signing intent clear, and avoid asking users to expose recovery phrases.

    For hardware, native library compatibility is only the first check. We test pairing, disconnects, reconnection, permissions, and operating-system restrictions. Bluetooth functionality commonly requires a development build rather than Expo Go.

    These integrations are where an early proof of concept earns its place.

    Make unreliable networks part of the design

    Mobile apps travel through lifts, underground stations, and weak Wi-Fi. We therefore treat connectivity changes as normal application conditions.

    At minimum, we provide useful loading states, understandable errors, and a safe retry path. Where offline work is essential, we design local persistence and synchronization deliberately rather than adding a generic cache and calling the app offline-ready.

    Queued writes require decisions about duplicate submissions, ordering, and conflicts. A retry should not create a second booking or repeat a payment request. We coordinate client behavior with backend idempotency and clear confirmation states.

    We also distinguish locally saved information from successfully synchronized information in the interface.

    Measure performance before adding complexity

    We test performance in release builds on representative physical devices. Development tooling can change performance characteristics enough to make impressions misleading.

    Common issues include rendering large lists inefficiently, shipping oversized images, triggering unnecessary updates, and doing expensive work on the JavaScript thread. We start with appropriate list components, sensible image sizes, and focused profiling.

    React Native's New Architecture is progressing in 2023, but we do not treat enabling it as an automatic fix. We check library compatibility and Expo support for the particular setup before adopting experimental paths.

    A dependable app with understood bottlenecks is more valuable than an ambitious configuration nobody on the team can confidently debug.

    Test the journey, not just the components

    We combine tests of business logic with component tests and end-to-end coverage for the most important journeys. Signing in, completing the primary task, and recovering from failure deserve priority.

    Our device checks include permission denial, expired sessions, backgrounding, keyboard overlap, and reopening the app through a deep link. Features involving cameras, notifications, or connected hardware require physical-device testing.

    As a remote studio, we make reproduction steps, build identifiers, and short screen recordings part of bug reports. Shared preview builds help designers and engineers evaluate the same implementation instead of debating different local versions.

    We also document architectural decisions so collaboration does not depend on everyone attending the same call.

    Plan releases and updates from the start

    EAS Build can produce application binaries, and EAS Submit can help upload them to the stores. Neither removes the need for developer accounts, signing configuration, store metadata, privacy disclosures, or review preparation.

    EAS Update can deliver compatible JavaScript and asset changes without rebuilding the native binary. It cannot add a missing native module or bypass app-store rules. We define runtime compatibility carefully and keep update channels aligned with our environments.

    For production, we prepare monitoring, ownership, and a recovery plan before publishing. Crash reports and backend logs should be useful without collecting unnecessary personal data.

    We leave room in the schedule for store review and real-device acceptance testing rather than treating release as a final button press.

    Our short pre-build checklist

    Before committing to implementation, we confirm:

    • The hardest native integration works on iOS and Android.
    • The Expo SDK and required libraries are compatible.
    • Development builds and environment boundaries are defined.
    • Navigation, accessibility, and offline expectations are documented.
    • Backend authorization and credential handling are designed.
    • Testing, store submission, monitoring, and update ownership are assigned.

    Build the right app, with room to evolve

    In 2023, React Native and Expo give us a practical foundation for many cross-platform products. The strongest results come from combining shared code with deliberate native testing, clear backend boundaries, and thoughtful design, not from pretending platform differences disappear.

    If you are exploring a mobile product, connecting hardware, or adding AI or Web3 capabilities to an existing experience, we would be glad to shape the technical approach with you. Start a project with Kordspace, or explore our related services pages to see how we can collaborate.

    Conversation (0)

    No comments yet. Start the conversation.

    Join the conversation

    Create a free member account to comment.