DEV Community

Cover image for How to use React or Vue with Vite and Docker
Nekhil Malik
Nekhil Malik

Posted on • Edited on

How to use React or Vue with Vite and Docker

Introduction

Hello fellow developers!

With this guide, we will be creating a project that includes React or Vue as the front-end framework and Vite as the server, and Docker as the development environment.


[Update 16th Aug 2024]

Added the Resource Section


Prerequisites

These should be available on the host machine

  • Node
  • Docker, Docker Compose (In any form CLI / Desktop client)

Basic knowledge of

  • React
  • Vite
  • Docker
  • Makefile (not mandatory, just to make life easier)

These definitions will be in the references of the article and give you a very small idea of these terms. For more information, use the links provided with the definitions.

What is Vite?

Vite is a revolutionary frontend development tool that provides a more rapid development environment for developers as compared to existing solutions.

Vite uses source code over native ESM. And by using ES build and ESM under the hood, Vite enhances startup speed 10-100x.

This supports almost every frontend framework, react, vue, and so on.

I suggest you go ahead and look at their website and specifically this one


What is Docker?

Docker is an environment provider for your application regardless of the technology and it separates that from the host (based on your choice).

Although it does so many things and makes the life of dev to ops very easy. I will leave those things to you.


What is Makefile?

Make is a Linux utility that allows us to bind multiple shell commands to a single command. The Makefile is used for it.

We can use package.json > scripts for the same as well but I prefer to use it as it works with non-node projects as well.


Let's Start.

Step 1: Create the project

First, we will use this command to create a Vite project

npm create vite@latest
Enter fullscreen mode Exit fullscreen mode

In this step, it will ask you these questions

  • App Name
  • Js Framework - React / Vue ..
  • Framework Variant - Javascript / TypeScript

Framework

js

Choose accordingly. I have selected React for now.

Note: I am using node version v17.8.0


Step 2: Update Vite Config

We need to specify the host and port in vite.config.js in order to work with the Docker.

export default defineConfig({
 plugins: [react()],
 server: {
   host: true,
   port: 8000, // This is the port which we will use in docker
   // Thanks @sergiomoura for the window fix
   // add the next lines if you're using windows and hot reload doesn't work
    watch: {
      usePolling: true
    }
 }
})
Enter fullscreen mode Exit fullscreen mode

Step 3: Setup Docker

I am intending to use docker-composer for it as it's easier to scale as compared to relying on Dockerfile.

First, we will add docker-compose.yml within the root of the project.

version: "3.4"
services:
 vite_docker:
   image: node:alpine
   container_name: vite_docker
   entrypoint: /bin/sh
   ports:
     - 8000:8000
   working_dir: /srv/app
   volumes:
     - type: bind
       source: ./
       target: /srv/app
   tty: true
Enter fullscreen mode Exit fullscreen mode

This is pretty straightforward from the labels. I am adding the meaning again.

image: node:alpine
Enter fullscreen mode Exit fullscreen mode

Docker will choose the node:alpine image to build this env. You can choose node:lts, node:latest but I prefer alpine as it's small in size and suitable for production as well.

container_name: vite_docker
Enter fullscreen mode Exit fullscreen mode

The container name will be vite_docker which we will use to execute future commands and refer to this application.

entrypoint: /bin/sh
Enter fullscreen mode Exit fullscreen mode

The entry point refers to the application that will execute at the time of entry within the container. It could be /bin/bash etc but as I am using an Alpine image, you have to add those dependecies.

ports
Enter fullscreen mode Exit fullscreen mode

The ports on which the application will run, in our case 8000, and with this mapping, we will be able to access the host machine using the same port.

working_dir
Enter fullscreen mode Exit fullscreen mode

The working_dir is the directory for the project within the container. When you log into the container, you will land in this directory.

volumes
Enter fullscreen mode Exit fullscreen mode

The key configuration here is the volumes. This will allow us to use the host file within the container and we will be able to work on it by building or restarting the container.

Here I am using type: bind which allows us to do this and update the files within the container as we update/ add anything in the host and vice versa.

Also with this, you need to build and run it every time as you will modify the host.

   volumes:
     ./: /srv/app
Enter fullscreen mode Exit fullscreen mode

It is useful in other situations, but avoids syncing between hosts and containers.

tty
Enter fullscreen mode Exit fullscreen mode

The tty is used to run the container continuously.


Step 4: Build the Docker Container

So far, we have added scripts to build our environment and have these files in our project.

files

Run this command to build the image on your local machine and start the container. You only need to run this command the first time, and whenever you make changes to docker-compose.yml.

docker-compose up --build --no-recreate -d
Enter fullscreen mode Exit fullscreen mode

From the second time, we can use

docker-compose up -d
Enter fullscreen mode Exit fullscreen mode

Now our container is up and you should be able to test it using the following command.

docker-compose ps
Enter fullscreen mode Exit fullscreen mode

This is what it should look like

docker-up


Step 5: Build and start the Application

Just to clarify, we have a running container, but not the installed or running react app. For that, we need to log into the container and then execute the commands.

docker exec -it vite_docker sh
Enter fullscreen mode Exit fullscreen mode

We have entered the container and now need to run the commands to install the Node packages and start the app.

npm i && npm run dev
Enter fullscreen mode Exit fullscreen mode

It will install the packages and the application will run on the defined ports.

npm i

I already had the packages so it is not showing the logs related to the packages.

These will be the files in the project. Please ignore Makefile (it's a bonus for later :) )

files after the installation

and the running app

app


Step 6: Modification in the project files

So far, everything has been running smoothly. Using the browser, we can access the project from the host after creating the Docker container and installing the app packages.

But that's not everything, right?

What about changing the heading Vite + React or adding new components and different layouts? Also, we don't want to rebuild the container every time we make any changes and we are doing it to make our life easier, not difficult :)

Let's do it.

Video

app-running

With this, we can see the changes from the host are getting reflected in the container (due to volumes > bind) and we can see those changes in the browser without any build or restarting of the container and Vite is doing its job perfectly.


Resources

Important files and you will do the remaining work and learn

GitHub Link for the files

Lazy like me :) Working Project with updated docker compose config

With this you can clone the project and start working directly. This project depends on these utilities

These will be the commands which you need to execute

If you have the make utility

// Only first time 
make first

// All the time
make start
Enter fullscreen mode Exit fullscreen mode

If you have the node installed in the host. This will also run the docker commands

// Only first time 
npm run first

// All the time
npm run start
Enter fullscreen mode Exit fullscreen mode

GitHub Link for the Branch


Bonus

Throughout all the steps, we worked through various commands. If you are familiar with Docker, then that is great. Otherwise, it may seem overwhelming at first.

Therefore, I always prefer using something that allows me to combine these commands into one, with a more meaningful name or whatever I prefer.

Makefile

Here comes the Makefile, so with this you will need an extra utility on your OS but it will be very helpful. Make utility is used to combine various shell commands into one. Using what we did in steps 4 and 5, we can combine everything into a single command.

To install and run the dev server, we can have this

make first
// make is the utility
// first is command from the Makefile
Enter fullscreen mode Exit fullscreen mode

And next time, we can always use

make start
Enter fullscreen mode Exit fullscreen mode

There are various other commands related to restart, clear, stop which we can use to simply execute multiple commands at once.

and the commands are in the Makefile

CURRENT_DIR=$(patsubst %/,%,$(dir $(realpath $(firstword $(MAKEFILE_LIST)))))
ROOT_DIR=$(CURRENT_DIR)
CURRENT_USER=
DOCKER_NAME=vite_docker
DOCKER_COMPOSE?=docker-compose
DOCKER_EXEC_TOOLS_APP=$(CURRENT_USER) docker exec -it $(DOCKER_NAME) sh
NODE_INSTALL="npm i"
SERVER_RUN="npm run dev"


.PHONY: build install dev up start first stop restart clear


build:
 $(DOCKER_COMPOSE) up --build --no-recreate -d


install:
 $(DOCKER_EXEC_TOOLS_APP) -c $(NODE_INSTALL)


dev:
 $(DOCKER_EXEC_TOOLS_APP) -c $(SERVER_RUN)


up:
 $(DOCKER_COMPOSE) up -d


start: up dev
// this will up the docker env and run the npm run dev it to


first: build install dev
// this will build the env, up it and run the npm install and then run npm run dev it to


stop: $(ROOT_DIR)/docker-compose.yml
 $(DOCKER_COMPOSE) kill || true
 $(DOCKER_COMPOSE) rm --force || true


restart: stop start dev


clear: stop $(ROOT_DIR)/docker-compose.yml
 $(DOCKER_COMPOSE) down -v --remove-orphans || true
Enter fullscreen mode Exit fullscreen mode

Note: You can always use package.json > scripts as a complete/ partial replacement for Make.

Final notes

I appreciate you taking the time to read the article! Feel free to comment if you have any questions.

I'd be pleased to connect with you on LinkedIn

Top comments (12)

Collapse
 
sergiomoura profile image
SΓ©rgio Moura

Great tutorial!

Seeing the "Makerfile" bonus, i noticed it was not oriented for windows users.
Anyway, I executed the steps and this just worked fine, except for the sweet hot reload effect. After some research, i found the solution. Just add,
watch: {usePolling: true} to the server config. Now, my vite.config.ts is like this:

export default defineConfig({
  plugins: [react()],
  server: {
    host: true,
    port: 8000,

    // add the next lines if you're using windows and hot reload doesn't work
    watch: {
      usePolling: true
    }
  },
})
Enter fullscreen mode Exit fullscreen mode

Thank you, Malik!

Collapse
 
nekhilmalik profile image
Nekhil Malik

Thanks @sergiomoura for pointing out this one. I am adding it to article. Hope you won't mind :)

Collapse
 
wotta profile image
Wouter van Marrum

Nice and simple demonstration. Thank you for taking the time to put this together and sharing this!

Collapse
 
nekhilmalik profile image
Nekhil Malik

Thanks Wouter for the kind words. Surely, it will motivate me to add the GitHub link for these files as well ;)

Collapse
 
ken_mwaura1 profile image
Zoo Codes

My Workflow preferred using Shell files to automate starting up containers and scripts. But now seeing the use of make is definitely an awesome alternative, time to dig into makefiles. Great write-up

Collapse
 
duongphuhiep profile image
DUONG Phu-Hiep • Edited

This article show how to run a Vite Dev Server in a Docker container, expose it to the port 8000 to serve a local project.. I don't understand in which circumstance we need this Docker container? You already installed vite on your local in the first step so why not run it directly with 'vite --port 8000` to serve the local project?

Collapse
 
nekhilmalik profile image
Nekhil Malik

Hello @duongphuhiep, sorry for the late reply. It's true, I can't say anything logically that can make mistake a correct statement.

I purpose, as long as we do not use the first step, and we have a package.json with packages, then we can proceed from step two and we won't need npm/vite on the host machine.

I will test it myself and update the blog. Thanks again for pointing this out.

Collapse
 
miggu profile image
miggu

Good question. I can immediately think of two reasons of doing this.

  1. You will isolate the node to a specific version, and when working with teams , you will make sure everyone is running the same version on node. (unfortunately this could have been explained in the tutorial)
  2. If you're going to deploy it later via CI.
Collapse
 
anujsd profile image
Anuj Dube

Great Article, was facing some issues while containerizing web app. This article saved lot of my time.

Collapse
 
nekhilmalik profile image
Nekhil Malik

Glad to here that :) thanks.

Collapse
 
hunkoys profile image
Dominic

I like your post!

What's the reasoning behind chosing to put --no-recreate in your build?

Collapse
 
nekhilmalik profile image
Nekhil Malik • Edited

For me, it's a matter for choice. The main reason to select this option is that I don't want to recreate my container even if something is changed in the image.

As I am using node:alpine which could point to different image after some time with new version of node and my system could try to download the new one and could change the whole env. It won't affect this simple project but in a big project that could cause issue.

BTW Happy New Year :)