Approximately 2 years back we decided to move from Native Android and iOS applications to something cross-platform. As most, I went looking for a boilerplate, and this story is about why I decided to write my own after all deliberations.
Note: this article is not about why you should / shouldn’t do Native. It is about how I was not disappointed with what I found, but felt that they just left me wanting for more…
To view the repo, click here.
Circa 2015 — working for an e-commerce company, we had separate mobile, desktop, checkout, account and tracking code bases (so that’s 5). And yes, native iOS and Android apps (2 more). Imagine the size of development team you would need to maintain all of that. And the fact that we were a startup. And I haven’t even mentioned the API and DevOps teams.
To deploy a new change / feature, we had to make changes in 3 applications at least (if not account / tracking) and test them out separately. And do this 2–3 times a week as the company was still learning and we had to be agile.
A year into this, talking to my manager, we felt that it was not possible to maintain so many applications at the pace needed for a startup. So we decided to write an in-house application that, to start with, would work as a single code base for our desktop, mobile and checkout versions. And may be if things work out, we also integrate account and tracking (reality check — tracking is the only piece that is not a part of it today).
We started off with an official Aurelia boilerplate. A year later, everything was controlled using an in-house CMS — no html editing, no separate testing for functionalities. The business logic was shared and everything was modularised using web components (polymer). The folder structure is below:
basically, 2 main separations:
shared: All shared stuff moved in here — like business logic, images, even fonts and translations. After all add to cart is same, whether its desktop or mobile (or even apps).
src: All website presentation stuff went here. small for mobile, large is desktop. We later also introduced checkout.
This was pretty scalable. Later, when the company was looking to venture into the FMCG business, it was just about adding a new folder under
src, grocery-small 😌.
Ok, so a year later, it was all done. With a lot of A/B and number comparisons, we were able to convince the analytics guys 😏 and in phases replaced the mobile, desktop and checkout sites with this new solution. We were all very happy and satisfied with it.
Or, were we?
This is when I started looking for a boilerplate to quickly get started with RN (note — I am not a native iOS or Android developer). I also wanted to move to TypeScript.
There were a lot of learnings we had had when writing the single code base for mobile, desktop and checkout versions. A lot of code refactoring that was required at later stages to incorporate multiple versions of sites that could use the same business logic and shared assets. For e.g. the
images folder for mobile and desktop had different resolution images (we solved that with build), similarly for
locales. Also, the
components folder needed some guidelines to be used across desktop and mobile, before developers could write one, like:
- Be compatible with different resolutions (support 320px to 1440px)
- Have no CSS, yes, NO CSS of their own
- Be concerned about only displaying data
- NOT be concerned about fetching data, framework should do that
Most of the boilerplate solutions that I found online were either too simple, just a few
npm commands put together, or were too heavily loaded and included too many external dependencies / libraries. I am not a big fan of solving problems through
npm installs, and highly recommend my team also to write their own, except when it makes no sense to do so.
Also, since I was getting started with React Native, I did not want everything baked in. So, not having much time to experiment (welcome to startup culture), CRNA was the most popular choice then, and I went with it.
Frankly, it was not bad. Worked out pretty well, but there were a few things that I would loved to have seen in the beginning itself, but like Morpheus said — there is a difference in knowing the path, and walking the path.
The application was supposed to support 2 languages (eng and arabic, so LTR and RTL), and that also meant 2 different fonts. Apart from that, multiple countries, so to enable context switching based on country, everything had to come from
config. On top of that, we had to be agile enough to support a/b testing. We had to clearly separate out what we could do in front-end and what had to come from back-end, so that we are not again just iterating between versions (which, by the way, is more difficult in apps, as it is not a website and is installed on your user’s devices). They may choose to not update the app.
The inspiration for the boilerplate
There were a lot of learnings from creating the single code base for our web version of business and I wanted to put in all those here.
For e.g, we structured the application into the same two separations—
presentation. We also built a lot of smaller components, what we liked to call,
- widgets: carousels component, banner component — any component providing a complete functionality, but still remained modular and isolated
- elements: Custom elements, may be
<ButtonDefault>, that have default application styles and properties like font, size and overrides the native elements.
elements especially helped us in the long run. Like the designer at a very later stage wanted to change the application’s Arabic font, as the current version had different
line-heights from english font and it was getting very hard to make the UI pixel perfect for both layouts. Since we were using a custom
<CText> element, it was literally a one line change in the application. One might say that this is a specific use case, but think about it, even for a single language application, do you want to be changing the application’s default font, styles, color in every screen, component?
The project went well. We launched the apps to the store after 3 months of development time (yes, you read it right 😎).
A year and half later
Working on a new apps project, I wanted to — again — re-use my learnings from the previous experience. Unfortunately, I had not tagged the code from where it could be branched off and continued, and the existing code had gone too far ahead to be considered. So the project was written afresh. I did try to incorporate all the learnings, but it had to be written again.
And this is where I felt I needed to write a boilerplate.
A boilerplate that not just helps anyone bootstrap an application quickly, but also has all the learnings a general react native app may face. It should try to enforce industry best practices (e.g, eslint), encourage developers to maintain a code structure, use a config (importance of this cannot be stressed enough), solve for things that may lead to a lot of re-work if changed later and can be too costly late into the development phase.
I did not find these in any existing solutions, at least free ones.
The project has been setup based off RN Getting Started and instructions from Microsoft’s Github TypeScript React Native Starter repo.
As you can see below, the boilerplate code structure is heavily inspired from the learnings of the previous applications — both web and app.
├── android Android Native code
├── ios iOS Native Code
│ ├── redux Business Logic
│ │ ├── constants
│ │ ├── actions
│ │ ├── api
│ │ ├── reducers
│ │ ├── store
│ │ └── thunk
│ └── utilities
│ ├── config
│ ├── constants Screens, Localization
│ ├── navigators Router, Navigation
│ ├── view
│ │ ├── elements Custom elements
│ │ ├── assets
│ │ ├── screens
│ │ ├── styles Typography and globals
│ │ └── widgets Custom components
│ └── utils
├── __tests__ Unit Tests
│ ├── presentation
│ └── redux
├── .eslintrc Lint configuration - AirBnb
├── .travis.yml Travis CI
├── tsconfig.json TypeScript Configuration
├── index.js Application Entry point
The application is divided into, now familiar, 2 folders —
src. This has worked out pretty well so far for me, and the plan is to extend this further.
I have tried to include a lot of learnings from my previous experiences. The code has been structured such that the developers are free to add / remove libraries / features as per their requirements (say, you want to use react-navigation, instead of react-native-navigation — just change
This is an opinionated approach to building apps with React Native. Anyone who has been coding long enough knows that there is a huge difference between writing code, and writing code that can sustain in a production environment.
Writing a web application is a whole new challenge in itself. Load time, critical rendering path, bundling, first time to render, and the list just goes on.
Using this boilerplate, we now want to move to a truly single code base, for web and apps. Taking calculated, unit tested steps, I think we should be able to build a solution that we can proudly show off as being truly cross-platform.
The plan is to write a
web folder that can use
shared, thus reducing the need to invest time in application’s business logic and focus on building and mitigating the UI related tasks / issues.
The aim of this boilerplate is to help people jumpstart into building enterprise react native apps. And hopefully face lesser surprises in the development phase at later stages.
I would love to hear any feedback anyone has. Please feel free to create feature requests, or to contribute with PRs. Otherwise, what is open source without a community.