I miss Svelte-like composable components in Rails.
That is, composable components that:
- Look and behave like HTML in templates, not ERB (e.g.
<_component />
, not<%= render: component
%>) - 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.
- 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.