This article assumes you already have some experience with
react-native, you know how to build mobile builds of a RN codebase, and have them run in a simulator.
I am mostly experienced in
Ruby on Rails full stack development with mostly focus on backend and DevOps, i have worked on and off on different JS frameworks, but I am not an expert(yet) in
webpack configurations etc. So proceed with caution, and point out any improvements in the process in comments.
I am not yet sure how many parts this series will take. Also my work of conversion of this app is still WIP, so who knows what and where that work ends up. But i will share my journey anyway.
Also note, we did not had any active android builds, so this series won’t be explaining steps involved with building android successfully. But you should be able to have them run in the similar way as i will explain for ios below.
You can skip Credits and background section if you like and can jump to “_Let’s Just begin the actual stuff!” section._
Before we begin our journey, i wanted to give credits to a couple of people who helped a lot on this journey, first one is the Bruno Lemos, i got the
react-native monorepo setup with initial
react-native-web setup from his excellent blog post. And other one is Thomas Gladdines, he was so kind to help me through email with all queries i had in the process.
On a product i am working on, they have a fully functional react-native app released in iOS app store (It did not had android builds and releases, we are planning on that as well but thats not the topic here). We had its RN version recently upgraded from
56.x to 59.9 , the app IS on app store, but with not a lot of users just yet, as it is a startup still trying to get kick-started with their initial contracts and everything. Anyway.
Suddenly one morning, we have an emergency meeting, and the project manager tells us that we have these X and Y clients whom we are going to partner with, and these are business critical deals. And the issue is, they both need a web app instead of the mobile app, that too ASAP. And we need to port our react-native app to web version within a week or so, knowing we are team of only two devs. Yup! Just like that.
And incidentally, both of us had not any experience of converting apps to web. So I was tasked to do some R&D and provide a feasible plan for this conversion with minimum friction and time requirements. So as per pressure from the business side, we had to choose something where we have minimum to none learning curve.
In a perfect world , we will just be putting our code through some code convertor which coverts our
react-native app to
reactjs web app. But we don’t live in a perfect world, do we? Turns out, react community is making great progress towards hybrid apps and PWAs but it is not yet quiet there, ionic has its react beta out, there is a project called ReactXP from microsoft and expo is also working on web compatibility of its apps. It almost felt like we needed this conversion a couple of years too early.
So keeping in mind our short time notice and business criticality, we just decided to keep our learning curve low and not worrying too much about future and to use react-native-web, which currently officially supports Rn 0.55, and we are on 0.59, as we had saw some people mention they are successfully running RN-web on RN 0.59, some mentioned some hacks like making
react-native-web think its RN 0.55 when compiling for web. So we just decided to go with RN-web to get something running on web ASAP.
I had to do a few re-dos to make it all work, thats why i thought it worths to write about it in detail, so people are not stuck where i did stuck and had to re-setup everything in a hope it will work, kind of restart
This is a tough question, there is active progress on RN web, although it doesn’t look very active as far as commits go, and its having hard time keeping up with latest RN versions, but as more and more people start using this, more and more people will collaborate and participate in its development, and we should see better future.
The monorepo architecture itself is quiet amazing and works flawlessly with
RN(except the issues of linking), but only issue we can foresee with
react-native-web is its active development. Its great to try out and get something running. But if you face too many issues and/or missing features you can’t live without, and you also happen to have lot of time to transition to
ReactXP or anything like that, you can try those out. But as I mentioned, those are also not yet that mature, so good luck with that as well.
Before starting, i should stress on importance of version control, just keep committing every small step which worked with proper commit messages, and you will save a lot of time.
Ok, here we go.
As i mentioned above, i am using the monorepo architecture to share code between mobile and web, and i followed an excellent blog post on the matter. There is a boilerplate repo also linked in the article, which you can use. But i preferred to setup this whole thing from scratch as described in the post i linked, so have a better sense of understanding of whats actually going on. Turns out, there is a lot going on.
BTW, there is an interesting debate of
monorepo vs multi-repo architecture, we are not going that route. But it worths mentioning, when we are using something like RN-web, it makes most sense to have this architecture. Maybe not that much if we were just sharing the services and reducers etc among web and native react.
Ok, now we have our monorepo setup, with basic boilerplate running on both mobile and web. Let’s start importing our existing mobile app to this architecture. Before we do that, i must mention that you can try converting the existing app to monorepo setup in place, but that didn’t work for us, was taking too much time that we decided to just port it over.
During the setup from above guide(which you have to do from there, i will not be doing that all again over here), you would have noticed we are using
yarn, and its
workspaces feature. We have a folder called
packages on the root, it contains currently 3 sub-folder, each with their own
package.json file. but important part is, all the packages will be installed in the
node_modules folder at the root. Not in sub
Lets go through these packages/folders one by one:
Components: This folder will hold generic components or the shared code so to speak, in this component we will have everything that we want to share. You can get creative with the naming if you like.
Mobile: As name suggests, this folder will hold our mobile specific code. And when working/building mobile we will be staying in this folder. This folder further has the usual folders we see in any react-native app, the
src folders etc. You should already know what are those and how we use them.
Web: This is where actual web magic happens. This is supposed to be focus of this article.
Just like there is no free lunch, there are a few issues I have came across. I am pasting first 3 straight from Bruno’s original article.
react-native-websupports most of the
react-nativeAPI, but a few pieces are missing like
- If you come across a dependency that doesn’t work well with the monorepo structure, you can add it to the nohoist list;
react-native linkmight not work well with monorepo projects without
nohoist; to workaround this, use
**/react-nativeOR instead of installing the dependencies only using
yarn workspace mobile add xxx, install them in the root directory as well:
yarn add xxx -W. Now you can link it and then later remove it from the root
package.json. (So far what I have been doing is the second option from this. Which is to copy dependencies to root
packakeg.jsonand later remove them from there.)
- Lookout for library versions when you copy the dependencies from existing app, they WILL change and may jump to latest versions without you noticing if you don’t properly lock them in
package.json, so i recommend you spend some time locking them properly and than making sure everything works in old app, before starting to port it.
- Be prepared to have some details of the functionality of your mobile app to be compromised, at-least for short term. As odd as it sounds, its a reality I faced, some RN components are not even supported in RN-web, one of them is
Alert, which we happen to use a lot, so we will need some patch(if we can find any) to make it work or we will have to use something else to achieve the same functionality.
- Unless you decide to limit the width of your web app,
you will probably have to fix a lot of responsiveness issues. And for some components/screens of your app. It may even feel like re-writing the view layer. As you can imagine the drastic changes for responsiveness.
- I have not yet setup some proper
versioning scriptlike the one we were using in the old setup, but that might be a bit of problem for us. And we may need something custom for that, unless we can live without this and waste time every-time we need to release a new version.
- Lastly, as of point 4, 5 and 6, you can already guess, this RN and RN-web combo will work best for new react apps aiming for PWA or hybrid results, so to speak. This doesn’t mean it doesn’t worth a shot when you are short on time and want something out on web from existing RN codebase.
I decided to first have dependencies moved, installed, and
yarn.lock file properly updated and than copy actual code. So i did that. I copied everything under
devDependencies sections in old
package.json(except react and react-native, any any other duplicates), and moved it over to
packages/components/package.json sections respectively. (Note that I was not careful about the gotcha number 4 i mentioned above, that caused number of issues for us later on, so lookout for that). After i got everything installed and mobile app was still running fine(of-course, because we haven’t imported anything from these new dependencies), i made a new commit.
This particular step may sound like an easy thing, but it’s actually not. Believe me, I spent at-least one and a half of day to make it build for mobile in this new architecture! Yup…
To import old code, i will explain what strategy i took, and you can decide what works best for you. I decided to copy whole existing code to
components package of the app, which is for the shared code. Idea was to first build the mobile successfully in this architecture, then try on web, and move what needs to be moved to mobile or web specific sub-packages.
So i just copied everything from my old src folder to
packages/components/src and in our old setup, we had
App.js outside of the
src, i moved that inside of the
src as well, and had to update some import paths in
App.js but that’s fine. Now app should work? No, don’t forget linking and other
xcode specific settings your dependencies may require.
There might be a better way to do this than what i am going to describe by my experience.
As mentioned in point number 3 of the gotchas section, linking is a bit tricky. Before linking though, if you use
cocoa pods, please setup those. Even if you don’t, this might be a good time to use them. Install cocoa pods. When it looks good, choose how would you like to link the libraries, as described in above mentioned section, for now, what i am doing is copying over all dependencies from
packages/components/package.json to main
package.json, yarn install and run
react-native link from main directory. Then remove these dependencies from main package.json and yarn install. Last step, obviously, inside
ios directory run
I had some complications , which turned out to be caused by something else, which i will explain in a minute, but on this stage, what i did was to copy over my old
podfile completely. I mean if you have a
podfile, it didn’t hurt to copy old one and just fix the references to the
node_modules folder present in main directory of the repo.
At this step, you can try and run your
.scworkspace file of your project, clean the build have the
metro bundler running in the background (as described in the monorepo setup guide i linked command is something like:
yarn workspace mobile start), and let’s try building the app. If you are lucky enough, your app will run, mine didn’t. It did built successfully, but it failed when loading files from metro bundler.
First i had
bundling failed: Error: Unable to resolve modulepath
... error, i blindly added a package named something path(you can google it up, i don’t remember, maybe it was named just path). Then i started seeing
bundling failed: Error: While trying to resolve modulefs
from file... and
node_modules/fs/package.json was successfully found. However, this package itself specifies a
main module field that could not be resolved
thats when i thought maybe, **it is something wrong with my config** , not my dependencies, because i have all my dev and other dependencies installed same as in previous app. So it is not the dependencies i need but something else. What is turned out to be? _I missed to copy babel presets from oldbabel.config.js` to new one in mobile package. And that solved both of these issues. _
This error was appearing after loading from metro bundler, turns out, it is complaining about missing proper linking. As some libraries don’t just require you to run
react-native link they sometimes involve manual steps as well, like the
react-native-permissions package. So i had two options at this stage:
- Go through all my dependencies readme, and make sure we have everything setup as needed.
- Match the old
.xcworkspacefile libraries and linked section and make sure we have everything matching up.
For the shortage of time, i took route 2, i don’t recommend it, especially if you have android builds active as well. This took me some time to make sure we have everything we need. And after a few rounds of failure with similar errors, i was able to run the app.
NOTE: This is the section i am almost sure about that this would have some better way of doing this than the above mentioned two, definitely better than the approach I took. Comment if you can suggest anything, and i will add it in the list for readers.
There might be some other bits according to your setup, but that was it for me and i had my mobile build running! Finally!
PS: As i mentioned, i had not properly locked my dependencies, i had some issues in the app at this point, but it was working, and our only goal was to have a web version ready ASAP. So we just took note of those issues and moved on.
Well, at-least for part one. We have our mobile app running in this architecture. We can go from here in the next part and actually start porting for web app.
Please share your experiences and anything you want to add/fix in the article.
Till next time, TC.