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