← back to blog

Creating a Reusable React, Vite and TypeScript Project Setup

Web Development 8 min read

Following on from my older Blog Post (below), I thought I’d put together a new setup with Vite instead of Webpack. I’ve detailed this below.

March 2026 Update Notes:

Please Note: Vite is now considered the standard recommendation from React for starting a new React app with a build tool, and Vite’s own React templates support TypeScript out of the box.

Also, for production installs, I would now use npm ci –omit=dev rather than npm ci –only=production, as omit=dev is the current npm-documented approach for leaving dev dependencies out of the installed tree.

Lastly, the node:<version>-alpine image is also a sensible choice where you want a smaller Linux-based Node image, as Alpine-based images are much slimmer than many alternatives.

You can check out the repository and setup here: Github: Cactusman07/react-vite-setup – feel free to clone it, and follow the Readme.md to get started. Please be aware that you should probably run an npm audit & update packages as it is now a couple of years old 🙂


As part of modernising my front-end development workflow, I wanted to spend some time moving away from heavier Webpack-based project setups and try a cleaner, faster approach using Vite.

Webpack has been a huge part of the JavaScript ecosystem for a long time, and it is still used in many production applications. But for new React projects, especially smaller applications, prototypes, internal tools, or reusable starter projects, Vite provides a much simpler developer experience.

To explore this properly, I created a reusable React, Vite and TypeScript setup that I can use as a starting point for future projects.

The goal of this project was not to build a large application. It was to create a clean foundation that solves the boring setup problems once, so future projects can start faster.

Why Vite?

Vite gives you a fast local development server, modern build tooling, and hot module reloading with very little configuration.

For React projects, this makes the development experience feel much lighter. Instead of spending time configuring loaders, plugins, transpilation, and development servers manually, Vite gives you a sensible starting point out of the box.

The setup includes:

The aim was to keep the project minimal, but still practical enough to reuse.

Why TypeScript?

TypeScript is now my preferred default for React projects. It helps catch mistakes earlier, makes components easier to understand, and improves the development experience when working with props, state, API responses, and shared types.

Even in a simple starter project, TypeScript adds value because it creates a stronger foundation from the beginning.

Rather than adding TypeScript later, this setup starts with it from day one.

Why Docker?

One of the main things I wanted from this setup was consistency.

It is common for different developers to have slightly different versions of Node installed locally. One person might be using Node 18, another Node 20, and another might have an older global npm setup. These differences can create bugs that are hard to reproduce.

Using Docker helps avoid that.

This project uses a Node Alpine image so the development environment runs inside a container. That means the Node version and environment are isolated from the host machine.

The benefit is simple: everyone working on the project can run the same environment.

Using Node Alpine

The project uses a node:alpine image because it is small and lightweight.

For development projects, this keeps the container lean. For production-style builds, smaller images can also help reduce image size and unnecessary operating system dependencies.

The important thing here is that the Node environment is defined by the project, not by whatever happens to be installed on a developer’s machine.

Docker Compose Setup

The project uses a docker-compose.yml file to define the development container.

A few key parts of the setup are worth understanding.

Entrypoint

entrypoint: /bin/sh

The entrypoint defines what runs when the container starts.

In this case, the container starts with a shell, which allows you to log into the container and run commands manually. This is useful for a development setup because you can install packages, run the dev server, and inspect the environment from inside the container.

Working Directory

working_dir:

The working_dir defines the directory inside the container where commands will run.

When you log into the container, you land in the project directory. This keeps the development workflow simple because you do not need to navigate around the container manually every time.

Bind Mounts

type: bind

A bind mount connects files from your host machine into the container.

This is what makes local development practical. When you update a file on your machine, the change is reflected inside the container. When the Vite dev server is running, those changes can trigger hot reloading in the browser.

This gives you the consistency of Docker while still allowing you to edit files normally in your local editor.

Building the Container

To build the Docker image and start the container for the first time, run:

docker-compose up --build --no-recreate -d

In this project, I also added an npm script shortcut:

npm run docker-build

You only need to rebuild the image when the Docker configuration changes. For example, if you update the docker-compose.yml file, change the base image, or alter the container setup, you should rebuild.

After the first build, you can usually start the container with:

docker-compose up -d

Or using the npm script:

npm run docker-up

Once the container is running, you can check it with:

docker-compose ps

This confirms that the container has started successfully.

Logging Into the Container

Once the container is running, the next step is to log into it.

docker exec -it vite-react-example sh

Or using the npm script:

npm run docker-login

This opens a shell inside the container.

From there, you are working inside the isolated Node environment defined by the project.

Installing Dependencies and Starting the App

Once inside the container, install the project dependencies and start the Vite development server:

npm i && npm run dev

Or use the shortcut:

npm run docker-install

This installs the required npm packages and starts the development server.

The application should then be available at:

http://localhost:8000/

At this point, hot reloading should work. If you make a change to a React component, the browser should update automatically without needing a full manual refresh.

Development Workflow

The usual workflow looks like this:

npm run docker-up
npm run docker-login
npm i
npm run dev

Or, when setting up for the first time:

npm run docker-build
npm run docker-login
npm run docker-install

The idea is to keep the common commands easy to remember, while still allowing the underlying Docker and npm commands to be visible and understandable.

This is important for starter projects. A good template should not feel like magic. It should make common tasks easier, while still being clear enough that you can debug it when needed.

Production Builds

For production or CI/CD environments, I prefer to use:

npm ci

instead of:

npm install

The reason is that npm ci installs dependencies based on the lockfile. This makes it more predictable for automated builds because it avoids unexpected dependency changes.

In a continuous integration flow, predictability matters. You want the same dependency tree installed each time unless the lockfile has intentionally changed.

For production-style installs where development dependencies are not needed, I would use:

npm ci --omit=dev

This keeps the install focused on production dependencies only.

In a Dockerfile or production image, it also makes sense to explicitly set:

ENV NODE_ENV=production

This makes the runtime intent clear and helps ensure packages and scripts behave as expected in a production environment.

What This Setup Is Useful For

This setup is useful for projects where I want a clean React starting point without having to repeat the same configuration every time.

Examples include:

It gives me a fast development environment, TypeScript support, Docker consistency, and a simple structure that can be extended as the project grows.

What I Like About This Approach

The main benefit is that it keeps the setup simple.

Vite handles the front-end development experience. React provides the component model. TypeScript adds safety and better tooling. Docker keeps the environment consistent.

Together, they make it easier to start a project without immediately getting buried in configuration.

This kind of setup is also a good reminder that not every project needs a huge framework or complicated build pipeline. Sometimes the best starting point is a small, well-understood foundation that you can build on as needed.

Final Thoughts

This project was a useful exercise in creating a reusable React setup that feels modern, lightweight, and easy to work with.

Moving from Webpack-style setups to Vite makes the local development experience noticeably faster and simpler. Adding Docker means the environment is easier to reproduce. Adding TypeScript gives the project a stronger base from the beginning.

For future React projects, this gives me a clean starting point that I can reuse, adapt, and build on without having to repeat the same setup work each time.

You can check out the repository and setup here: Github: Cactusman07/react-vite-setup