Setting up Rails 7 for TypeScript and React
Rails 7 has been released recently, introducing quite a significant front-end mechanics revamp. Webpacker has been retired, and DHH now makes many compelling arguments in favor of a no-Node approach.
Well, maybe it'll get us somewhere someday, but at the moment, almost all of us prefer our TypeScript and/or JSX compiled to continue delivering quality stuff.
If you're not ready to embark on this delightful "no-transpiling" journey quite yet, there's a plan B.
In this article, we'll discuss how to set up Rails 7 for TypeScript and React, and we'll use the magnificent esbuild builder for that (Yay!).
Installation
The good news is that esbuild does all the heavy lifting for us. TypeScript and JSX are supported out of the box. All we need to do is employ the jsbundling-rails gem.
We can install it along with a new rails app (using the -j esbuild
flag), or if upgrading:
- By adding
jsbundling-rails
gem in Gemfile and runningbundle install
- And then running
./bin/rails javascript:install:esbuild
The last command will install esbuild, and add this command into package.json
:
// package.json
"scripts": {
"build": "esbuild app/javascript/application.tsx --bundle --sourcemap --outdir=app/assets/builds"
}
It's very straightforward. It takes the files from the app/javascript
directory and then compiles them into app/assets/builds
where they get picked up by the asset pipeline.
There's also the Procfile.dev
file, which is used to run several commands at once (using foreman). Just remember to start your app with ./bin/dev
instead of the ./bin/rails server
. It will run both rails server and the esbuild.
Now rename application.js
into application.tsx
, put some JSX into it, and you're done.
// app/javascript/entrypoint.tsx
import * as React from 'react'
import * as ReactDOM from 'react-dom'
const App = () => {
return (<div>Hello, Rails 7!</div>)
}
document.addEventListener('DOMContentLoaded', () => {
const rootEl = document.getElementById('app')
ReactDOM.render(<AppWithState />, rootEl)
})
Include the file into the view with javascript_include_tag 'application'
(no more javascript_pack_tag
!).
Super-simple, and it's also super fast thanks to esbuild.
Getting TypeScript errors
esbuild
compiling is unbelievably fast, but it doesn't do any type-checking. We can still run it separately from the command line with something along these lines:
tsc --project tsconfig.json --noEmit
I prefer adding that into project.json
script and into the Procfile.dev
file.
// package.json
"scripts": {
"build": "esbuild app/javascript/application.tsx --bundle --sourcemap --outdir=app/assets/builds"
"check-types": "tsc --project tsconfig.json --noEmit --watch --preserveWatchOutput"
}
Note the last flag --preserveWatchOutput
. It prevents tsc
from automatically clearing the screen and blowing up the whole foreman output.
Then add it to the Procfile.dev
:
types: yarn ts-check
Next steps
This setup is what works for me at the moment. I imagine, in the meantime, more tools and gems emerge, providing better Rails integration, but so far, I'm pretty pleased with the ability to use esbuild.
This setup is what works for me right now. I imagine, in the meantime more tools and gems emerge providing better Rails intergration, but so far I'm pretty pleased with ability to use esbuild.
-
Read this blog post for live-reloading.