React without webpack: fast path to a working app from scratch
Many people use Webpack because it's popular. create-react-app makes it even easier. But what if you prefer zero configuration? How minimal can your configuration for a React app get? This post contains my two favorite recipes for prototyping and shipping production React apps.
Truly single page application
These days I start every React project (including DataStation) the same way: a single HTML file.
First import React from a CDN.
index.html<script src="https://unpkg.com/react@17/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom@17/umd/react-dom.development.js"></script>
Then import Babel so we can use JSX.
index.html<script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>
Make a basic app with a few components:
index.html<div id="root"></div>
<script type="text/babel">
function Header() {
return (
<header>
<div className="container">
My great app
</div>
</header>
);
}
function Body() {
return (
<main>
<div className="container">
<h1>Home</h1>
<p>This is one great app.</p>
</div>
</main>
);
}
function App() {
return (
<div>
<Header />
<Body />
</div>
);
}
ReactDOM.render(<App />, document.getElementById('root'));
</script>
Add some styles:
index.html<style>
body {
margin: 0;
padding: 0;
}
.container {
width: 1200px;
max-width: 100%;
margin: 0 auto;
}
header {
background: black;
color: white;
padding: 15px 0;
}
</style>
Put it all together:
index.html<script src="https://unpkg.com/react@17/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom@17/umd/react-dom.development.js"></script>
<script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>
<style>
body {
margin: 0;
padding: 0;
}
.container {
width: 1200px;
max-width: 100%;
margin: 0 auto;
}
header {
background: black;
color: white;
padding: 15px 0;
}
</style>
<div id="root"></div>
<script type="text/babel">
function Header() {
return (
<header>
<div className="container">
My great app
</div>
</header>
);
}
function Body() {
return (
<main>
<div className="container">
<h1>Home</h1>
<p>This is one great app.</p>
</div>
</main>
);
}
function App() {
return (
<div>
<Header />
<Body />
</div>
);
}
ReactDOM.render(<App />, document.getElementById('root'));
</script>
I was able to build a fairly complex but working prototype of DataStation using this single file approach. It is very simple.
Downsides and alternatives
There are a few downsides to this approach. Breaking out this file
into separate CSS with link
tags and multiple
JavaScript with or without esmodules is easy enough. But all
non-esmodule libraries will be in the global window
namespace. And it's easier to manage dependencies
through package.json
rather than
in index.html
referencing a CDN URL.
Recently there have been a number of projects that have tried to solve the complexity of Webpack by providing zero configuration. Parcel and esbuild are two I've used. Esbuild is one of the fastest bundlers today, and is the one I now default to.
Esbuild
All esbuild does is bundle and "browserify" all JavaScript files
imported by some entrypoint script. So in this form we'll have three
separate parts of the project: a 1) index.html
file
that references a 2) CSS file and a 3) single JavaScript file.
The CSS file is easy:
style.cssbody {
margin: 0;
padding: 0;
}
.container {
width: 1200px;
max-width: 100%;
margin: 0 auto;
}
header {
background: black;
color: white;
padding: 15px 0;
}
The HTML entrypoint will no longer need to include React or Babel
since esbuild will handle that when imported from our JavaScript
file. All it needs to do now is contain the "root" div
and reference the CSS and JavaScript files:
<link rel="stylesheet" type="text/css" href="/style.css">
<div id="root"></div>
<script src="/app.js"></script>
Now we can break the React code up into three separate
files: index.jsx
, body.jsx
,
and header.jsx
. And we can switch to
using import
to include React.
import React from 'react';
export function Header() {
return (
<header>
<div className="container">
My great app
</div>
</header>
);
}
body.jsx
import React from 'react';
export function Body() {
return (
<main>
<div className="container">
<h1>Home</h1>
<p>This is one great app.</p>
</div>
</main>
);
}
index.jsx
import React from 'react';
import ReactDOM from 'react-dom';
import { Header } from './header';
import { Body } from './body';
function App() {
return (
<div>
<Header />
<Body />
</div>
);
}
ReactDOM.render(<App />, document.getElementById('root'));
And that's it! Now we need to set up package.json
with
all the necessary packages.
$ yarn init
$ yarn add --dev esbuild
$ yarn add react react-dom
Calling esbuild
will make the bundle.
$ yarn esbuild --bundle index.jsx --outfile=build/app.js && cp index.html style.css build
And then using a simple HTTP server will make all of the site accessible.
$ python3 -m http.server --directory build
And you're done! If you want to make it even more developer-friendly you can install fswatch like we do in DataStation development.
Share
New post is up on building React apps without Webpack, a fast path and a good path from scratch!#javascript #webpack #reactjs #esbuildhttps://t.co/eXNEMTU0b4 pic.twitter.com/wgCQZVGkDk
— DataStation (@multiprocessio) July 8, 2021
With questions, criticism or ideas, email or Tweet me.
Also, check out DataStation and dsq.