Containerising a React application

Ewere Ebie
6 min readMay 5, 2023

We will be working with a Gallery application made with create-react-app called snapshot. In the end, we should have our app running within a container and accessible on our host machine from a designated port.

Prerequisites

To complete this lesson you should have

Step 1 —Pull down the repository

Open your terminal on the folder you wish to have this repository and pull down the repo with the command

git clone https://github.com/Yog9/SnapShot.git

once it's complete open the folder with your code editor.

Step 2— Designing an Image

The first step to creating a container is to first design an Image. What is an image? It is the step-by-step guide that docker follows to initialize a container. For better understanding think of it as the blueprint for your container just like a class is for an object in Object oriented programming.

Create a dockerfile with your editor at the root folder of your project and populate it with the following content.

FROM node:16.0.0-alpine

WORKDIR /app
COPY ./package.json ./yarn.lock ./
RUN yarn install --pure-lockfile
COPY ./ ./

EXPOSE 3000

CMD ["yarn", "start"]

In the first line, we are pulling the image node:16.0.0-alpine from Docker hub so we can base our image on it. This will save us time as the image is prepackaged with a Linux distribution as well as Node.js and some other utilities like yarn. I used version 16.0.0 just out of preference and because it’s stable.

In the second line, we create and enter a working directory called app within our image that will serve as the root folder for our project when the container is created.

In the third line, we copy the dependency files(package.json and yarn.lock) into the app folder(./).

Installation of the necessary dependencies happens in the fourth line, before copying the rest of the project into the image in the fifth line.

We request port 3000 to be exposed for access during container creation by the host machine at the sixth line because when the application starts it will run on port 3000. This will enable us to reach the app within the container from a browser.

And lastly, on the seventh line, we specify the start script located in package.json as the instruction to be executed when the docker container starts.

Step 3 — Building an Image

Now we have our docker file ready to go it's time to create our image. Run the command below from a terminal pointed at the root of your project where your Dockerfile is located.

docker build . -t snapshot-image

docker build is the actual command while the period sign(.) after it specifies the folder context it should expect to find a Dockerfile. The tag flag(-t) is used to pass in the name(snapshot-image) docker will associate with your image to make it easily identifiable. Once this is done you can confirm your image has been created by running the command below.

docker image ls

Step 4— Creating a container

Finally, it's time to start up our application by instantiating our image to create a container. Paste the command below to create a new container.

docker run -it -p 4300:3000 --name snapshot-app snapshot-image

docker run is the actual command, the flag -it tells docker to remain interactive allowing us to see the output of our app’s start command from within the container in our terminal.

-p 4300:3000 is used to map the container port to a port on our host machine. 3000 is the port we exposed in our docker file and is the port our react app is running within our container, while 4300 is the host port we want to use in reaching our application from a browser.

-name snapshot-app is used to give the container the name “snapshot-app” and we append the image name “snapshot-image” from which to create our container.

To confirm your container is running open another terminal and run the command below, you should see the container name listed in the output

docker ps

Now the container is up and running you should see an output stating the code was compiled successfully along with links to the running application like so.

If you try to visit these links you would notice the site can’t be reached. This is because the app was started on port 3000 within our container but is exposed on port 4300 on our host machine. I did this port mapping purposely to prove to you that the application is not running on our host environment but is working from an isolated space (container) running a Linux distribution.

To reach our app we must use the right link http://localhost:4300/SnapShot

Viola!!!

We have successfully containerised a react application. 😁

Once you commit your docker file and push it to a repo anyone can start up the application from whatever Operating system running docker and expect it to behave the same every time.

Wait… There’s more

If we make a change to a file in our project the development server running in the container will not refresh because the app we started is isolated from the host. We need a way to maintain a connection between our project directory and that of the container. This is where bind mounts and volumes come in.

Bind mounts link a directory on the host machine to one within the container allowing host file modification automatically reflect so we don't need to rebuild an image and restart a container again.

Kill the running container with these commands

docker stop snapshot-app && docker rm snapshot-app

To bind our project directory to one within a new container we need to get the directory path of our project on the host machine. For windows use the cd command and for UNIX systems pwd will work as well as the env variable $PWD.

docker run -it -p 4300:3000 --name snapshot-app --rm -v /app/node_modules -v /Users/mac/Documents/tuts/SnapShot:/app snapshot-image

the -v flag is used to create an anonymous volume for the node_modules folder within the container and bind our host to the container. We need an anonymous volume because when we create a host volume the contents of the container will be replaced by the contents of the host project folder which has no node_modules in it.

The — rm flag is used so when the container is removed it will also delete the anonymous volume.

When you make changes to your project, it should refresh the container build and reflect on the browser. Hurray!!! 🎉

Conclusion

We’ve seen the process of creating containers with images and associating them with volumes. This setup will enable your projects to become more stable and predictable in the long run.

I’m glad you took the time to read my blog. If you liked it give me a clap and follow my medium account for more content like this. Thanks 👋.

References

--

--

Ewere Ebie

I write because it’s less exhausting than speaking. And its fun