Svelte-like composable components in Rails

I miss Svelte-like composable components in Rails.

That is, composable components that:

  1. Look and behave like HTML in templates, not ERB (e.g. <_component />, not <%= render: component %>)
  2. Keep component specific Ruby, JS, CSS and HTML in the same file, or if there is too much information for one file, in the same folder.
  3. On render, scope all JS, CSS and HTML seamlessly

Essentially replacing or living alongside .erb, with a new file extension.

It’s not as important to me that they are any more testable than partials are today. My problem is not one of a huge team needing to keep things separate and testable (which is the problem React and ViewComponents seem to solve), but of a one-man team wanting code to be pretty, enjoyable to write, and readable.

As far as I can tell, view components don’t look and behave like HTML in templates, they still look like ERB, and, as they solve for a different problem, look like a lot more work to write than partials.

In essence I’d love to just open up a file or folder like this:

   app/view/components/_header.html.rorc # rorc = ruby on rails component
<ruby>
      # Ruby goes here, used both for defining required and optional inputs for DX, and any backend logic that is specific to the component
   required_child :title
</ruby>

<style>
  /* Scoped CSS goes here, all identifiers scoped on render */

   h1 { color: red }
</style>


<h1><%= title %></h1>

which is used like this:


<_header>Title goes here</_header>

Or for a more advanced example:

   app/view/components/_expandable_header.html.rorc

(The code block is so large that you have to scroll it to see the template rendering. IRL you might want to split it into different files):

<ruby>
   optional_attribute :user
   optional_slot :text

   def header_text 
      "Hello %{user.name}: %{title}" if user

      "Hello,  world: %{title}"
   end 
</ruby>

<script>
   // Scoped JS goes here, function names scoped on render:
   function expand_text() {
     // Something
  }
</script>

<style>
  h1 {
     color: red;
  }
</style>

<!-- Embedding ruby same as ERB: -->
<h1><%= header_text %></h1>
<% if body do %>
  <button onclick="expand_text">Expand</button>
  <div id="expandable">
      <%= text %>
  </div>
<% end %>

and then use it like this:


<_expandable_header>Title goes here</_expandable_header>

or, more advanced:

<!-- % used before attributes to indicate they are to be parsed as ruby -->
<_expandable_header %user="user">
   Title goes here

   <slot:text>
       <p>Some text goes here</p>
   </slot:text>
</_expandable_header>

Is this insane? Is anyone else working in this direction?

I love Rails, and if I manage to squeeze the time I’d love to learn everything I need to contribute something like this as a Gem.

1 Like

The first thing which jumps out at me with this approach is… how do you test the various components?

I think all you would need is a function for rendering that can be passed to, and then just test them like you would ViewComponents?

I would imagine the “ruby” part creates a object with the name of the file and Component on the end, so HeaderComponent, that can be called with .new, like ViewComponents do.

So

class HeaderTest 
   test "renders heading" do 
       render_inline(HeaderComponent.new(title: "Test")

      assert_selector "h1", text: "Test"
   end
end

Or something to that effect

1 Like

You’re not crazy! I also really want this feature. I managed to get Phlex working in my Rails app, but I’m still struggling to set it up so that I can have the component specific CSS and JS in the component folder and to scope these to the component as well, like with CSS Modules. But Phlex is still pretty new, and kinda hard for me to understand and get it to cooperate with the rest of the app.

My goal is to build a set of reusable components that I can just copy and paste into any site I’m building. They will come with their own Stimulus controllers and styles. This would speed up development soooo much.

I would also like to be able to define a theme in one yaml file and have all the components look there for their look and functionality. Then creating a brand new site would only be a matter of picking out components and creating a new theme based on the closest one in my library to what the current project needs.

I’m pretty new to the game but this sounds smart AF to me. Def not insane. Especially for getting consistent yet composable outputs out of LLMs. Holler at me if you build a library or you have some components you want people to try out in a different environment.

If your interested in this sort of stuff check out Marco Roth’s recent talk at Rails World. Although it’s primarily focused on building a better ERB engine that’s just the initial goal. He hints at larger goals this is working towards such as reactive server-rendered templates (similar to Phoenix LiveView), scoped CSS (similar to Vue) and many of the things talked about in this thread in his new project ReActionView. It’s interesting stuff.