Preact on Rails with Webpack
by Nick Gauthier on
I needed to brush up my front-end skills, since I’ve been in Gopherland for a couple years now, so I decided to write a few tutorials about using Rails with modern front-end frameworks. I’m learning this as I go, so please feel free to comment or message me on Twitter if you have some tips about the setup.
In this article, we’re going to walk through how to set up Rails 5.1 with Webpack to use Preact as a front-end framework. Rails 5.1 is the first version of Rails to support Webpack as part of the application scaffolding process, which makes it easy (well … easier) to get started with.
Preact is an alternate implementation of React using native dom features and with a focus on being small and light and lower in dependencies. I like the idea of Preact because it seems more approachable because of its smaller source code. I know I can just dig through it if I encounter something unexpected. I mean, look at its source code (seriously, open that link, that’s Preact without ES6 and JSX).
Prerequisites
In order to run through this tutorial, you’ll need:
- Ruby >2.2.2 (I’m using 2.4.0)
- Rails >5.1 (I’m using 5.1.4.rc1)
- NodeJS >6 (I’m using 6.11.2)
- Yarn >0.20.1 (I’m using 0.27.5)
Creating a new Rails App with Webpack
The first thing we need to do is make a new Rails app with Webpack. We’re also going to remove all the things we won’t need, since we’ll be using Preact. Here’s how we set up our application:
rails new hello \ --skip-action-cable \ --skip-sprockets \ --skip-coffee \ --skip-javascript \ --skip-turbolinks \ --webpack
We’re going to skip action cable, sprockets, coffeescript, rails javascript helpers, and turbolinks. And we’re going to turn on Webpack.
Using Webpacker @ master
Heads up! There’s currently a bug with automatic compilation so we’re going to use Webpack @ master. Once a release > 2.0.0 of rails/webpacker
is released, you should be able to use that instead of this step.
Edit our Gemfile, and modify the webpacker
line to use master from GitHub:
gem 'webpacker', github: 'rails/webpacker'
Then update it:
bundle update webpacker
As of the writing of this post, I’m on master@691389f
.
Finally, reinstall webpacker:
rails webpacker:install
(you can answer a
to allow all updates)
Trimming the Fat
Webpacker’s install is great because it brings in everything you might need. But, we can trim down a bunch of its dependencies since we will just be using Preact. First off, lets remove some vestigal asset pipeline folders:
rm -rf app/assets/ lib/assets/
Also, edit app/views/layouts/application.html.erb and remove the stylesheet tag and replace it with a webpack tag:
<!DOCTYPE html> <html> <head> <title>Hello</title> <%= csrf_meta_tags %> <%= javascript_pack_tag 'application' %> </head> <body> <%= yield %> </body> </html>
Next, let’s check out our package.json
:
{ "name": "hello", "private": true, "dependencies": { "autoprefixer": "^7.1.3", "babel-core": "^6.26.0", "babel-loader": "7.x", "babel-plugin-syntax-dynamic-import": "^6.18.0", "babel-plugin-transform-class-properties": "^6.24.1", "babel-plugin-transform-object-rest-spread": "^6.26.0", "babel-polyfill": "^6.26.0", "babel-preset-env": "^1.6.0", "coffee-loader": "^0.8.0", "coffee-script": "^1.12.7", "compression-webpack-plugin": "^1.0.0", "css-loader": "^0.28.5", "extract-text-webpack-plugin": "^3.0.0", "file-loader": "^0.11.2", "glob": "^7.1.2", "js-yaml": "^3.9.1", "node-sass": "^4.5.3", "path-complete-extname": "^0.1.0", "postcss-cssnext": "^3.0.2", "postcss-loader": "^2.0.6", "postcss-smart-import": "^0.7.5", "precss": "^2.0.0", "rails-erb-loader": "^5.2.1", "resolve-url-loader": "^2.1.0", "sass-loader": "^6.0.6", "style-loader": "^0.18.2", "webpack": "^3.5.5", "webpack-manifest-plugin": "^1.3.1", "webpack-merge": "^4.1.0" }, "devDependencies": { "webpack-dev-server": "^2.7.1" } }
Wow, that’s a lot of stuff! We can remove the coffeescript packages since we’ll be using ES6:
yarn remove coffee-loader coffee-script
We can also remove the rails-erb-loader since we won’t be using any erb templates in our javascript:
yarn remove rails-erb-loader
Now we can remove those loaders from webpack:
rm config/webpack/loaders/coffee.js config/webpack/loaders/erb.js
And in config/webpacker.yml
we can remove the following extensions:
- .coffee
- .erb
- .ts
- .vue
Just to make sure you didn’t break anything, you can run:
./bin/webpack
to bundle up your assets. Mine looks like this:
Hash: 77d3b57fb7806bb9009c Version: webpack 3.5.5 Time: 262ms Asset Size Chunks Chunk Names application-8f8bdd9f4ff51391ec46.js 4.47 kB 0 [emitted] application manifest.json 68 bytes [emitted] [0] ./app/javascript/packs/application.js 515 bytes {0} [built]
Installing Preact
Installing Preact is as easy as:
$ yarn add preact yarn add v0.27.5 [1/4] Resolving packages... [2/4] Fetching packages... [3/4] Linking dependencies... [4/4] Building fresh packages... success Saved lockfile. success Saved 1 new dependency. └─ preact@8.2.5 Done in 3.96s.
Of course, that doesn’t do anything to our app, because we’re not using Preact anywhere yet. In order to try it out, we’re going to need a page to work with in our app. I’m going to be lazy and just add a view to the application controller.
In config/routes.rb
:
Rails.application.routes.draw do root to: 'application#index' end
$ mkdir app/views/application $ touch app/views/application/index.html.erb
Now we can run rails server
and it should just be a blank screen, but in the console, we should see:
Hello World from Webpacker
Yay! Just for a sanity check, edit app/javascript/packs/application.js
and edit the message and refresh your
browser and you should see a new message (because Rails should recompile your assets for you with webpacker).
Additionally, we’re going to use JSX, so we need to install the babel JSX transformer:
yarn add babel-plugin-transform-react-jsx
And in order to map this transformer to Preact’s h
method, add this line inside plugins
in your .babelrc
:
["transform-react-jsx", { "pragma":"h" }]
Preact Hello World
Now it’s time to actually use Preact (finally, whew). Edit app/javascript/packs/application.js
and add this line:
import('hello.js')
That’s going to import app/javascript/hello.js
(which we are about to write) into your app.
Here’s what we put into app/javascript/hello.js
(which is straight from Preact’s tutorial):
import { h, render, Component } from 'preact'; render(( <div id="foo"> <span>Hello, world!</span> <button onClick={ e => alert("hi!") }>Click Me</button> </div> ), document.body);
Now, we should be able to refresh our site, and see the Preact demo. Click the button and it says “Hi!”. We did it.
Wrapping up
One of the things I was interested in is page weight. When running our app in dev mode, I saw two JS files, one is application.js, which contains a bunch of webpack code for dynamically loading all our JS files separately. This is great in development because we only have to rebuild what we change. So that’s why there’s a second “chunk” file also being loaded.
Doing a production asset build is as easy as:
RAILS_ENV=production ./bin/webpack
Then run our dev server again:
rails server
As long as you don’t touch any JS files our production built JS files are used. From here I was able to see that our application.js was just shy of 1kB, and our chunk with hello.js and preact in it are 3.7KB. Great! So we’re under 5KB, which is awesome.
I’m not sure if it’s possible to have just one application.js file (no chunks) so if you know how, let me know!
blog comments powered by Disqus