Sandbox your React Native development
Expo CLI is a tool to install and run React Native applications. Expo is a toolchain that claims to be “the fastest way to build an app”.
You can install those tools on your local machine. But maybe you want to create a stand-alone development environment using Docker containers.
That way you can be sure of a consistent development environment across different machines.
In this post I’ll show you how to create a docker container that will allow you to develop React Native applications.
I assume that you have Docker Desktop and docker-compose installed on your machine.
If not, head over to the official website.
Instructions for my favorite OS, Arch Linux (or rather Manjaro Linux) are available on the wiki.
You’ll also need Node.js.
Let’s first make a parent folder that will hold our project. As I am on Linux, the following command will do the trick:
mkdir docker-react-native && cd docker-react-native
Then we’ll initialize the project on our local machine:
npx expo-cli init react_native_app --npm
This command will create a new folder and install all required dependencies using npm. If you rather use yarn, you can leave out the
The Docker setup relies on Bret Fisher’s node-docker-good-defaults.
Create the following
Dockerfile inside the main folder. In our example, it’s called
# pull base image FROM node:14.13.1-buster-slim # set our node environment, either development or production # defaults to production, compose overrides this to development on build and run ARG NODE_ENV=production ENV NODE_ENV $NODE_ENV # default to port 19006 for node, and 19001 and 19002 (tests) for debug ARG PORT=19006 ENV PORT $PORT EXPOSE $PORT 19001 19002 # install global packages ENV NPM_CONFIG_PREFIX=/home/node/.npm-global ENV PATH /home/node/.npm-global/bin:$PATH RUN npm i --unsafe-perm -g npm@latest expo-cli@latest # install dependencies first, in a different location for easier app bind mounting for local development # due to default /opt permissions we have to create the dir with root and change perms RUN mkdir /opt/react_native_app && chown node:node /opt/react_native_app WORKDIR /opt/react_native_app ENV PATH /opt/react_native_app/.bin:$PATH USER node COPY ./react_native_app/package.json ./react_native_app/package-lock.json ./ RUN npm install # copy in our source code last, as it changes the most WORKDIR /opt/react_native_app/app # for development, we bind mount volumes; comment out for production # COPY ./react_native_app . ENTRYPOINT ["npm", "run"] CMD ["web"]
Let’s build the docker container!
docker-react-native folder (where the
docker build -t react_native_app .
Now let’s try running it:
docker run -it --rm --name react_native_app \ -p 19006:19006 \ -v (pwd):/opt/react_native_app/app:delegated \ -v notused:/opt/react_native_app/app/node_modules \ react_native_app
Let’s break it down:
docker run: starts th docker container
-it: interactive mode with
tty- so that we can kill the docker container easily with
--rm: remove the container after stopping it
--name: give the thing a name!
-p 19006:19006: binds the port 19006 inside the docker container to localhost:19006
-v (pwd):/opt/react_native_app/app:delegated: this command mounts the current folder (which contains all our code) to the working directory of the docker container - see the commands in the
-p notused:/opt/react_native_app/app/node_modules: if you don’t use this trick, the
node_modulesfolder of your local machine will override the
nodes_modulesin Docker - that can lead to problems
http://localhost:19006 and you should be able to see the web version of the React Native app.
Typing that out is a hassle, so let’s use docker compose.
Inside the main folder, create a
docker-compose.yml file with the following content:
version: '2.4' services: react_native_app: build: context: . args: - NODE_ENV=development environment: - NODE_ENV=development tty: true ports: - '19006:19006' - '19001:19001' - '19002:19002' volumes: - ./react_native_app:/opt/react_native_app/app:delegated - ./react_native_app/package.json:/opt/react_native_app/package.json - ./react_native_app/package-lock.json:/opt/react_native_app/package-lock.json - notused:/opt/react_native_app/app/node_modules healthcheck: disable: true volumes: notused:
Now you can use the following commands:
docker-compose build: builds the container
docker-compose up: spins up the container
docker-compose up -d: spins up the container in detached mode (background)
docker-compose down --remove-orphans: brings down the container and cleans up abandoned containers
It’s easy to get confused about the file structure inside the Docker container and how it translate to our local machine.
Make sure that there are no typos. Mount and copy the data from your local machine to the correct location inside the Docker container.
In my example, I use placeholder names like
I’m sure you will want to customize those to fit your project. In that case, it’s easy to forget to change the names in the Dockerfile or
The Expo CLI is quite fragile. Often, dependencies are missing or the container can’t find the correct dependencies.
If you get
Build error: missing babel-preset-expo, go inside the folder for your Expo app. In the example it’s
package.json contain the dependency? If not, add it and rebuild the container.
npm add babel-preset-expo
Inside the parent folder (
docker-compose build docker-compose up -d
The hidden folder
.expo also often causes problems. Delete it and it will rebuild again.
Inside the main folder:
rm -rf react_native_app/.expo docker-compose up -d