Hacker News new | past | comments | ask | show | jobs | submit login
Rux: A JSX-inspired way to render view components in Ruby (github.com/camertron)
135 points by kalaracey on March 6, 2023 | hide | past | favorite | 40 comments



The view stack in Rails is one of the creakiest part of the stack. A lot of people don't realize it, but the way partials in Rails leaks the state of the parent renderer is the source of a lot of bugs and code organization issues.

It's great to see more attention coming to it. Some other libraries worth keeping an eye on:

https://www.phlex.fun - Joel shipped this a few months ago ... it's very similar to Rux except it uses Ruby classes to achieve a similar effect. What's particularly interesting about Joel's efforts is that he is very worried about speed and performance, so it's a very fast templating framework.

https://youtu.be/9-rqBLjr5Eo?t=560 - This is the best overview of what Phoenix is shipping for their HTML framework, which is called HeeX (most of the docs already assume you're "in the know" with Elixir). HeeX is nice (like Rux) in that you can use HTML tags to embed server-side rendered components in markup with tags like `<.icon/>`. Not sure about Rux, but HeeX can reason about HTML from within itself, which means it can validate the DOM and make technologies like LiveView possible (https://hexdocs.pm/phoenix_live_view/Phoenix.LiveView.html).

I'm hoping at some point in the future a "de facto" (or actual) standard for Rails view components come into being so we can have an HTML-aware templating layer in Rails and improve the ecosystem through more standard UI components.


+1 , I came to that conclusion on the view stack being one of the leakiest if not the leakiest part of the Rails stack after developing saas with heavy SPA-like tendencies. Good to see I am not alone, thanks for sharing the links too


This is pretty cool, I like the idea of using inline templates in ViewComponent. I wonder how this would look using something like Markaby[1], so the templating stayed pure ruby, instead of having to be passed through a transpiler...

[1]: https://github.com/markaby/markaby

Modifying their own example, I kinda like it.

    class GreetingComponent
      def call
        div {
          "Hey There" +
            NameComponent(first_name: "Homer", last_name: "Simpson")
        }
      end
    end


I maintain a collection of view components[0] and lots are built without a template, just using the Rails tag helpers directly[1]. They're easy to write, familiar to any Rails dev and very fast.

[0] https://govuk-components.netlify.app/

[1] https://github.com/DFE-Digital/govuk-components/blob/main/ap...


I do this as well. Our design system in-progress has 32 components and only 3 are rendered with templates.

They are fast and easy to write and easy to modify for sure. I wouldn't mind having the code look more like the output (without a template) but it's not something I've been missing...


Am I understanding correctly that there’s a significant difference in performance between using a ViewComponent + a partial vs. a ViewComponent which renders html via a tag - from inside the component?

Don’t partials get compiled when used from a ViewComponent?

Also - the gov.uk project makes my heart sing ;)


> Am I understanding correctly that there’s a significant difference in performance between using a ViewComponent + a partial vs. a ViewComponent which renders html via a tag - from inside the component?

I don't think there will be much difference at all in everyday use, but some libraries that value performance don't avoid templates for that reason, Pagy for example.

https://github.com/ddnexus/pagy

Personally I omit them in my projects whenever we want to customise attributes, I hate seeing stuff like this in templates:

    <h2 class="<%= heading_classes %>" id="<%= heading_id %>">Some header</h2>
I'd much rather see:

    <%= tag.h2("Some header", class: heading_classes, id: heading_id) %>
And as lots of the components allow attributes to be customised, using the tag helpers directly seems like a good choice.

> Also - the gov.uk project makes my heart sing ;)

Thanks :) Although I just reimplement the components and forms in Rails, the proper hard work is done upstream.


interesting approach, thanks for sharing it!


You might want to take a look at Phlex, which essentially has the same syntax: https://github.com/joeldrapper/phlex


I didn't know about ViewComponent until now, it looks more appealing to me than Rux. Dealing with transpilers is a necessary evil in JS land, I really don't want to bring that headache into my Ruby code.


You can avoid transpiling if you write views in pure Ruby instead. Ruby syntax has everything you need to concisely represent all valid HTML — method calls are tags, keyword arguments are attributes and blocks are content.


ViewComponent is really cool, but still a little fresh. I'm excited to see how they update it in the coming year.


ViewComponent is years old, in large production deployments, and very stable.


We use it extensively at GitHub and consider it very stable.


Personally I prefer haml, in my opinion JSX is grotesque.


I'm surprised no one has yet done React SSR in Rails using Graal (using the polyglot bridge between Rails running on Truffleruby and React on Graal.js).


Doesn't sound too surprising


Does this support HAML-style syntax? We're 100% HAML-only for templating, whether normal Rails views or ViewComponent... https://github.com/haml/haml https://haml.info/ so going back to writing HTML or ERB feels like a huge downgrade.


No, rux doesn't support HAML, since HAML is an entirely different "language" than rux. You'd have to figure out how to identify HAML embedded in Ruby and parse it with the HAML parser. Entirely possible, but not something rux will (probably) ever do.


Rails has had Builder::XmlMarkup for a long time. It's been extracted into a gem: https://www.rubydoc.info/gems/builder/Builder/XmlMarkup

You could easily write a method to do this:

    def html(&blk)
      io = StringIO.new()
      builder = Builder::XmlMarkup.new(:target => io, :indent => 2)
      blk.call(builder)
      io.to_s
    end

    html do |t|
      t.span "#{@first_name} #{@last_name}"
    end
Not the prettiest, but it doesn't require additional gems or a new syntax highlighter and linter.


Surely rux would cover more edge cases than your function? A lot of people would see this function and struggle to understand what it is doing, but if you explicitly use something like rux it's much more obvious. I guess rux would have more overhead than this though.


Kind of interesting,i would like it if it had a cleaner fallback to a non JSX version. If you one day decide to not use rux

~~~ h('div.example', [

        h('h1#heading', 'This is hyperscript'),

        h('h2', 'creating React.js markup'),

        h(AnotherComponent, {foo: 'bar'}, [

          h('li', [

            h('a', {href: 'http://whatever.com'}, 'One list item')

          ]),

          h('li', 'Another list item')

        ])

      ])

    );
~~~


even simpler, I have always wished that JSX/JSX-alikes would serialize to something like a tuple like `(tag: string | Component, attributes: Record, children: List)`, so your example would start looking something like this:

    <div class="example">
      <h1 id="heading">This is an example</h1>
      <h2>creating React markup</h2>
      <AnotherComponent foo="bar">
        <li>
          <a href="http://whatever.com">One list item</a>
        </li>
        <li>
          Another list item <hr />
        </li>
      </AnotherComponent>
    </div>
would translate to something like this:

    ["div", {"class": "example"}, [
      ["h1", {"id": "heading"}, ["This is an example"]],
      ["h2", {}, ["creating React markup"]],
      [AnotherComponent, {"foo": "bar"}, [
        ["li", {}, [
          ["a", {"href": "http://whatever.com"}, ["One list item"]],
        ],
        ["li", {}, [
          "Another list item",
          ["hr", {}, []],
        ],
      ],
    ]
and that all Rux/JSX/hyperscript/whatever would compile down to that


Ruby has blocks, so in Phlex, you’d write it like this using methods and keyword arguments.

  div(class: "example") do
    h1(id: "heading") { "This is an example" }
    h2 { "creating React markup" }
    AnotherComponent(foo: "bar") do
      li { a(href: "http://whatever.com") { "One list item" } }
      li do
        text " Another list item"
        hr
      end
    end
  end


You’re halfway to Clojure’s hiccup syntax[1] there.

[1]: https://github.com/weavejester/hiccup/blob/master/doc/syntax...


"JSX-inspired" caught me off gaurd, the only thing JSX could inspire me to do was build something so I never had to touch it again, I landed on something similar to what you have here, I call it "Elementary"

Here's the JSON for a landing page written in my syntax: https://github.com/jazzyjackson/lookalive/blob/master/docs/i...

In another project I use it extensively to mix javascript in with the object syntax as you would with JSX: https://github.com/lookalive-software/geodesy/blob/newfocus/...

The implementation is fairly simple, given JSON returns an HTML string.

https://github.com/lookalive-software/elementary/blob/master...


here's a couple more to throw into the mix.

Hypertext allows you to write HTML from Ruby. https://github.com/soveran/hypertext

rbexy - A Ruby template language inspired by JSX https://github.com/patbenatar/rbexy

imho, hypertext has a great deal of potential especially the DSL.


I've been building a Javascript server that implements Hotwire/Turbo lately powered by JSX templating (not React!) and minimal client-side JS. Its a wonderful dev experience but this might make me port those ideas back to Ruby. The rest of Rails is just too good to pass up.


The html inside the call method looks cool but I have hard time understanding to how the syntax highlighting would react to seeing html inside the ruby code? Would it highlight it correctly?


There's a VSCode extension for highlighting rux (Ruby + HTML): https://marketplace.visualstudio.com/items?itemName=camertro...


Thx


I wish the syntax was more like erb i.e. <%= %> instead of { } although I'm sure someone will explain to me why they can't!


I’m sure they could, I imagine they’re appealing to those who prefer JSX’s syntax.


Yeah that's right, I was trying to simulate JSX syntax. I'm not against ERB syntax tho, maybe the lib could eventually support both?


yeah doing a call_html_erb method would be fantastic


Does something like this exist for python?


You could implement a HTML renderer with Collagraph (https://github.com/fork-tongue/collagraph).

From the README: Write your Python interfaces in a declarative manner with plain render functions, component classes or even single-file components using Vue-like syntax, but with Python!

  - Reactivity (made possible by leveraging observ)
  - Function components
  - Class components with local state and life-cycle methods/hooks
  - Single-file components with Vue-like syntax (.cgx files)
  - Custom renderers



Full circle complete? back at PHP early days


I'm under the impression erb is much more like PHP. This is almost the inverse of erb, in a way.




Consider applying for YC's Spring batch! Applications are open till Feb 11.

Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: