Summarized using AI

Introducing ReActionView: An ActionView-Compatible ERB Engine

Marco Roth • September 05, 2025 • Amsterdam, Netherlands • Talk

Introducing ReActionView: Modernizing the Rails View Layer

This talk, delivered by Marco Roth at Rails World 2025, presents the debut of ReActionView, a new ActionView-compatible ERB engine built upon the previously introduced Herb ecosystem. The presentation recaps the journey from introducing the Herb parser, through building and shipping developer tools for the Rails view layer, to launching a new, modern rendering engine for ERB templates.

Main Topic

ReActionView is an ERB engine compatible with .html.erb files, but with advanced HTML validation, better developer tooling, improved error messaging, and the potential for reactive updates—addressing longstanding shortcomings in Rails' view rendering experience.

Key Points

  • Motivation and Background:

    • Ongoing feedback from the community highlighted a lack of tooling and feedback for Rails view development, especially with hotwire and embedded Ruby helpers.
    • Prior ecosystems and tools (like stimulus LSP) provided limited support, mainly for static HTML, not fully supporting ERB tag helpers.
  • Herb Ecosystem Overview:

    • Herb is introduced as an extensible, HTML-aware ERB parser, linter, formatter, and tooling foundation.
    • Written in C for performance and portability, with bindings for Ruby, JavaScript, and browser environments.
    • Delivers a comprehensive syntax tree, recognizing the structure of interleaved HTML and Ruby within templates.
    • Supports advanced developer workflows—automatic formatting, linting (including new accessibility rules), Tailwind class sorting, and more.
  • Building a Modern Rendering Engine:

    • Traditional Rails ERB rendering is string-based and not HTML-aware, allowing malformed HTML and vague errors.
    • By leveraging the Herb parser, ReActionView transforms rendering into an HTML template engine, enforcing validity and providing pinpointed error feedback directly in editors and browsers.
    • Maintains API and interface compatibility with the standard ERB engine, enabling drop-in replacement and seamless Rails integration.
    • Demonstrated via migration example, showing instant benefits (syntax-awareness, better error messages, consistent output).
  • Tooling and Developer Experience Enhancements:

    • Errors in templates are highlighted with specificity, both in the command-line interface and directly in browser renderings.
    • New debugging features include visual outlines of rendered view, partial, and component boundaries, clickable annotations, and instant navigation to corresponding source files.
    • Features tailored for dynamic content identification, aiding complex template inspection and development.
  • Vision for the Future:

    • Outlines upcoming adoption levels for ReActionView and encourages immediate experimentation via a simple Rails integration.
    • Future plans include optimizations (e.g., inlining partials), server-client component bridging, integration with third-party JS frameworks, scoped CSS, selective rendering, and further embracing web platform advancements.
    • Emphasizes a vision where Rails remains productive and relevant by adopting modern frontend paradigms while retaining accessibility and stability.

Conclusion / Takeaways

  • The introduction of ReActionView marks a significant step in enhancing the Rails view layer with better feedback, HTML correctness, and integrated developer tooling.
  • Herb and its tooling ecosystem are now production-ready for experimentation and feedback, with paths toward deeper framework integration.
  • The talk advocates continued exploration and innovation to ensure Rails keeps pace with modern web development, empowering the Ruby community to build robust, maintainable, and accessible applications.

Introducing ReActionView: An ActionView-Compatible ERB Engine
Marco Roth • Amsterdam, Netherlands • Talk

Date: September 05, 2025
Published: Mon, 15 Sep 2025 00:00:00 +0000
Announced: Mon, 04 Aug 2025 00:00:00 +0000

This talk is the conclusion of a journey I've been sharing throughout 2025. At RubyKaigi, I introduced Herb: a new HTML-aware ERB parser and tooling ecosystem. At RailsConf, I released developer tools built on Herb, including a formatter, linter, and language server, alongside a vision for modernizing and improving the Rails view layer. At Rails World, I'll debut ReActionView: a new ERB engine built on Herb, fully compatible with `.html.erb` but with HTML validation, better error feedback, reactive updates, and built-in tooling.

Rails World 2025

00:00:06.879 All right, good afternoon.
00:00:10.000 First up, I want to give a big thanks
00:00:12.080 and for being able to be here on stage
00:00:15.280 to Zavi and Amanda for trusting me to be
00:00:17.600 here today. And yeah, with that out of
00:00:20.320 the way, I am Marov. I'm a fullstack
00:00:22.800 developer and open source contributor.
00:00:24.960 And I love open source. I love to give
00:00:27.920 gifts. I love to work on hotwire and I
00:00:31.679 have been working on adjacent projects
00:00:33.760 like stimulus reflex and cape ready over
00:00:35.520 the years and I've been on and off
00:00:37.360 maintaining stimulus not as much anymore
00:00:39.600 in the last few years though but I love
00:00:42.960 Ruby and I want to keep Ruby and Rails
00:00:45.440 as structive as possible for people to
00:00:47.440 get started with
00:00:49.680 as I've been working with hotwire over
00:00:51.360 the years I have been teaching at boot
00:00:53.760 camps have been mentoring early career
00:00:55.760 engineers and also been helping
00:00:57.920 businesses get hotware adopted in their
00:01:00.000 applications
00:01:01.520 and there has been a lot of feedback in
00:01:03.600 regards of lack of feedback and lack of
00:01:06.080 tooling and that's also why here on this
00:01:08.960 stage two years ago I was talking about
00:01:10.720 the ecosystem at large but it was also
00:01:13.360 releasing the um stimulus LSP which
00:01:17.119 helps people to
00:01:19.280 find errors in their view templates
00:01:21.200 especially when it comes to undefined
00:01:23.439 controllers but also helping you with
00:01:26.159 getting um autocomplete for these data
00:01:28.720 action attributes. The problem was that
00:01:31.360 this is only HTML. It wouldn't work for
00:01:34.799 ERB in the sense that it wouldn't work
00:01:37.680 for action view tag helpers or the ERB
00:01:40.479 helpers you might have in your
00:01:41.600 application.
00:01:43.200 So what works on these kind of templates
00:01:45.360 it wouldn't work on those kinds of
00:01:47.439 templates where you have tag help tag
00:01:50.079 diff content tag and so on. So we were
00:01:53.119 missing the support for these tag
00:01:55.920 helpers. there was no tooling and we
00:01:58.880 also kind of outlined this in the talk
00:02:01.200 and I had this point on the um slides
00:02:05.119 where we were saying we want to add a
00:02:07.200 triple SP as well but also make sure
00:02:09.200 that as soon as Ruby's LSP kind of
00:02:11.280 supports that as well.
00:02:13.680 So in these few cases that I had and a
00:02:16.160 few other ones kind of led me to think
00:02:18.080 about maybe we need a better ERB parser
00:02:20.720 that also is HML aware.
00:02:23.440 So I want to tackle the bigger picture
00:02:25.840 and that's what I was able to do um in
00:02:28.400 the Rubik Kai talk early this year in
00:02:30.640 Japan. I wanted to kind of show what we
00:02:33.840 can do with a yearb parser and then
00:02:36.080 later at ris confil this year as well I
00:02:38.239 kind of showed what we can do with this
00:02:39.680 now in in terms of tooling and today I
00:02:42.400 want to talk about how we can use this
00:02:44.000 to render ERB as well. So going back to
00:02:46.959 Ruby Kai I introduced Herb. Herb is a
00:02:51.120 HTML where ERB parsed ecosystem
00:02:53.519 specifically built for HTML and ERB
00:02:56.160 files.
00:02:57.680 It's an ecosystem of tools. It's mostly
00:03:00.160 set out as a um parser, llinter and
00:03:04.080 formatter. And as we have been building
00:03:06.159 this, we have been adding more and more
00:03:07.920 tools to kind of helps um support this
00:03:10.560 vision. The most important piece is the
00:03:13.280 um parser
00:03:15.680 which um I have been talking about in
00:03:18.239 the talk itself. But to kind of recap
00:03:20.640 from that talk, it is a HTML ERB parser.
00:03:23.760 It's written in C. It's built on Prism
00:03:27.280 for parsing the ERB in the Ruby Ruby in
00:03:30.720 the ERB and is designed for tooling. So
00:03:34.400 what it gives you is an interleaf syntax
00:03:36.080 tree of HTML and embedded Ruby. So we
00:03:38.799 have structural awareness of the files.
00:03:41.840 I like to kind of show this example here
00:03:43.680 where we have this if and some HTML in
00:03:47.280 the middle. What the regular ERB parser
00:03:50.400 would see in this case would be the ERB
00:03:52.400 tag, some text and then some ERB tag
00:03:55.040 again. But it wouldn't kind of connect
00:03:57.120 the dots that this is a combined uh
00:04:00.879 statement. So the if is being closed
00:04:02.799 with that ends at the end. And that's
00:04:05.120 what ear uh Herb allows us to kind of
00:04:07.519 capture. It has the information about
00:04:10.799 the opening tag, the closing tag at the
00:04:13.920 end. So we can kind of enclose these
00:04:16.079 elements inside of it. And the HTML
00:04:19.359 element itself is contained within it.
00:04:22.320 And with that we have the hierarchy and
00:04:23.919 everything we need to kind of build out
00:04:25.360 advanced tooling.
00:04:27.360 So that was the parser. It's running um
00:04:30.000 is built in C. So it's running in the
00:04:31.680 browser. It's running in Ruby for a C
00:04:33.680 extension. It's running um in Node.js
00:04:36.560 and JavaScript. So, it's really kind of
00:04:38.160 versatile of how you might be able to
00:04:39.759 use this. So, at Railscon, I was talking
00:04:42.960 about HTML template in general, but also
00:04:46.479 the vision that we could now build
00:04:48.080 having these tools available.
00:04:50.320 And this doesn't come without issues
00:04:52.160 like the the Rails um view layer in
00:04:54.639 general. we have been seeing a lot of
00:04:56.400 friction in these kind of areas in the
00:04:58.320 view layer especially around like
00:05:00.560 reusability but also the complexity in
00:05:03.360 front end logic or also around the
00:05:06.160 tooling.
00:05:08.000 So I kind of showed in that key in that
00:05:10.240 talk that um we have a new herb
00:05:12.720 formatter now which is a HTML ERB
00:05:15.759 formatter. So what you can do with this
00:05:18.080 is you can give it any string of HTML
00:05:20.639 EB, save your file or run the CLI and it
00:05:24.880 will formats the code for you
00:05:26.560 automatically.
00:05:28.400 And this is built in into the editor. So
00:05:30.800 you can save the file and it will just
00:05:32.400 snap right in as it should be since it's
00:05:35.280 aware of all the surroundings. It's
00:05:37.120 super accurate and super fast.
00:05:40.479 Then we have the llinter which helps you
00:05:42.639 kind of catch common mistakes kind of
00:05:44.720 enforce best practices. So you can see
00:05:47.360 that um these tools just work with HML
00:05:50.240 EV as well.
00:05:52.320 Recently we also shipped a new kind of
00:05:55.120 set of rules that is aware of these
00:05:57.520 conditions. So it is able to detect if
00:06:00.160 you are maybe rendering a ID twice on a
00:06:02.960 page which is not technically valid
00:06:04.880 HTML.
00:06:06.880 These um offenses are also shown in the
00:06:08.639 CLI. If you run it, they are nicely
00:06:10.960 annotated with syntax highlighting and
00:06:12.720 it's super easy to pinpoint what's wrong
00:06:14.400 about it. Since Rails Conf, we shipped
00:06:17.919 18 new lint rules, nine of which are
00:06:21.039 focused on accessibility,
00:06:23.039 which makes now a total of 32 rules that
00:06:25.520 the lint supports.
00:06:28.479 Then in the same kind of um realm, we
00:06:31.280 have now a stimulus lint package too,
00:06:32.960 which is specifically for stimulus
00:06:34.560 rules, which we are shipping today. So
00:06:38.240 the stimulus LSP had these diagnostics
00:06:40.080 built in into the language server but
00:06:41.919 they weren't really accessible outside
00:06:43.360 of it and stimulus link extracts those
00:06:45.440 now using herp and can now um um run
00:06:49.600 these um diagnostics u independently
00:06:54.560 it's built using the seamless parser
00:06:56.319 project and is now using herb um going
00:06:58.880 forward so we are going to have a lsp
00:07:02.319 update very soon so you can also get um
00:07:06.240 annotation on ERB tag helpers.
00:07:11.360 Similarly, we might want to explore
00:07:13.039 what's possible to have turbulent rules
00:07:15.280 if that makes sense. Not sure if that
00:07:17.120 there's too much to get there, but it's
00:07:19.599 something to explore.
00:07:21.680 Another thing that's quite common in the
00:07:23.039 front end ecosystem is the um use of
00:07:25.120 Tailwind obviously, but they have this
00:07:27.199 prettier plug-in for automatically
00:07:28.639 sorting the classes.
00:07:30.880 So, I've been working on one that
00:07:32.319 integrates with the herb formatter as
00:07:33.840 well. So when you add a tailwind class
00:07:36.160 to your HTML element and save the file,
00:07:39.280 it will sort them according to the order
00:07:41.280 that they recommend in the docs.
00:07:44.800 This will also work for both year bit
00:07:47.039 helpers and regular HTML which is super
00:07:48.880 convenient.
00:07:51.360 That's coming also very soon. I hope to
00:07:53.440 ship this within the next month.
00:07:56.560 Then
00:07:58.560 we have the vision that I was sharing at
00:08:00.879 Railscon for the EB rendering engine.
00:08:03.680 The idea here is that we want to take
00:08:05.280 the current stringbased template engine
00:08:07.840 and transform it into a HTML templating
00:08:10.560 engine.
00:08:12.319 What it means is that we want to have an
00:08:13.759 engine that cannot produce invalid HTML
00:08:16.000 because it just enforces it. So if you
00:08:18.960 have regular ERB today and you give it
00:08:21.280 this kind of template here where you
00:08:22.639 have a missing end tag at the ends and
00:08:25.360 you ask action view today to compile
00:08:28.000 this using the templates it will do that
00:08:30.960 for you because it hasn't any
00:08:33.120 understanding of um ERB and HTML HTML I
00:08:36.880 mean what we could do with a engine that
00:08:40.000 is aware of it we could say that if you
00:08:42.560 give it the same templates and we try to
00:08:44.320 compile this using this time the herp
00:08:46.480 handler that it would kind of um throw
00:08:49.680 and say I cannot compile this because
00:08:51.279 it's not valid HTML.
00:08:53.839 That's what I want to talk about today.
00:08:55.360 How we can kind of implement this to
00:08:56.880 make this work.
00:08:59.440 So the rest view layer has been pretty
00:09:03.519 much the same since it was released in
00:09:05.920 2004.
00:09:07.680 It hasn't really changed too much. It's
00:09:10.720 still
00:09:12.399 somewhat the same, which is not a bad
00:09:14.560 thing. It's really good that we have
00:09:15.680 this consistent um thing you can rely
00:09:18.080 on. Back then it launched as part of
00:09:20.720 action pack. So we kind of completed the
00:09:22.320 MVC story in Rails. And even back then
00:09:25.200 it was using ERB or embedded Ruby for
00:09:28.000 short.
00:09:30.160 But even then it was templates and text
00:09:32.480 files with sprinkles of Ruby embeddings.
00:09:35.440 And today action view ships with EUBI as
00:09:40.000 the engine. And ERB is here to stay is
00:09:43.519 not going anywhere. and so is action
00:09:45.519 view. But maybe we can kind of look what
00:09:47.760 we can do to make it better kind of how
00:09:50.399 we can level it up by keeping it railsy.
00:09:53.600 So I want to kind of check what we can
00:09:55.120 do with action view if we kind of
00:09:56.720 reimagine how it works. Maybe try to
00:10:00.080 engine re-engineer some of the parts to
00:10:01.839 make it more um flexible and more like
00:10:05.120 refactor some of the stuff maybe and
00:10:07.279 optimize some things. But also let's see
00:10:09.519 if we can make it somehow reactive. So
00:10:12.160 you need less and less JavaScript
00:10:15.120 and that's kind of the idea of this
00:10:16.640 reaction view initiative. Um the idea
00:10:19.760 here is that we have an initiative to
00:10:21.519 explore what we can do in the view layer
00:10:23.839 uh in this year and beyond.
00:10:27.519 So the design goals are to be backwards
00:10:29.440 compatible. Obviously we want to improve
00:10:31.279 the developer experience. We want to
00:10:32.800 embrace web standards and see if you can
00:10:35.200 integrate with other frameworks too if
00:10:36.959 possible. Over the last few years, we
00:10:40.160 have seen a lot of improvements of the
00:10:41.600 web platform in general, including web
00:10:44.079 components,
00:10:45.680 import maps or also um PWAs.
00:10:49.519 In CSS, we have been seeing a lot of
00:10:52.000 awesome improvements too like container
00:10:53.839 queries, CSS nesting, and now even
00:10:56.720 functions.
00:10:58.560 In the web APIs, we have the new view
00:11:01.440 API, the popovers, and even now the
00:11:04.079 super powerful invocal commands.
00:11:07.519 And if you look today how we have a
00:11:09.920 service rendered app on the left hand
00:11:12.240 side with rails and some HTML over the
00:11:15.360 wire compared to a single a client side
00:11:17.360 rendering app with a rails API and a
00:11:19.360 single page app they are very far apart
00:11:22.399 there's no like middle ground and people
00:11:25.040 choose to use client side rendering if
00:11:27.040 they want high fidelity if they want to
00:11:28.800 reuse or use existing component
00:11:30.800 libraries or even have a bunch of
00:11:32.560 engineers already
00:11:35.360 is great it is simple it gets the job
00:11:37.920 done in most of the cases, but if you
00:11:40.560 want to have something more ambitious,
00:11:42.079 it's super hard to go from hot wire to
00:11:44.640 fully um um high dynamic. So, the
00:11:48.160 transition is really hard. There's not
00:11:49.680 really an on-ramp to go from the left
00:11:52.079 side to the right side. And that's why
00:11:54.480 some teams today begin new projects
00:11:57.200 directly in spas because they don't feel
00:11:59.760 like that action is powerful enough. And
00:12:02.640 that's kind of a mistake in my opinion
00:12:04.480 because I think that's giving away most
00:12:06.240 of what makes Rails great or so
00:12:08.320 productive.
00:12:10.320 So I want to kind of check out what we
00:12:12.079 can do to close the gap to make sure
00:12:13.519 it's coming closer together. I want to
00:12:15.920 see how where we can go with this. So I
00:12:18.800 have a new release today which is Herps
00:12:21.040 07
00:12:23.040 and Herp.
00:12:31.680 This new version of Herb ships a new ERB
00:12:34.320 engine.
00:12:36.320 This ERB engine is HTMLware and it can
00:12:39.519 render ERB. It is designed to be ERB
00:12:42.800 engine compatible. So you can use the
00:12:44.560 same interface to call ERUBY. So let's
00:12:47.519 compare them.
00:12:49.360 If you use ER by engine today and give
00:12:51.279 it a template, this time we have an
00:12:53.279 actual closing tag. What it will use is
00:12:55.920 a reax to figure out the dynamic pieces
00:12:58.560 of that template. So we have a string,
00:13:00.720 we have Ruby and another string. And it
00:13:03.760 transforms this to call add text add
00:13:06.079 expression for these individual pieces.
00:13:09.440 And what this gives us is a compile
00:13:10.959 template. In this case, it's a little
00:13:12.560 bit of a format and simplified, but we
00:13:14.800 have a buffer and we kind of shuffle
00:13:16.959 strings into it or Ruby um method um
00:13:21.360 returns into the buffer
00:13:24.480 with Herb. If we now have this
00:13:26.800 templates, the same templates, we can
00:13:28.880 now parse this templates and we get a
00:13:31.600 syntax tree of that same template. I
00:13:34.000 will compact this a little bit so we can
00:13:35.600 see it better. We have a HTML element
00:13:37.920 nodes. We have opening nodes. We have
00:13:41.760 the body of the elements and some
00:13:43.360 closing a close tag.
00:13:45.600 Now we can strategically visit these
00:13:47.680 this tree to kind of visit all these
00:13:49.600 nodes and we can do this using a herb
00:13:51.839 visitor
00:13:53.440 to kind of build a compiler to compile
00:13:55.600 this syntax tree into a compile
00:13:57.680 template.
00:13:59.600 So if you implement these methods in
00:14:01.680 this case we have the element node here.
00:14:03.680 In this case you just want to visit all
00:14:05.760 these individual nodes. The element node
00:14:08.800 itself doesn't have any content but it
00:14:10.399 holds the references to these other
00:14:12.160 nodes. So we have the opening tag and in
00:14:16.079 here we can say let's visit all these um
00:14:18.720 tokens that we have and take the values
00:14:21.040 of those and call add text on them.
00:14:24.560 For the text nodes it's just text
00:14:26.720 content. So we can just call add text
00:14:28.399 for that too. And for the ERB tags, we
00:14:32.160 can check if the tag opening is a output
00:14:34.079 tag. And if it is, call add expression
00:14:36.639 otherwise call add code. And if you kind
00:14:39.920 of compact this back out out of this um
00:14:42.000 visitor, that's what it's going to call
00:14:43.839 in sequence. And if you compare this to
00:14:46.320 what Ruby does, it's not quite the same.
00:14:48.880 It's more verbose, right?
00:14:51.519 But if you kind of change what add text
00:14:53.360 and add expression is under the hood, if
00:14:55.199 you kind of collect them in an array
00:14:57.279 first and kind of collect all of those,
00:15:00.320 we get something like this where we have
00:15:01.839 the tokens and the type that they have
00:15:03.600 inside of them. If you look at those
00:15:06.160 now, we see we have a bunch of
00:15:07.360 consecutive text tokens. So we can kind
00:15:10.240 of before we compile the templates,
00:15:11.839 compact them into one individual token
00:15:15.920 and then we have something very similar
00:15:17.440 to EUBI. And if you now try to compile
00:15:21.920 this again, it will produce pretty much
00:15:24.560 the same output.
00:15:27.279 So if you handle all these different
00:15:28.720 node types that herb has, we end up with
00:15:31.600 almost the same compile templates.
00:15:34.480 So what we can do now with this, we can
00:15:36.320 plot this into rails and change the
00:15:38.079 default from erupy to herb just for
00:15:40.320 testing purposes.
00:15:42.160 Since this is API compatible, we can in
00:15:45.120 action view change the Ruby engine
00:15:47.279 handler to use herb and then on the
00:15:50.959 handler itself all the loads also herb
00:15:53.759 and then change the defaults from er to
00:15:57.199 um herb as well. So if you run our
00:16:00.480 application now and open it in the
00:16:02.320 browser, it renders.
00:16:05.360 So this is now all running on the Herp
00:16:07.920 parser, the Herb compiler and renders
00:16:10.079 this using Herp, which is super
00:16:11.839 exciting.
00:16:20.079 The cool thing here is that it gives us
00:16:21.680 all the advantages that her parser gives
00:16:23.279 us, which means we don't are able we're
00:16:25.920 not able to produce invalid HTML. So we
00:16:28.480 ensure we have valid HTML and valid
00:16:30.480 syntax and change it from being a string
00:16:32.959 template engine to be a HTML templating
00:16:35.040 language engine.
00:16:37.199 And this gives us valuable feedback also
00:16:39.279 in our editor now because what's running
00:16:41.680 in the editor here is the same parser
00:16:44.160 engine that's actually rendering um and
00:16:46.560 compiling the template at the end. So
00:16:49.279 this is very much um accurate, more
00:16:52.320 accurate than what we would have if
00:16:53.839 that's not the case,
00:16:55.920 which also makes it more instant because
00:16:57.440 you can just run it in your editor and
00:16:59.519 see that something is wrong about it
00:17:00.880 without even trying to open it in the
00:17:02.399 browser and see something is wrong.
00:17:05.199 But since we have all this understanding
00:17:06.720 now, we can do a lot of cool things with
00:17:08.559 that. So let's improve how Rails renders
00:17:11.520 error messages.
00:17:13.520 Today if you kind of have an error in
00:17:15.280 your syntax in your syntax error in your
00:17:17.120 templates it'll just give you the whole
00:17:18.880 template and saying something is wrong
00:17:20.480 in here figure out where and that's not
00:17:23.120 really helpful. So let's see how we can
00:17:24.880 do that and prove that the her parcel
00:17:27.679 knows where this is. It can pinpoint it
00:17:29.520 exactly and we already do this in the
00:17:31.840 llinter so we can see where something is
00:17:33.840 wrong about it. So let's bring this back
00:17:36.240 into the browser. And when the browser
00:17:38.400 is being opened and trying to request it
00:17:40.880 from the server, we now show exactly
00:17:43.760 where something is wrong in the
00:17:45.360 templates, which makes it very much easy
00:17:48.000 to debug um what's wrong about it.
00:17:57.440 And since we're dealing with HTML ERB,
00:17:59.280 we can also do more things with it. So
00:18:01.200 we can also check for invalid HTML
00:18:03.360 saying if you have nested P tags which
00:18:06.160 are not valid HTML we can add a warning
00:18:08.160 for that too. Since this is just a
00:18:10.400 warning we can dismiss those and if we
00:18:13.280 dismiss them we can still see the
00:18:14.559 rendered contents in the background.
00:18:17.919 Another nice thing about action view is
00:18:19.919 this config called annotate rendered to
00:18:21.679 view help view files names which adds
00:18:24.960 these comments at the top on the bottom
00:18:27.360 between the view files. The problem is
00:18:29.919 that we cannot really style these
00:18:31.520 elements or these comments in visually.
00:18:35.280 But our compiler already knows how to
00:18:37.120 render HM elements and attributes.
00:18:40.160 So what if we just inject additional
00:18:43.280 attributes in development? So instead of
00:18:46.160 rendering comments, we render data
00:18:48.240 attributes onto these elements since we
00:18:50.160 know where they are in the document. And
00:18:53.039 if we add some CSS to it using an
00:18:55.520 outline,
00:18:57.039 what we get is a very nice outline of
00:19:00.240 these view files to have the boundaries
00:19:02.720 where these elements are uh these
00:19:04.400 templates are rendered.
00:19:13.760 Since we have views, we have partial, we
00:19:15.440 have components, we can add a type to
00:19:17.679 that to kind of differentiate them. So
00:19:21.200 when you have a page with a lot of
00:19:22.559 components, you can see them color coded
00:19:25.840 for views partial and components.
00:19:29.440 If you now also kind of add the relative
00:19:31.760 path within the project, we can also say
00:19:34.320 on hover of on these labels, we show the
00:19:36.559 full path.
00:19:38.480 And if we add the full
00:19:41.440 path to that element too, we can click
00:19:44.720 on these labels and it will open it
00:19:46.480 right in your editor where this um view
00:19:49.039 is defined.
00:19:58.400 Since it's an outline and there are H1
00:20:00.640 elements and attributes, this doesn't
00:20:02.400 interrupt the natural flow. So it
00:20:04.240 shouldn't change any layout at all. This
00:20:07.600 is immensely helpful for debugging and
00:20:09.280 finding out where stuff is defined in
00:20:10.880 your app.
00:20:13.200 So can we do the same for ERB output
00:20:16.160 tags as well? And obviously the answer
00:20:18.160 is yes. So if you have dynamic content
00:20:21.600 on the page um you have this toggle now
00:20:23.760 on the right side. I will enlarge this
00:20:25.440 one more time. So what you can see here
00:20:27.760 you see the full outlines of all the
00:20:29.679 dynamic pieces on this page.
00:20:32.240 And this is really helpful to see what
00:20:34.080 is actually being rendered and how it's
00:20:35.360 being rendered.
00:20:37.200 We have another option for showing the
00:20:38.960 content even on hover. So if you hover
00:20:41.520 over these elements, you see what this
00:20:43.280 actually is being rendered as.
00:20:53.840 And you guessed it, you can also click
00:20:55.600 on them. And if you click on them, it
00:20:58.240 will open the file, the location and the
00:21:01.039 line right in your editor where this is
00:21:03.120 defined.
00:21:05.919 There's one more nice modes which is for
00:21:08.320 enabling all of them on the same page
00:21:09.840 which is not as helpful, but it's still
00:21:12.000 kind of interesting to see how a page is
00:21:13.919 kind of set up.
00:21:15.919 So what is next? We have outlined these
00:21:19.600 six adoption levels at the Rails conf
00:21:21.280 talk and what we are shipping today is
00:21:23.440 the first two levels of those. If you go
00:21:26.000 into your Rails app today, run bundle
00:21:28.080 app reaction view and run the generator,
00:21:31.280 it will generate a uh initializer
00:21:34.960 and if you say intercept ERP true and
00:21:37.919 run your servo again, you will get this
00:21:41.520 out of the box without any more
00:21:42.960 configuration and you have all these
00:21:44.720 tools built in.
00:21:46.880 And obviously this is just the
00:21:48.320 beginning. I think this gives us a good
00:21:50.640 idea what we can do. So just give it a
00:21:53.200 shot. See how it works in your apps and
00:21:55.039 please report anything that might uh
00:21:57.200 look weird.
00:21:59.679 Then the next point would be to look if
00:22:02.480 you can optimize stuff inside action
00:22:04.400 view itself now that we have all this
00:22:05.679 introspection. There are some ideas here
00:22:08.080 to inline partial render calls maybe
00:22:10.240 inline some action view tack helpers and
00:22:12.159 more but this needs more exploration and
00:22:14.640 I'm not sure if that's really viable.
00:22:17.679 The one more thing that we can do is
00:22:19.679 also try to take inspiration from Fenix
00:22:22.480 live view and they are heak templates.
00:22:25.120 If we know about these element um
00:22:27.200 templates now we can check out we can
00:22:29.679 track state and see before and after um
00:22:33.760 what changed and only render the
00:22:35.360 relevant pieces in the template. So you
00:22:37.280 don't have to render the whole template
00:22:38.320 every time.
00:22:40.320 The one more thing you can do here then
00:22:41.679 too is say if you limit this ERB you're
00:22:44.720 using in your view we might be able to
00:22:46.880 do something simil that react does from
00:22:48.799 bringing the client components to the
00:22:50.159 server side we can do the other way
00:22:52.320 around to say we can maybe bring some
00:22:54.240 ERB components to the client side maybe
00:22:57.039 by transpiling them to web components
00:22:58.960 transparent to react components or
00:23:00.720 something similar this should help with
00:23:03.760 optimistic UI updates and might also
00:23:06.400 improve offline functionality that you
00:23:08.240 also heard from most of the
00:23:10.880 And then the final step would maybe be
00:23:12.400 to see if you can integrate external
00:23:14.000 components too from third party
00:23:15.520 packages. If you don't want to abandon
00:23:17.520 HTML all together, but you only want to
00:23:20.480 have on certain pages um use react
00:23:22.880 components or swelled components or view
00:23:24.640 components.
00:23:26.799 The idea is here that we do something
00:23:28.400 similar than what import maps rails does
00:23:30.480 that we can say we register some of
00:23:32.480 these components from let's say chat cn
00:23:35.360 or from a folder in your application and
00:23:38.080 by doing that and declaring that the
00:23:39.760 reaction view context has idea about
00:23:41.840 these components and you can just use
00:23:43.840 them in your regular er views
00:23:47.039 the engine would know that these are
00:23:48.320 client side components would know how to
00:23:50.320 deal with them how to hydro them how to
00:23:52.159 pass props around and know how to deal
00:23:54.159 with them
00:23:56.320 This is tightly integrated. This would
00:23:58.000 be just working without any setup and
00:24:00.720 would kind of bridge the gap between
00:24:03.039 going fully server side or going fully
00:24:04.799 client side.
00:24:06.720 There are more ideas to explore. One of
00:24:08.640 them is one of the issues is that people
00:24:11.039 are saying I have a lot of one-off
00:24:12.720 components components and people are
00:24:15.120 often reaching for Alpine.js.
00:24:17.760 One thing I don't like about AlpineJS is
00:24:19.919 the inline JavaScript you have to do in
00:24:22.159 the attributes which is kind of weird to
00:24:24.480 me. So maybe you can explore something
00:24:27.039 with inline stimulus as well. I think
00:24:29.760 SWEL does this really nicely where they
00:24:31.600 have the script tags within the same
00:24:33.279 component that is only scoped to this
00:24:35.919 specific view file. So we might be able
00:24:38.400 to do something similar to that.
00:24:41.200 The same is true for CSS. So we might be
00:24:43.760 able to do scoped CSS per view file.
00:24:47.200 Vue.js actually does this. They have a
00:24:49.279 style scoped
00:24:51.600 and what they do is whenever they have
00:24:53.600 the style scoped, they take all these um
00:24:56.559 CSS rules and then transform them using
00:24:59.520 data attributes to kind of scope all
00:25:01.039 these rules to the specific components
00:25:02.480 so they don't leak in your whole app.
00:25:06.559 We can also do selective rendering. This
00:25:08.320 might be super useful for triple frames
00:25:10.240 to say if you have a full view and you
00:25:12.880 only say I want to render this ID
00:25:15.440 reviews piece of that view that you
00:25:17.440 don't have to render everything around
00:25:18.799 it because you can select specifically
00:25:20.799 that element.
00:25:25.039 But the idea here is just to bring more
00:25:26.400 of these modern front end ideas back to
00:25:28.240 Rails. So there isn't really a a need to
00:25:30.880 abandon the Rails view layer and keep it
00:25:32.880 Rails way. Action view has been really
00:25:35.679 stable. There weren't too many breaking
00:25:38.000 changes at all over the years and it
00:25:40.240 makes it super easy to build on. I think
00:25:42.960 Rails shouldn't discourage the use of
00:25:44.480 modern JavaScript. It should embrace it
00:25:48.480 and also embrace modern web standards.
00:25:50.159 We have been seeing a lot of
00:25:51.120 advancements in that area. So, it would
00:25:53.120 be super neat to just fit it in here.
00:25:56.080 Provide good defaults. That's what DRA
00:25:57.840 is known for. and have all these
00:25:59.440 built-in options and migration paths
00:26:01.200 that when you need more
00:26:03.679 so that the view layer scales the same
00:26:05.279 way as the rest of the framework does in
00:26:07.039 Rails like active record with multiple
00:26:09.039 databases
00:26:10.720 but again this is just vision talk but I
00:26:13.919 think if we as a framework and community
00:26:15.679 want to advance what we are doing we
00:26:17.039 need to explore what's possible and new
00:26:19.360 innovations require exploration
00:26:22.240 even if none of this is actually viable
00:26:23.760 in production I think the outcome of
00:26:25.679 this would be still valuable for the
00:26:27.520 tooling and the ecosystem at large. So
00:26:30.720 to conclude, the prism parser had a
00:26:33.840 really big effect on Ruby internals and
00:26:35.840 Ruby tooling and I think the same could
00:26:38.320 be true for herb and HML templating and
00:26:41.279 um tooling as well. Herb started as a
00:26:43.840 parser and is now an ecosystem of
00:26:46.480 valuable tools and reaction view started
00:26:49.760 to as a vision to kind of show some of
00:26:53.120 the shortcomings in the Rails view layer
00:26:54.880 and today it might just be a alternative
00:26:57.840 a rendering engine but believe it shows
00:27:01.200 what's possible that we have these tools
00:27:03.200 now and we can push more boundaries
00:27:07.039 to bring the web framework and the race
00:27:09.440 framework forward and that we can kind
00:27:11.360 of hold up with the mono JavaScript
00:27:12.799 stack so we don't have to write
00:27:14.080 JavaScript in the end
00:27:16.480 and the teams don't have to abandon the
00:27:18.240 beauty of action view.
00:27:21.200 The community, Ruby community in general
00:27:23.279 has built incredible tooling and I think
00:27:25.679 now it's time to bring the same care to
00:27:27.679 the view layer with fur and reaction
00:27:30.240 view. I believe we have a path forward
00:27:31.679 to level this up and in the end that's
00:27:34.240 why I'm here and that's why I love Ruby.
00:27:37.520 I believe that the herb engine provides
00:27:40.000 a lot of valuable um insights in what we
00:27:42.960 can do and I would love to see this in
00:27:45.760 some way or form making it upstream into
00:27:47.360 rails.
00:27:49.600 Rails has a super unique position as a
00:27:51.200 fullstack framework and I went to keep
00:27:53.360 it as attractive as possible so people
00:27:55.679 can enjoy building apps with rails.
00:27:59.440 Finally, I want to big give a big thanks
00:28:01.279 to all the people that have been
00:28:02.320 supporting this journey so far and all
00:28:04.960 the people that have been contributing
00:28:06.159 to the herb repo directly.
00:28:09.279 If you haven't already, please give her
00:28:11.200 a star on GitHub or check out the docs
00:28:15.120 and also this new open source library
00:28:17.279 called reaction view on GitHub and the
00:28:19.760 newly built um doc site for that as
00:28:22.159 well. So please try the tools, give
00:28:24.399 feedback, report issues and that's what
00:28:28.320 I have for Hey,
00:28:32.320 hey,
00:28:36.399 hey.
Explore all talks recorded at Rails World 2025
+19