Settings

Theme

Rux: A JSX-inspired way to render view components in Ruby

github.com

135 points by kalaracey 3 years ago · 41 comments

Reader

bradgessler 3 years ago

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.

  • ravagat 3 years ago

    +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

psadauskas 3 years ago

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
  • petepete 3 years ago

    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...

    • caseyf 3 years ago

      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...

    • andrei_says_ 3 years ago

      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 ;)

      • petepete 3 years ago

        > 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.

    • zem 3 years ago

      interesting approach, thanks for sharing it!

  • camertron 3 years ago

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

vlunkr 3 years ago

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.

  • joeldrapper 3 years ago

    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.

  • FigurativeVoid 3 years ago

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

throwaway09432 3 years ago

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

kalaraceyOP 3 years ago

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).

compumike 3 years ago

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.

  • camertron 3 years ago

    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.

kayodelycaon 3 years ago

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.
  • ImaCake 3 years ago

    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.

hhthrowaway1230 3 years ago

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')

        ])

      ])

    );
~~~
  • sarahdellysse 3 years ago

    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
metapsj 3 years ago

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.

hokumguru 3 years ago

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.

Alifatisk 3 years ago

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?

jack_riminton 3 years ago

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!

  • __ryan__ 3 years ago

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

    • camertron 3 years ago

      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?

xanderatallah 3 years ago

Does something like this exist for python?

  • ivoflipse 3 years ago

    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
  • est 3 years ago
vdfs 3 years ago

Full circle complete? back at PHP early days

  • Kerrick 3 years ago

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

Keyboard Shortcuts

j
Next item
k
Previous item
o / Enter
Open selected item
?
Show this help
Esc
Close modal / clear selection