3 tips for web compatibility with react-native
Part 2 of 2
As you read in my last post, I built a very basic app. Along the way, I learned some tips, which I will explain in this article. I thought it would be very buggy on the web. But, to my surprise, there wasn’t a single obstacle that I wasn’t able to overcome, at least in my simple app.
It’s worth reading the first post, so that you have your project set up right. I will continue to use the same repository example in that article.
Tip # 1 — Use webpack aliases to load different modules for the web
You already have one alias set up: 'react-native$': 'react-native-web'
.
I’ll explain how I added firebase and react-router to my project. There are lots of ways to accomplish this, but I’ll show what worked for me.
Firebase
I wanted my app to run with firebase. There is a firebase SDK specifically for react-native, which I installed. Following the instructions there will enable firebase to work in your native applications, but not for the web. Install it as per instructions for native applications.
Then, here are a few things to make it work for the web:
Use a webpack alias to a local re-export of firebase/app
The react-native firebase package follows the same API as the web SDK. But, importing the right stuff is a little different between the two.
In react-native, you import a sub-package, such as auth
, like so:
import auth from '@react-native-firebase/auth';
In the web SDK, you do it like this:
import { auth } from 'firebase/app';
One is a named export (auth
), the other is a default export.
I made a webpack alias to point to a local file that would export the web SDK stuff as default exports.
// Prerequisite: yarn add @react-native-firebase/app @react-native-firebase/auth @react-native-firebase/firestore firebase// webpack.config.js
const path = require('path')
{
...
resolve: {
alias: {
...
'@react-native-firebase/auth$': path.resolve(__dirname, 'src/web-compat/firebase/auth.js'),
'@react-native-firebase/firestore$': path.resolve(__dirname, 'src/web-compat/firebase/firestore.js'),
}
}
}{
...
resolve: {
alias: {
...
'@react-native-firebase/auth$': path.resolve(__dirname, 'src/web-compat/firebase/auth.js'),
'@react-native-firebase/firestore$': path.resolve(__dirname, 'src/web-compat/firebase/firestore.js'),
}
}
}// src/web-compat/firebase/auth.js
export { auth as default } from 'firebase/app';// src/web-compat/firebase/firestore.js
export { firestore as default } from 'firebase/app';
Now, anywhere in my application, I can import firebase modules in this way:
import auth from '@react-native-firebase/auth';
For native platforms, it will use @react-native-firebase/*
. For the web, it will use the webpack alias to pull in the web SDK version. Pretty nifty, eh?
You will need to finish the installation for the native package by following the steps here.
Initialize a firebase web app
In the index.web.js
for web, you can initialize your firebase app for the web there.
import {initializeApp} from 'firebase/app';
import 'firebase/auth';
import 'firebase/firestore';initializeApp({ /* ...Your config here... */ });
react-router
The same trick can be done for react-router.
// Prerequisite: yarn add react-router-native react-router-dom// webpack.config.js
{
...
resolve: {
alias: {
...
'react-router-native$': path.resolve(__dirname, 'src/web-compat/react-router.js'),
}
}
}// src/web-compat/react-router.js
export * from 'react-router-dom';
export { BrowserRouter as NativeRouter } from 'react-router-dom';
Now I did some cheating here. I have the named export NativeRouter
pointing to the router I would want for the web, which is BrowserRouter
. I did this so that I could implement this easily in src/App.js
:
import { NativeRouter, Link } from 'react-router-native'
...
<NativeRouter>
<Link to="/home">Take me home</Link>
</NativeRouter>
and have the same named component work for both native and web platforms.
Tip # 2 — Use platform specific extensions
This is a cool feature of react-native. Read more about it here.
Let’s try it out in our app.
// src/App.js
import TextArea from './textarea'
...
<TextArea/>// src/textarea.js
import React from 'react';export default function TextArea() {
return (<div><textarea /></div>); // That's right, that's HTML!
}// src/textarea.native.js
import React from 'react';
import {TextareaItem} from '@ant-design/react-native';export default function TextArea() {
return <TextareaItem />;
}
You may not believe that this will work until you try it, but the power here is awesome!
We have 2 files, one for web code, and the other for native mobile code. Webpack will pick import src/textarea.js
, as it normally should. react-native will do us a huge favor, and will import src/textarea.native.js
due to the platform and it’s extension, completely ignoring src/textarea.js
.
You can even put HTML in your web file! This unleashes a full range of possibilities to customize certain components for the web vs native mobile.
Tip # 3. Use Platform.OS as a last, but viable option
Here is an example of something small that I felt like was appropriate in a Platform.OS
conditional statement.
import {Platform} from 'react-native';
import auth from '@react-native-firebase/auth';
...
async function signInGoogle() {
if (Platform.OS === 'web') {
const provider = new auth.GoogleAuthProvider();
return auth().signInWithRedirect(provider);
}
else {
const {GoogleSignin} = await import(
'@react-native-community/google-signin'
);
GoogleSignin.configure({
webClientId: 'YOUR_CLIENT_ID.apps.googleusercontent.com',
});
const {idToken} = await GoogleSignin.signIn();
const googleCredential = auth.GoogleAuthProvider.credential(idToken);
return auth().signInWithCredential(googleCredential);
}
}
Conclusion
See final code example: https://github.com/cgood92/react-native-web-example.
react-native-web is ready for more exploration and usage. I will consider using it on any react-native project in the future, so that I can have a mobile web version of the app I want to create. By using the methods of webpack aliases, platform specific extensions, and Platform features, I believe you can make almost any use case work for both web and mobile applications.