bin/rails g scaffold oh my what is this basic garbage in my views. Oh my indeed. What a simplistic, unstyled hot mess. Perhaps you stopped using this because it’s so far-fetched from the rest of your application’s style. Or maybe you use it, and then spend three hours adding your 300 tailwind classes to each file.

I really like the value scaffold generators provide. Controller, routes, tests… and the views. The views, while helpful as placeholders, are as basic as can be. As your app’s design starts to come together, the erb generators lose their muscles.

A few years back I started taking the time to customize the scaffold generators. But it’s one of those things that happens rarely so I always have to look up “Where are the source files located?”, “Where do they go?”.

They are in railties/lib/rails/generators/erb/scaffold/templates/, and they go in lib/templates/erb/scaffold/.

“One of these days I oughta add a task that copies the files out of the Rails gem for me.” It turns out, there’s already task for that.

bin/rails app:templates:copy

Run that, and Rails’ erb templates (and a few others) will join your party. Lo, stay your willpower, these small files are beasts.

Thor heard you like erb so he put erb in your erb so you can erb with your generated generators.

They are .tt Thor Template files, which contain two layers of ERB. The first ERB layer reflects upon your generator’s options, and then there’s an escaped ERB layer that gets printed into the file. You know, typical <%= @hotdogs.each do |hotdog| %> kinda stuff.

Reflect upon your template

Make me some hotdogs…

rails g scaffold hotdogs yay

The index template:

<div id="<%= plural_table_name %>">
  <%% @<%= plural_table_name %>.each do |<%= singular_table_name %>| %>
    <%%= render <%= singular_table_name %> %>

The resulting index:

<div id="hotdogs">
  <% @hotdogs.each do |hotdog| %>
    <%= render hotdog %>

Notice how the template file uses <%% for escaped erb tags that should stick around into the final template.

It can be visually difficult to edit these files. I’ve found Claude can do an okay job assisting you, and ChatGPT generally does not. I recommend giving it a shot though. Ask Claude to look at an existing view you like and update your .tt.

Testing the output

Once you have these templates in your project directory, you can run a more limited version of the scaffold command to test them. First, make sure you commit everything, as you may very much wish to start over.

You can run this to regenerate your Hotdog views:

rails g erb:scaffold hotdogs yay --force

You don’t need the --force command, but very quickly you will begin to appreciate it. The goal is to make a view file you don’t need to edit after scaffolding it. It’s probably going to take a few tries.

Now that your generators make these delightful, handcrafted artisan views, you may want to rerun the scaffold generators for your existing models. Perhaps you are adding an attribute to a model, and wish the view would take care of itself. Just rerun the scaffold generator with the attributes you wish your views knew about. Sweet.

A Bulma Example

So, what might this look with Bulma? Here’s a snippet of a scaffold template I worked on earlier today. In the example below, I removed a conditional related to “is this a password field” or “attachment field”. The files you copied will have those and you should keep the conditional.

<% attributes.each do |attribute| -%>
  <div class="field">
    <%%= form.label :<%= attribute.column_name %>, class: "label" %>
    <div class="control">
      <%%= form.<%= attribute.field_type %> :<%= attribute.column_name %>, class: "input" %>
    </div>
    <p class="help"></p>
  </div>
<% end -%>
  <div>
    <%%= form.submit class: "button is-primary" %>
  </div>
<%% end %>

We wrapped each attribute with the field class, and each control with .control to create consistent spacing. We added the label and input classes as appropriate, since Bulma makes us repeat ourselves. The whole point of these scaffold generators is to make a great starting point, so even though we can’t assume a good help text, let’s at least drop the placeholder there so we don’t have to look anything up. It’s hidden if empty, and can be deleted if we must. Finally, we’ll style the submit button with the usual Bulma goods, .button and .is-primary.

This translates to the following:

  <div class="field">
    <%= form.label :yay, class: "label" %>
    <div class="control">
      <%= form.text_field :yay, class: "input" %>
    </div>
    <p class="help"></p>
  </div>

  <div>
    <%= form.submit class: "button is-primary" %>
  </div>

Wash through the index, partial, and form scaffold templates, and you never scoff at the basic generators again!