So React Native's packager actually has a really cool piece of functionality that conditionally loads modules with platform specific extensions where present:
https://facebook.github.io/react-native/docs/platform-specif...Currently I use this functionality exclusively for platform specific branching, without any platform-specific conditional logic in my codebase whatsoever. So to give a really trivial example, you can have something like this:
In App.js (shared as-is across platforms):
import * as element from './element'
export default () => (
<element.View>
<element.Text>
Hello world!
</element.Text>
</element.View>
)
In element.js (for web components):
export const View = 'div'
export const Text = 'span'
In element.android.js (for android specific components):
import ReactNative from 'react-native'
export const View = ReactNative.View
export const Text = ReactNative.Text
This way once you build up a set of reusable lower-level cross platform components that share the same APIs, you can compose these components together without any platform specific branching, since the differences between platforms will be abstracted away by these lower level components, which will automatically get loaded by the React Native packager for the appropriate platform.
In practice, a lot of elements don't map one-to-one between web and React Native, and will require a lot more shimming to get at a reasonably close API (forms, inputs, and lists have been especially painful so far), but React's component model gives you all the tools you need to handle and smooth over those mismatches. This approach works great for utility functions that need to provide platform dependent functionality as well, so it's not just for elements.
Disclaimer: My project is still in a super early stage right now, so I'm not sure how well this approach can scale, but so far I'm enjoying the experience a lot more than I thought I would thanks to this approach, due to the lack of need for platform-specific branching logic in my code.