- Home
- >
- Mobile apps development
- >
- How To Start Your first hybrid app in 2022
Before we Start React-Native-Web
- Basic understanding of React (understanding React native as well is preferred but not necessary)
- Some understanding of ES6
- Have npm installed on your machine
- yarn install. If you don’t, feel free to substitute any instance of
yarn add ...
withnpm install -S ....
Downloading Expo
Download the Expo XDE through the above link. Expo will save us the headache of running iOS/Android simulators on our machine. With Expo, you can create a React Native app and test it on your own phone. There is an CLI as well that you could use, but we’ll be using the XDE for this project. Once you’ve done that, open it and register an account if you haven’t already.
Expo on your Phone
Through the link, find and download Expo client for the mobile device (iOS version, or Android) on which you’ll be testing. Once it’s downloaded, log in using the same account you’re using with the XDE.
Create your app
Open theExpo XDE and click ‘create new project…’ to create an app. Use the blank template.
Run your app
- Open the expo client on your device
- Your project should appear under ‘Recently in Development’
- Click it and wait while the Javacript bundle is built on your phone
- You should see the following screen on your device
- Change some text in App.js and save it, and your app should automatically reload with the updated text
2) Then Web
Dependencies
Let’s start by getting the necessary packages to run this on the web.
Open your terminal, navigate to the project folder and then run the following command:
yarn add react-scripts react-dom react-native-web react-art react-router-native react-router-dom
Here’s what we’re adding:
- react-scripts: contains the scripts used in create-react-app.
- react-dom: allows react-code to be rendered into an HTML page
- react-native-web: the main source of magic in this app. This library will convert our react-native components into web elements. Thankfully, react-scripts is already configured to use it, so you won’t need to touch any Webpack yourself to get it up and running
- react-art: a peer dependency for react-native-web
- react-router-native: routing library for React Native
- react-router-dom: routing library for React on the web
File Restructure
We need to have two separate entry points which point to the same root application. One App.js
will be used by Expo, and the other src/index.js
will be used by react-scripts to be rendered on the web.
- Create a folder called
src
and copy the existingApp.js
into it to createsrc/App.js
- Refactor your root folder’s
App.js
so that it simply imports and renders thesrc/App.js
that you just created
// /App.js
import React from 'react';
import HybridApp from './src/App';
const App = (props) => {
return (
<HybridApp />
);
}
export default App;
- Create a folder called
public
and createpublic/index.html
- In your
index.html
simply make a html skeleton, with<div id="root"></div>
in the body so that your app has somewhere to render
<!-- public/index.html -->
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>Pokedex</title>
</head>
<body>
<div id="root"></div>
</body>
</html>
- In your
src
folder, createindex.js
and write the following code to have it render your app into the DOM
// src/index.js
import React from 'react';
import ReactDom from 'react-dom';
import App from './App';
ReactDom.render(<App />, document.getElementById("root"));
Scripts
react-scripts is automatically configured to recognize React Native code and translate it using the react-native-web library that we imported. That’s why we can make this work without pulling our hair out over Webpack configurations. All we need to do now is set some scripts in our package.json
so that we can easily run it.
Add the following property to your package.json
// package.json
"scripts": {
"start-web": "react-scripts start",
"build-web": "react-scripts build"
},
Run it on the Web
In your terminal, run yarn start-web
and in moments your web app should appear in the browser. Congratulations, you have created your first hybrid app!
Now let’s make this app do something.
Adding Content and Functionality
Let’s start off simple by adding a list of Pokemon.
To keep things simple, we’ll just store our list of Pokemon insrc/spokemonStore.js
.
// src/pokemonStore.js
export default [
{
number: '1',
name: 'Bulbasaur',
photoUrl: 'https://assets.pokemon.com/assets/cms2/img/pokedex/full/001.png',
type: 'grass'
},
{
number: '4',
name: 'Charmander',
photoUrl: 'https://assets.pokemon.com/assets/cms2/img/pokedex/full/004.png',
type: 'fire'
},
{
number: '7',
name: 'Squirtle',
photoUrl: 'https://assets.pokemon.com/assets/cms2/img/pokedex/full/007.png',
type: 'water'
}
];
Instead of using map, we’ll follow React Native convention and use FlatList, as it will work just fine with react-native-web.
// src/App.js
import React, { Component } from 'react';
import { View, Text, FlatList, StyleSheet } from 'react-native';
import pokemon from './pokemonStore'
class App extends Component {
render() {
return (
<View style={styles.container}>
<FlatList
keyExtractor={pokemon => pokemon.number}
data={pokemon}
renderItem={({ item }) => <Text>{item.name}</Text>}
/>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
alignItems: 'center',
justifyContent: 'center',
marginTop: 50,
padding: 50
},
});
export default App;
Platform-Specific Styles
Note: It won’t be an issue for a barebones project like ours, but in your own projects, you may have to adjust the styles web layout so that it looks similar to your native app. To do so, you can create src/index.css
with your web-specific adjustments and and import it into index.js
.
/* src/index.css */#root {
/* layout adjustments for web app */}
However, that will effect the layout of that app as a whole. Inevitably we need to figure out away to change specific parts of the app based on the platform, and this is where things get interesting.
Platform-Specific Code
We need to add some basic routing so that we can go from viewing a list of Pokemon to the details of a single Pokemon. react-router-dom is great for the web, and another library, react-router-native, is great for React Native. To keep our code clean, we will create a generic Routing.js
file that will abstract that distinction and be available throughout our code.
We will create routing.web.js
with components from one library and routing.native.js
with similarly named equivalents from the other. Import statements to our routing files will omit the file extension, and because of that, the compiler will automatically import whichever one is relevant to the platform for which the app is being compiled and ignore the other.
This will allow us to avoid writing code that checks for the platform and uses the appropriate library every single time we use routing.
First create the web version of your routing file src/routing.web.js
export {
BrowserRouter as Router,
Switch,
Route,
Link
} from 'react-router-dom';
Then, the native version, src/routing.native.js
.
export {
NativeRouter as Router,
Switch,
Route,
Link
} from 'react-router-native';
Now, create two files, Home.js
and Pokemon.js
. These will be the components rendered by your routes.
In Home.js
, simply render a list of Pokemon like you did in App.js
.
// src/Home.js
import React from 'react';
import { View, Text, FlatList } from 'react-native';
import pokemon from './pokemonStore';
const Home = props => {
return (
<View>
<FlatList
keyExtractor={pokemon => pokemon.number}
data={pokemon}
renderItem={({ item }) => <Text>{item.name}</Text>}
/>
</View>
);
};
export default Home;
In Pokemon.js
render information about a single Pokemon. You can hard-code in one now as a placeholder. We’ll refactor and tie everything together after we’re sure that the routing works.
// src/Pokemon.js
import React from 'react';
import { View, Text, Image } from 'react-native';
import pokemon from './pokemonStore';
const Pokemon = props => {
const examplePokemon = pokemon[0];
return (
<View>
<View>
<View>
<Text>{`#${examplePokemon.number}`}</Text>
</View>
<View>
<Text>{`Name: ${examplePokemon.name}`}</Text>
</View>
<View>
<Text>{`Type: ${examplePokemon.type}`}</Text>
</View>
<View>
<Image
style={{ width: 50, height: 50 }}
source={{ uri: examplePokemon.photoUrl }}
/>
</View>
</View>
</View>
);
};
export default Pokemon;
Now change your App.js
so that it now simply renders your routes. One for each component. Make sure you pass in all of the route’s props into the component by adding {…props}
. You’ll need that in order to access the ‘history’ prop which can be used to change routes.
// src/App.js
import React, { Component } from 'react';
import { View, StyleSheet } from 'react-native';
import { Router, Switch, Route } from './routing';
import Home from './Home';
import Pokemon from './Pokemon';
class App extends Component {
render() {
return (
<View style={styles.container}>
<Router>
<Switch>
<Route exact path="/" render={props => <Home {...props} />} />
<Route path="/pokemon" render={props => <Pokemon {...props} />} />
</Switch>
</Router>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
alignItems: 'center',
justifyContent: 'center',
marginTop: 50,
padding: 50
}
});
export default App;
Refresh your app and make sure that each route works by changing the address bar manually. Therefore, deliberately adding a ‘render’ prop instead of ‘component’ into each route because we’ll be passing in some props to each component as we render them.
Your root route will look the same as before. Type ‘localhost:3000/pokemon’ in your address bar and this what you should get:
Tying it all Together.
Now it’s time to add some logic to make this app work. We need to do the following:
- In
App.js
, create a function calledselectPokemon
which takes a Pokemon as an argument and sets it to the App state as ‘selectedPokemon’. To prevent errors, set a default state with a ‘selectedPokemon’ set to null - Pass
selectPokemon
intoHome.js
through its props - In Home.js Wrap each Pokemon in the list in a
<TouchableOpacity />
component so that, once clicked, it can: a) CallselectPokemon
and pass in the pokemon as an argument and b) callthis.props.history.push(‘/pokemon’)
in order to switch to the Pokemon route - In
App.js
, pass in a prop calledselectedPokemon
into the <Pokemon /> being rendered in the second route. It’s value should bethis.state.selectedPokemon
, which will be undefined until you select a Pokemon. - In the Pokemon component, remove the reference to the pokemonStore and instead refer to
props.selectedPokemon
. It would be a good idea to also add some default content that is conditionally shown if no Pokemon is selected. - Create View and some Text with the message ‘return to home’. To make it work, wrap it with a
<Link/>
Tag from your routing file and in that Link component, add the property ‘to=”/”’ to have it redirect to your home route.
Here are what your three changed files should now look like:
// src/App.js
import React, { Component } from 'react';
import { View, StyleSheet } from 'react-native';
import { Router, Switch, Route } from './routing';
import Home from './Home';
import Pokemon from './Pokemon';
class App extends Component {
state = {
selectedPokemon: null
};
selectPokemon = selectedPokemon => {
this.setState({
selectedPokemon
});
};
render() {
return (
<View style={styles.container}>
<Router>
<Switch>
<Route
exact
path="/"
render={props => (
<Home {...props} selectPokemon={this.selectPokemon} />
)}
/>
<Route
path="/pokemon"
render={props => (
<Pokemon
{...props}
selectedPokemon={this.state.selectedPokemon}
/>
)}
/>
</Switch>
</Router>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
alignItems: 'center',
justifyContent: 'center',
marginTop: 50,
padding: 50
}
});
export default App;
// src/Home.js
import React from 'react';
import {
View,
Text,
FlatList,
TouchableOpacity
} from 'react-native';
import pokemon from './pokemonStore';
const Home = props => {
const handlePress = pokemon => {
props.selectPokemon(pokemon);
props.history.push('/pokemon');
};
return (
<View>
<FlatList
keyExtractor={pokemon => pokemon.number}
data={pokemon}
renderItem={({ item }) => (
<TouchableOpacity onPress={() => handlePress(item)}>
<Text>{item.name}</Text>
</TouchableOpacity>
)}
/>
</View>
);
};
export default Home;
// src/Pokemon.js
import React from 'react';
import { View, Text, Image } from 'react-native';
import { Link } from './routing';
const Pokemon = props => {
const backButton = (
<View>
<Link to="/">
<Text>Go Back</Text>
</Link>
</View>
);
if (!props.selectedPokemon) {
return (
<View>
{backButton}
<Text>No Pokemon selected</Text>
</View>
);
}
const {
selectedPokemon: { name, number, type, photoUrl }
} = props;
return (
<View>
<View>
{backButton}
<View>
<Text>{`#${number}`}</Text>
</View>
<View>
<Text>{`Name: ${name}`}</Text>
</View>
<View>
<Text>{`Type: ${type}`}</Text>
</View>
<View>
<Image style={{ width: 50, height: 50 }} source={{ uri: photoUrl }} />
</View>
</View>
</View>
);
};
export default Pokemon;
You’re done! and should now be able to load your app, see a list of Pokemon, click on one to see more it’s details, and then press ‘Go back’ to see your list again.
Bonus tip: Platform-Specific code — The ‘Share’ Button
I showed you how to separate code for different platforms by using different files. However, there will likely be times where you have plaftorm-specific logic that can be solved in such a simple way. For example, we’re going to show a ‘share’ button in our Pokemon view, but we only need it on mobile. It would be overkill to make two versions of that entire component just to hide or show one feature. Instead, we’ll simply hide the functionality where it’s not needed. React Native gives us access to a Platform object, which contains a property called ‘OS’ with a value that changes with the operating system of whichever platform your app is running on. We can use that to hide or show our ‘share’ button accordingly.
// Make sure you import { Platform } from 'react-native';
handlePress = () => {
Share.share({
message: 'Check out my favorite Pokemon!',
url: props.selectePokemon.photoUrl
})
};
...
{ Platform.OS !== 'web' &&
<View>
<Button title="Share" onPress={this.handlePress}/>
</View>
}
As our example is relatively simple, but building a universal React app will inevitably get tricky as you add scale and functionality, since many React Native libraries have little to no compatibility with the web, and even those that do will often require some tinkering with Webpack in order to work. And you should also keep in mind that Expo, as much as it significantly simplifies setup, adds some limitation to the libraries that you can work with. If you’d like to know details about compatibility with specific libraries, this list would be a great place to start:
https://native.directory
Source: InApps.net
List of Keywords users find our article on Google:
react router |
flatlist expo |
react router dom |
react-scripts |
react-router-dom |
react scripts |
react native search flatlist |
flatlist search react native |
react native search bar flatlist |
search in flatlist react native |
react native flatlist search |
react router redirect |
react native flatlist |
flatlist react native |
search flatlist react native |
react select |
substitute pokemon |
react-router |
switch react router dom |
react-native-render-html |
pokemon let’s go route 1 |
react dom router |
this props react native |
import react dom |
react route |
universal yarn |
react router 4 |
react router example |
expo status bar height |
npm router |
pokemon all routes |
flatlist |
react router dom npm |
react-router-dom redirect |
react router dom link |
history push in react |
react-router-dom switch |
pokemon hair tie |
route render |
what is react router |
react router-dom |
entire pokedex |
npm install react router dom |
hrm flex |
expo cli version |
react routing |
react native blank screen |
flatlist search |
hybrid app developer jobs |
react native textinput style example |
magic edtech |
react router dom redirect |
router route react |
body press pokemon |
jsexport |
react router history back |
this.props.history |
clickit devops & software development |
that ll for sure grab the audience a yarn that s definitely gonna win |
pokemon route |
add ecommerce to platformos |
react router npm |
npm react-scripts |
react router link refresh page |
flex hrm mobile |
pokemon x routes |
bulbasaur png |
react native touchableopacity |
react native view onpress |
react-router-dom npm |
npm react router |
squirtle png |
responsive flatlist react native |
pokemon go export pokemon list |
charmander png |
expo remove package |
it’s a wrap yarn |
npm react scripts |
react-native-phone-input |
react-native-phone-input npm |
react native router |
budget home store folder |
expo client download |
wawa library |
flatlist react native web |
how much is a basic charmander |
hybrid app |
react json view |
react-native-maps |
expo xde download |
react router-dom npm |
export ‘switch’ (imported as ‘switch’) was not found in ‘react-router-dom’ |
pokemon 001 |
react flatlist example |
flatlist keyextractor |
flex hrm login |
pokemon 004 |
react native code push |
react router link |
history push |
wrap pokemon |
flat list in react native |
react native routing |
route 4 pokemon let’s go |
router react native |
yarn substitute |
native router |
pokemon wrap |
react native image view |
react-json-view |
routing in react native |
touchableopacity |
appstate writing center |
flex hrm |
pokemon index number |
react native text |
routing react native |
text input react native |
upload image react hooks |
expo react native |
importhome |
link react router dom |
npm install expo cli |
react app blank page |
react package.json |
route props |
update react-scripts to 5 |
yarn add dev |
expo start new project |
how to pass props in react |
list of pokemon by number |
router in react native |
difference between react router and react router dom |
pokemon by height |
react json viewer |
react native call keep |
react-roter-dom |
directory build props |
exact react router |
react js router |
react router dom react native |
react router react native |
route 10 pokemon x |
viewstyle |
whatsappweb js |
download expo go |
prop types npm |
add react router |
app waitwhile |
how to install switch in react |
react native route |
react native share image |
react native text link |
react router dom in react js |
route render react |
routing with react native |
history.push is not a function |
install expo cli |
react app showing blank page |
react router history push |
yarn add –save |
pokemon template |
react-router-dom 5 |
router link react |
router react js |
angularjs reload page |
code push react native |
react native list |
react router react |
start new expo project |
react native reload page |
react router default route |
react routerdom |
react-router redirect |
how to add react router dom |
react native image scale |
react route element props |
router example react |
routing in react |
webpack require import |
angularjs reloadonsearch |
pokemon substitute |
react native reload screen |
react router with router |
redirect react router dom |
flex cli |
hair developer substitute |
how to install router dom in react |
reactnative list |
redirect in react |
route component react |
routes in react |
switch in react router |
(possible exports: default) react |
class app extends react.component |
react native placeholder |
react native share link |
react refresh webpack |
react router use history |
react routing without react router |
render react router |
webreact |
image view react native |
react-router exact |
routerswitch.com |
routing for react |
routing with react |
yarn blanks |
create react app page title |
default path react router |
how much does bulbasaur cost |
link to another component react |
react router history |
reactrouter |
router react |
using react router dom |
browserrouter is not working |
document.getelementbyid is not a function react |
magic ed tech |
react location vs react router |
react native navigate to another screen |
react router hooks |
react router in react |
react-router-dom route |
reactjs router redirect |
yarn add peer dependency |
expo router |
react admin custom routes |
react app router |
react as prop |
react router dom switch |
react-router-dom navigate |
skeleton root |
how to add pokemon to pokemon home |
react image from public folder |
react js setstate |
reactdom.render |
routes route react |
check react native cli version |
import redirect react |
import three js in react |
react link router |
react native load more |
react redirect to url |
react router for react native |
set image in react native |
test routes app |
whatsapp key extractor |
check the render method of `link`. |
import react and react dom |
in-app messaging case studies |
pokemon script |
pokemon type icons |
react link component |
react native map example |
react-router vs react-router-dom |
setstate not working |
switch in react js |
android textview style example |
hybridrouter |
react convert html to react component |
react native search list |
react redirect to page |
react router dom route component |
textview placeholder |
use of exact in react router |
react role based components |
hybrid app development |
react native development services |
Let’s create the next big thing together!
Coming together is a beginning. Keeping together is progress. Working together is success.