React project setup with Vite for faster development | Blazing fast server startup and updates
Are you bored waiting CRA to create your project and development server to start spinning? Let's see what Vite can do for us.
Situation
We are working on a large project using React with many routes, and we have a deadline.
CRA takes too much time to create a new project in an average specs device. We want our new project and development server ready in seconds.
We are starting to build more and more ambitious applications. The amount of JavaScript we are dealing with also increased exponentially. It is common for large-scale projects to contain thousands of modules.
We are starting to hit a performance bottleneck for JavaScript-based tooling. It can often take an unreasonably long wait (sometimes up to minutes!) to spin up a dev server. And even file edits can take a couple of seconds to be reflected in the browser.
Let's see what Vite can do for us.
Some of the features of Vite.
NPM Dependency Resolving and Pre-Bundling
import { someMethod } from 'my-dep'
Vite will detect such bare module imports in all served source files and perform the following:
- Pre-bundle them to improve page loading speed and convert CommonJS / UMD modules to ESM. The pre-bundling step is performed with esbuild and makes Vite's cold start time significantly faster than any JavaScript-based bundler.
- Rewrite the imports to valid URLs like
/node_modules/.vite/my-dep.js?v=f3sf2ebd
so that the browser can import them properly. - Vite caches dependency requests via HTTP headers.
Hot Module Replacement
Frameworks with HMR (Hot Module Replacement) capabilities can leverage the API to provide instant, precise updates without reloading the page or blowing away application state.
TypeScript
Vite supports importing .ts
files out of the box. However, Some configuration fields under compilerOptions
in tsconfig.json
require special attention. Checkout more if you use TypeScript.
Vite uses esbuild
to transpile TypeScript into JavaScript which is about 20~30x faster than vanilla tsc
, and HMR updates can reflect in the browser in under 50ms.
JSX
.jsx
and .tsx
files are also supported out of the box.
CSS
Importing .css
files will inject its content to the page via a <style>
tag with HMR support. You can also retrieve the processed CSS as a string as the module's default export.
CSS Modules
Any CSS file ending with .module.css
is considered a CSS modules file. Importing such a file will return the corresponding module object:
/* example.module.css */
.red {
color: red;
}
import classes from './example.module.css';
function App() {
return <p className={classes.red}>Hello world</p>
}
CSS modules behavior can be configured via the css.modules
option.
If css.modules.localsConvention
is set to camelCaseOnly
, you can also use named imports:
/* vite.config.js */
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
// https://vitejs.dev/config/
export default defineConfig({
plugins: [react()],
css: {
modules: {
localsConvention: 'camelCaseOnly',
},
},
});
import { red } from './example.module.css';
function App() {
return <p className={red}>Hello world</p>
}
CSS Pre-processors
Good news for developers if you are a Sass user like me. Vite also provide built-in support for .scss
, .sass
, .less
, .styl
and .stylus
files. There is no need to install Vite-specific plugins for them, but the corresponding pre-processor itself must be installed:
# .scss and .sass
npm install sass --save-dev
# .less
npm install less --save-dev
# .styl and .stylus
npm install stylus --save-dev
You can also use CSS modules combined with pre-processors by prepending .module
to the file extension, for example style.module.scss
.
Static Assets
Importing a static asset will return the resolved public URL when it is served:
import imgUrl from './img.png'
document.getElementById('hero-img').src = imgUrl
Special queries can modify how assets are loaded:
// Explicitly load assets as URL
import assetAsURL from './asset.js?url'
// Load assets as strings
import assetAsString from './shader.glsl?raw'
// Load Web Workers
import Worker from './worker.js?worker'
// Web Workers inlined as base64 strings at build time
import InlineWorker from './worker.js?worker&inline'
Dynamic URLs
function getImageUrl(name) {
return new URL(`./dir/${name}.png`, import.meta.url).href
}
import.meta.url
is a native ESM feature that exposes the current module's URL. Combining it with the native URL
constructor, we can obtain the full, resolved URL of a static asset using relative path from a JavaScript module.
NOTE: Does not work with SSR.
This pattern does not work if you are using Vite for Server-Side Rendering, because import.meta.url
have different semantics in browsers vs. Node.js. The server bundle also cannot determine the client host URL ahead of time.
JSON
JSON files can be directly imported - named imports are also supported:
// import the entire object
import json from './example.json'
// import a root field as named exports - helps with treeshaking!
import { field } from './example.json'
CSS Code-Splitting
Vite automatically extracts the CSS used by modules in an async chunk and generates a separate file for it. The CSS file is automatically loaded via a <link>
tag when the associated async chunk is loaded, and the async chunk is guaranteed to only be evaluated after the CSS is loaded to avoid FOUC.
Preload Directives Generation
Vite automatically generates <link rel="modulepreload">
directives for entry chunks and their direct imports in the built HTML.
Env Variables
Vite exposes env variables on the special import.meta.env
object. Some built-in variables are available in all cases:
import.meta.env.MODE
: {string} the mode the app is running in.import.meta.env.BASE_URL
: {string} the base url the app is being served from. This is determined by thebase
config option.import.meta.env.PROD
: {boolean} whether the app is running in production.import.meta.env.DEV
: {boolean} whether the app is running in development (always the opposite ofimport.meta.env.PROD
)
.env
Files
Only variables prefixed with VITE_
are exposed to your Vite-processed code. e.g. the following file:
DB_PASSWORD=foobar
VITE_SOME_KEY=123
Only VITE_SOME_KEY
will be exposed as import.meta.env.VITE_SOME_KEY
to your client source code, but DB_PASSWORD
will not.
If you want to customize env variables prefix, see envPrefix
option.
❤️❤️ These were the important features of Vite.
Now let's create our React project with Vite.
- Run
npm init vite@latest
and enter you project name.
$ npm init vite@latest
? Project name: › vite-project
- Select your framework supported by Vite.
✔ Project name: … vite-project
? Select a framework: › - Use arrow-keys. Return to submit.
❯ vanilla
vue
react
preact
lit
svelte
- Select your variant.
✔ Project name: … vite-project
✔ Select a framework: › react
? Select a variant: › - Use arrow-keys. Return to submit.
❯ react
react-ts
✔ Project name: … vite-project
✔ Select a framework: › react
✔ Select a variant: › react
Scaffolding project in /home/raman/Documents/vite-project...
Done. Now run:
cd vite-project
npm install
npm run dev
By now your project should be ready. Now change your current directory with
cd vite-project
and just install dependencies withnpm install
.After installing dependencies, spin up your dev server with
npm run dev
.
NOTE:
npm start
doesn't work with Vite.
vite v2.5.10 dev server running at:
> Local: http://localhost:3000/
> Network: use `--host` to expose
ready in 1049ms.
If you don't believe this message, try on your own 😂😂. I was blown away myself when I saw this message.
Deploying a static site
Command for creating static files for production is npm run build
. But Vite creates our static files inside /dist
folder instead of /build
.
So if you use Netlify for deploying your React apps like me, Remember to setup:
- Build Command:
npm run build
- Publish directory:
dist
Checkout more for deploying with other hosting services:
- GitHub Pages
- GitHub Pages and Travis CI
- GitLab Pages and GitLab CI
- Google Firebase
- Surge
- Heroku
- Vercel
- Azure Static Web Apps
So, that's it on this topic. Hope you found this tool helpful.
Let me know if I missed something.
Thank you for reading this blog. ❤️❤️