This article is an attempt to put together everything available at the moment and see if it is really possible to implement production ready React-based website without pre-compilation like Webpack/Parcel or at least to have as less pre-compilation as possible.
It is possible to serve React project with minimal compilation of JSX without using bundlers like Webpack or Parcel.
Bundlers pack code into chunks, which has drawbacks: chunks have to be small enough to deliver only what's needed and big enough to make better use of HTTP1.1 connections. Finding the right balance may be tricky and involves some automatic and manual tuning. In addition it slows down the build and dev mode.
We will use Service Workers to do all the work in runtime and some Babel for production build.
Everything described in the article is highly experimental and I am cutting corners everywhere. By no means I recommend to use any of that in production :)
Among other important things from the articles, these ones will be crucial in order to make React app work:
Package specifier imports support (or import maps): when we import react in reality we should import something like
UMD support: React is still distributed as UMD and so far there's still an ongoing discussion how to publish it using ES modules
Let's solve these issues one by one.
First things first, let's assume the project will have the following structure:
obviously a place where we will install all dependencies
dir with and service scripts
app source code
Package specifier imports support
Let's install the shim and React:
In order to launch the app we can do something like this in:
Where inwe will have:
Does it work? Of course, no. Even though we've successfully imported everything.
Let's move on to the next challenge.
The above mentioned article led me to an idea that we can use Service Workers to intercept and pre-process network requests. Let's create the main endpoint, which will bootstrap the SW and App and use it instead of the App directly ( ):
And then let's create the Service Worker ():
Here's what we've done here:
We have created the export map, which associates package id with global var name
We have created atag in with contents of UMD-packaged script
We've exported the mapped global var as default export of module
Now let's adjust theto use the bootstrap entry point:
Now we're able to import React and React DOM.
It's worth to mention, that there's also another way. We can install ESM distributable:
And then use following map:
But unfortunately this project is quite stale, latest iswhereas React is .
There are two ways to do the JSX compilation. We can either go traditional way and use Babel to pre-compile or we can use it in runtime. Of course for production it would make much more sense to pre-compile, development mode can be more brutal. Since we already use Service Worker let's enhance it.
Let's install a special Babel package that can do it:
Now let's add following to the Service Worker ():
Here we've used same approach to intercept the network request and respond with slightly different content, in this case we use Babel to transform the original response. Please note that plugin for dynamic import has a different name, not a usual due to Standalone usage.
In the above mentioned article author used text transformation, here we will go a bit further and inject the CSS in the page. For that we again will use the Service Worker ():
Et voila! If you now open thein the browser you'll see the buttons. Make sure the proper Service Worker is being picked up, if you're not sure, open Dev Tools, go to tab and section, everything and reload the page.
More production friendly case
The above mentioned code works fine for dev mode, but realistically we don't want all of the app users to compile the code in their browsers, it's impractical. So let's push things a bit further and make the minimalistic production mode.
In order to do that we will create one more index filewith following content:
As you see, no shims here, we will use different technique to do rewrites. Since we still have to use Babel to compile JSX we can also rewrite module paths there instead of usingfor the shim. Let's install everything:
Let's add the scripts section to:
Next let's add:
Keep in mind that this file will be used only for production, for dev we configure Babel in the Service Worker.
Also let's add production mode support to Service Worker:
And use the condition in:
Now let's add a build scriptwhich will copy everything needed to dir:
We do this to makeleaner by skipping build dependencies.
Now if you openyou will see the same output as for but this time browser won't build Babel, it will use pre-built files.
As you see, the solution now has duplicates, like, the section in and list of files to be copied in . For demo purposes it's fine but for real usage it would be better to automate this.
HTTP2 should take care of those small source files sent over the network, hopefully.