Using React with importmaps on Rails 7

I think it was @ekohl who shared 2 weeks ago on IRC this link Rails 7 will have three great answers to JavaScript in 2021+ from DHH about new approaches to JS in Rails 7.

Today, I found this tutorial made by DHH on how to use React with rails, without webpack by using the new import maps with htm which basically gives the browser the ability to read ES6 and React’s JSX.

If we choose this path, there will no longer be big chunks of JS build, but each file will be served through the assets pipeline.

What I’m not sure about is how would our engine plugins work with that,
and how would our build and packaging process would be affected.

This is great:

3 Likes

I did indeed link the blog post, but the video does demonstrate that it looks so much easier. Thanks for that!

Perhaps this should be the goal instead of upgrading webpack from version 3 to 5.

Excellent question. I do think foreman-js might become obsolete, in favor of including certain parts in Foreman core. Since there is no transpilation going on, I think everything would live in the global namespace and plugins might be able to “just use” what core provides.

Today we’re on Rails 6.0 (people are working on Rails 6.1/6.2), but it would be very interesting to see how much a very minimal proof of concept using Foreman and Rails 7 without webpack would bring. If we can even just show the login screen (which is React rendered), that could give us some idea.

@ezr-ondrej since you’re working on the Rails migration, do you have any idea how feasible it would be to run on Rails 7 in a development setup?

I also lean to this option… I believe we should go with the rails approach instead of reinventing ourselves in this case.

Could be, though we could also import the modules via cdn in import maps and it might still make things easier to package

Sounds good, though I would like to see also how plugins can use it and build the React part in production.

We will still need to ship the modules somehow because we do support offline setups which can’t access the internet. However, it will be interesting to see how can most efficiently deal with this.

1 Like

I do not know about anything blocking 7.0, once we finish the Zeitwerk work. I’ll try it out once we get 6.1/2 working and Zeitwerk enabled.

1 Like

That’s interesting. Would that mean we’d have to ship the whole node_modules directory? Does that mean we could patch JS files on production like setups?

It should take some investigation. What it relies on is having ECMAScript Modules. Webpack has traditionally been used because browsers didn’t implement this, but by now we can rely on browsers to do so.

Looking at importmap-rails it looks like you can ship a node_modules directory and patch the JS files. It all becomes plain file serving again instead of transpilation.

This may have serious consequences for how we ship things though. Needing a complete node_modules directory can be quite large. On the other hand, if we don’t need any development tooling (babel, webpack, etc) it may be much smaller than what we have today.

Another thing that would be interesting to see is wether we can avoid the duplication of node_modules across plugins that makes their 1.5GB * plugins amount which can easily become 10GB… based on what we saw in @Shimon_Shtein container POC, please correct me if I am wrong…
Maybe if it’s part of the assets pipeline it will solve the duplication.

Also we don’t want to load huge bundle files, but if foreman-js vendor will just store all the supported libs versions and on run time it will just add lib per html head script, e.g <script …react.js><script… patternfly.js> then I guess it’d be a good solution.

And this is how it works with engine plugins:

Just in case we won’t be able to use import-maps due to things that must be compiled, e.g: Scss
DHH shows another supported solution with esbuild:

I think esbuild might be a really cool option to get away from webpack.

But if I understand the two videos above correctly, import maps alone would require us all (foreman, katello, and all other plugins) to restructure almost every component, switch to CSS from scss, and would likely require a ton of other random replace/rewrites of imported component libraries.

Perhaps you could have esbuild only compile the scss to css, and have import-maps handle react, but even then the work involved would largely be a restructure of all react components.

No small amount of work.

I’m also curious to see if the whole thing would explode when you attempt to render a Patternfly component. :smile:

I’m definitely on board for faster compilation and the centralization of modules (and deleting foreman-js :bomb::fire:).

My only concern is how possible is this really?

If the problem is node modules and not wanting to repeat them in each plugin, have we considered using something like single-spa or other micro-frontend infrastructures with import maps within the parent?

I’ve done that before and although the parent had a bit of a bulky node_modules file, all child apps were fed from there, meaning a single node_modules to rule them all. Micro-frontends also support parent state much in the same way we have our redux stores passed around today.

The benefit of that path would be that the majority of the front-end codebase could be left alone, it would just be creating a new framework to patch things into.

TLDR: If you are planning on a rewrite of all our front-end applications, we should probably also consider some more proven web technologies no?

Thanks for taking the time to watch the videos!

I have the same concerns, as for the scss - looks like there’s a way to handle that on Rails assets pipeline: The Asset Pipeline — Ruby on Rails Guides

as for the React render/returned JSX that needs to be wrapped by htm function,
I was thinking that we could add a centralized render wrapper in core that would take care of it,
but then it is still a lot of work to update each and every component we use, and probably means we can’t import directly from Patternfly(?)
but maybe we can use a modified version of React that parse the JSX as a htm string,
not sure if that would work well, but I am sure it’s a very ugly solution.
so, in this case, we could use the other suggestion of esbuild.

I also suggested it in the past, but from my understanding, the architecture of Foreman and plugins is a bit more complex, here is one of the main discussions: An Independent UI for Foreman and Plugins - Discussion and Investigation

There is an option to deal with JSX also in the assets pipeline as implemented in GitHub - reactjs/react-rails: Integrate React.js with Rails views and controllers, the asset pipeline, or webpacker.

Also: GitHub - babel/ruby-babel-transpiler: Ruby Babel is a bridge to the JS Babel transpiler.

Meanwhile, I started this repo as a POC to test if it fits our needs: GitHub - Ron-Lavi/react-rails-importmaps-example

currently, it just has a ticking clock that uses React without transpilation :slight_smile:

Screenshot (45)

after 3.2 is in I will have more time to play with it and add an engine plugin to it.

Reviving an old thread. I came across this:

Additionally, I do have a very early PR that upgrades to Rails 7:

While it’s not ready at all, it does boot and serve basic pages. This can be used to play with importmaps and other technologies for those interested.

1 Like