Write once, run anywhere with Create React (Native) App and react-native-web

Wow! So engineering! Much platform! Amaze

Phase 1 : The dirty work

create-react-app my-hybrid-app && cd my-hybrid-app
npm install --save-dev babel-plugin-transform-object-rest-spread babel-plugin-transform-react-jsx-source babel-preset-expo jest-expo flow-bin react-native-scripts react-test-renderer@16.2.0oryarn add --dev babel-plugin-transform-object-rest-spread babel-plugin-transform-react-jsx-source babel-preset-expo jest-expo flow-bin react-native-scripts react-test-renderer@16.2.0
npm install --save expo@^25.0.0 react-native@0.52.0 react-native-weboryarn add expo@^25.0.0 react-native@0.52.0 react-native-web
{
"presets": ["babel-preset-expo"],
"env": {
"development": {
"plugins": ["transform-object-rest-spread", "transform-react-jsx-source"]
}
}
}
{}
{
"expo": {
"sdkVersion": "25.0.0"
}
}
import React from 'react';
import App from './App';
import renderer from 'react-test-renderer';it('renders without crashing', () => {
const rendered = renderer.create(<App />).toJSON();
expect(rendered).toBeTruthy();
});
import React from 'react'
import HybridApp from './src/App'
export default class NativeApp extends React.Component {
render() {
return <HybridApp />
}
}
my-hybrid-project
|
├── README.md
├── .babelrc
├── .flowconfig
├── .gitignore
├── .watchmanconfig
├── package.json
├── app.json
├── App.test.js
├── App.js <-- entry point for CRNA (don't move/rename it!)

└── src
├── index.js <- entry point for CRA (don't move/rename it)
└── ... more source files

└── public
├── index.html
├── favicon.icon
└── manifest.json
// These scripts are merely copied from the create-react-native-app package.json file
...
"main": "./node_modules/react-native-scripts/build/bin/crna-entry.js",
"scripts": {
"start-web": "react-scripts start",
"build-web": "react-scripts build",
"test-web": "react-scripts test --env=jsdom",
"eject-web": "react-scripts eject",
"start-native": "react-native-scripts start",
"eject-native": "react-native-scripts eject",
"android": "react-native-scripts android",
"ios": "react-native-scripts ios",
"test-native": "node node_modules/jest/bin/jest.js --watch",
"test": "npm run test-web && npm run test-native"
},
"jest": {
"preset": "jest-expo"
}
...

Phase 2 : Profit!

npm run start-weboryarn start-web
npm run iosor yarn ios
import React, { Component } from 'react'
import { View, Text, StyleSheet } from 'react-native'
export default class App extends Component {
render() {
return (
<View style={styles.app}>
<View style={styles.appHeader}>
<Text style={styles.appTitle}>Welcome to React ⚛️</Text>
</View>
<Text style={styles.appIntro}>
To get started, edit src/App.js and save to reload.
</Text>
</View>
)
}
}
const styles = StyleSheet.create({
app: {
flex: 1
},
appHeader: {
flex: 1,
backgroundColor: '#222',
padding: 20,
justifyContent: 'center',
alignItems: 'center'
},
appTitle: {
fontSize: 16,
color: 'white'
},
appIntro: {
flex: 2,
fontSize: 30,
textAlign: 'center'
}
})
/* ... */#root {
display: flex;
flex-direction: column;
min-height: 100vh;
max-height: 100vh;
}

--

--

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store