DEV Community

Cover image for Better ways to Create React App
Valeria
Valeria

Posted on

Better ways to Create React App

If you've tried React, chances are you've used create-react-app at least once. In case you were lucky and you haven't, here's how it goes:

You run something along the lines of:

npx create-react-app my-app
Enter fullscreen mode Exit fullscreen mode

And observe the following console output:

Installing packages. This might take a couple of minutes.
Installing react, react-dom, and react-scripts with cra-template...


added 1394 packages in 44s

209 packages are looking for funding
  run `npm fund` for details

Initialized a git repository.

Installing template dependencies using npm...

added 55 packages in 4s

209 packages are looking for funding
  run `npm fund` for details
Removing template package using npm...


removed 1 package, and audited 1449 packages in 3s

209 packages are looking for funding
  run `npm fund` for details

6 high severity vulnerabilities
Enter fullscreen mode Exit fullscreen mode

Yup, you've got yourself almost 1.5 thousand packages and a dash of vulnerabilities.

Wanna see something scary? Check the folder size:

du -hc -s  node_modules
# 318M  node_modules
Enter fullscreen mode Exit fullscreen mode

No wonder this came along:
Heaviest objects in universe: node_modules folder wins

I'm puzzled why is this the recommended way, but gladly it's not the only one.

All-in-one: Vite

Vite is a batteries included bundler & development server with presets for multiple frameworks, including React with or without TypeScript.

To create an app with Vite run:

npm create vite@latest my-vite-react-app
# βœ” Select a framework: β€Ί react
# βœ” Select a variant: β€Ί react
#
# or
#
npm create vite@latest my-vite-react-app -- --template react
Enter fullscreen mode Exit fullscreen mode

Navigate to the folder and install dependencies:

cd my-vite-react-app && npm install
Enter fullscreen mode Exit fullscreen mode

That was much faster and less greedy:

added 87 packages, and audited 88 packages in 5s

8 packages are looking for funding
  run `npm fund` for details

found 0 vulnerabilities
Enter fullscreen mode Exit fullscreen mode

And it needs almost ten times less space too:

du -hc -s  node_modules
# 37M   node_modules
Enter fullscreen mode Exit fullscreen mode

You can stop reading right here if you need a replacement to create-react-app with all the features it offers. It doesn't get better than vite.

For those of you, eager to push it even further at the cost of simplicity I have one more alternative 😎

Need for speed: esbuild

Let's dig a bit deeper and take a look on the insides of both create-react-app (CRA) and vite. Both of them are a set of various tools configured to work together. CRA relies on webpack to bundle your files, while vite uses a bit more modern rollup. However they all are written in JavaScript and hence are capped by the performance of NodeJS script itself.

Enter esbuild: written in Go and advertised to be 10-100 times faster than any other popular bundler.

There's a catch, however: it is just a bundler. It doesn't come with one-liner to create an app and needs a bit of tinkering to get development server up & running.

Let's start by creating a folder:

mkdir my-esbuild-app && cd my-esbuild-app
Enter fullscreen mode Exit fullscreen mode

Next, install esbuild & react dependencies:

npm init -y && npm install esbuild --save-dev && npm i react react-dom --save
# added 7 packages, and audited 8 packages in 828ms
# found 0 vulnerabilities
Enter fullscreen mode Exit fullscreen mode

Expectedly the size is the least of all:

du -hc -s  node_modules
# 14M   node_modules
Enter fullscreen mode Exit fullscreen mode

Prepare yourselves: it's time for the promised tinkering πŸ§‘β€πŸ”¬

Create a folder with your public files:

mkdir public &&
echo '<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Esbuild + React</title>
    <link rel="stylesheet" href="/dist/main.css" />
  </head>
  <body>
    <div id="root"></div>
    <script src="/dist/main.js"></script>
  </body>
</html>' > public/index.html 
Enter fullscreen mode Exit fullscreen mode

And some source files:

mkdir src &&
echo ':root {
  font-family: Inter, Avenir, Helvetica, Arial, sans-serif;
  font-size: 16px;
  line-height: 24px;
  font-weight: 400;

  color-scheme: light dark;
  color: rgba(255, 255, 255, 0.87);
  background-color: #242424;

  font-synthesis: none;
  text-rendering: optimizeLegibility;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  -webkit-text-size-adjust: 100%;
}

a {
  font-weight: 500;
  color: #646cff;
  text-decoration: inherit;
}
a:hover {
  color: #535bf2;
}

body {
  margin: 0;
  display: flex;
  place-items: center;
  min-width: 320px;
  min-height: 100vh;
}

h1 {
  font-size: 3.2em;
  line-height: 1.1;
}

button {
  border-radius: 8px;
  border: 1px solid transparent;
  padding: 0.6em 1.2em;
  font-size: 1em;
  font-weight: 500;
  font-family: inherit;
  background-color: #1a1a1a;
  cursor: pointer;
  transition: border-color 0.25s;
}
button:hover {
  border-color: #646cff;
}
button:focus,
button:focus-visible {
  outline: 4px auto -webkit-focus-ring-color;
}

@media (prefers-color-scheme: light) {
  :root {
    color: #213547;
    background-color: #ffffff;
  }
  a:hover {
    color: #747bff;
  }
  button {
    background-color: #f9f9f9;
  }
}

#root {
  display: flex;
  align-items: center;
  justify-content: center;
  flex: 1;
}' > src/index.css &&

echo 'import React, { useState } from "react";
import ReactDOM from "react-dom/client";
import "./index.css";

const App = () => {
  const [count, setCount] = useState(0);
  return (
    <div className="App">
      <h1>Esbuild + React</h1>
      <div className="card">
        <button onClick={() => setCount((count) => count + 1)}>
          count is {count}
        </button>
      </div>
    </div>
  );
};

ReactDOM.createRoot(document.getElementById("root")).render(
  <React.StrictMode>
    <App />
  </React.StrictMode>
);' > src/main.jsx
Enter fullscreen mode Exit fullscreen mode

And finally, run the development server:

./node_modules/.bin/esbuild src/main.jsx --servedir=public --outdir=public/dist  --bundle
Enter fullscreen mode Exit fullscreen mode

And it works πŸ˜„
If you're up for a little bit challenge - I encourage you to add some assets to the page, e.g. an SVG logo from one of the previous apps. You'll need to set up a loader for that πŸ˜‰.

I must add though: at the moment there isn't a fast and easy way to get hot module replacement to work. To be honest, out of the box you are to refresh the page manually on every change, but live reload is a bit easier to setup.

Conclusion

Create-react-app is slow & heavy and Vite provides much smoother developer experience and works out of the box. However if you're building a huge project or need the smallest possible footprint - esbuild is worth looking into.

Top comments (16)

Collapse
 
joelbonetr profile image
JoelBonetR πŸ₯‡

You can also use Parcel bundler which is quite convenient most of the time

npm init -y && npm i parcel -D && npm i react react-dom -s
Enter fullscreen mode Exit fullscreen mode

Didn't you tried it before?

Collapse
 
valeriavg profile image
Valeria

Yes, I believe I did, though don't recall much. How does it fair in comparison to the tools from the article?

Collapse
 
joelbonetr profile image
JoelBonetR πŸ₯‡ • Edited

Didn't check tbh, I used it once on a production project and most of the time on side projects due to this "zero config"; actually it has a couple of quirks that need to be understood though. At the point we choose parcel, the output was definitely lower in weight than webpack, but I didn't tested it against rollup.

The setup speed is quite good and it handles config and deps whenever it found them in the project.

Quick example. If your entry point look like that:

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
  </head>
  <body>
    <div id="app"></div>
  </body>
</html>
Enter fullscreen mode Exit fullscreen mode

and you want to add a global scss, you can simply do:

<link rel="stylesheet" href="./styles/main.scss">
Enter fullscreen mode Exit fullscreen mode

and Parcel will install some scss-to-css parser as dependency, also optimize it with cssnano or any other optimizer and build the entire thing, replace the reference to the scss file in the html for a reference to the parsed, minified, optimized and hashed css version and serve it for you.

Isn't automation the absolute end target in our field? πŸ˜πŸ˜‚

Thread Thread
 
valeriavg profile image
Valeria

I tested parcel as you've suggested, here's what I've got:

npm i parcel --save-dev
npm WARN deprecated stable@0.1.8: Modern JS already guarantees Array#sort() is a stable sort, so this library is deprecated. See the compatibility table on MDN: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort#browser_compatibility

added 166 packages, and audited 167 packages in 23s

72 packages are looking for funding
  run `npm fund` for details

found 0 vulnerabilities
Enter fullscreen mode Exit fullscreen mode

166 packages, twice that of vite, but also no vulnerabilities.

Takes a lot more space on the disc though:

du -hc -s  node_modules
204M    node_modules
Enter fullscreen mode Exit fullscreen mode

It seems to be quite slow, to be honest:

./node_modules/.bin/parcel public/index.html
Server running at http://localhost:1234
✨ Built in 976ms
Enter fullscreen mode Exit fullscreen mode
Thread Thread
 
joelbonetr profile image
JoelBonetR πŸ₯‡

Thanks for the info! 😁

It's slower the first time always (or when you add new things that require new deps) as it needs to check your project thingies to install other dependencies, it should be faster in subsequent builds, also the top benefit is on config (or the lack of it)

Collapse
 
ivan_jrmc profile image
Ivan Jeremic

I replaced CRA with Vite long time ago never looked back.

Collapse
 
congquyen99 profile image
congquyen99

yep, CRA is sucks

Collapse
 
kenramiscal1106 profile image
Ken Daniele Ramiscal

I like this article.

Tools are evolving and we need to switch gears to other tools like vite or nextjs to solve some moder problems

Collapse
 
hoanghelios profile image
Hoang Dao

I tried Vite recently and then no more CRA =))

Collapse
 
jmalvarez profile image
José Miguel Álvarez Vañó

Thanks for the post, it is very interesting!

Collapse
 
rajeshkhadka profile image
Rajesh khadka

Sure, In my recent project CRA gave me 72 vulnerabilities

Collapse
 
evillord666 profile image
Ushakov Michael

I am finding very versatile to create bundle js and css files that code be used without npm, just include them 8n app layout is vite do the same? Personally I prefer to use gulp for this, if you would like to see an example, i'll paste here link to one of a github project

Collapse
 
valeriavg profile image
Valeria

While vite or the underlying rollup would do what you described, esbuild, as described in the end of the article, might be a better option. It can be used as an executable or via JavaScript or Go API.

Collapse
 
evillord666 profile image
Ushakov Michael

Thanks for replying and interesting article

Collapse
 
pengeszikra profile image
Peter Vivo • Edited

Vite can be help to remove vulnerability from legacy project which contains react 15 - 16 - 17 modules to upgrade react 18?
My node_modules around 1Gb.

Collapse
 
valeriavg profile image
Valeria

Vite doesn't remove vulnerabilities. It has less dependencies of its own, so replacing tooling of a legacy project with vite will help, but only to a certain extent.
Unfortunately, refactoring an old project is a tedious and manual task.