To ViewComponent or not to ViewComponent
Github is one of the largest Ruby on Rails applications, with a complex infrastructure, backend, and frontend. The UI that powers Github is rich and handles complex inline interactions, so it’s natural that Github would create its own frontend framework that works with Rails. Enter the ViewComponent library.
ViewComponent is defined as:
A framework for creating reusable, testable & encapsulated view components, built to integrate seamlessly with Ruby on Rails.
To a seasoned Rails dev, this might trigger some questions, like doesn’t Rails already have a way to write reusable, testable views? Wouldn’t this be achieved using partials and view helpers? What is ViewComponent trying to achieve that Rails doesn’t already have baked in?
Maybe the answer in this part of the docs:
Think of ViewComponents as an evolution of the presenter pattern, inspired by React. A ViewComponent is a Ruby object and template
Hmmm… do we actually want to bring “React” into Ruby and Ruby on Rails. The great thing about the Ruby community is that we already have plenty of ways to do things the right way, so is this something born out of the hype behind frontend frameworks like React? Or is there really something worth exploring?
If we keep reading the docs, we land on something interesting:
Based on several benchmarks, ViewComponents are ~10x faster than partials in real-world use-cases.
The primary optimization is pre-compiling all ViewComponent templates at application boot, instead of at runtime like traditional Rails views.
Is this true? That might be a great value if that’s the case. Well, we can run the benchmark tests ourselves and validate that claim.
First, clone the repo:
git clone https://github.com/ViewComponent/view_component.git
then cd
into the directory and install the dependencies:
cd view_component
bundle
Note: the main
branch might have change that break the benchmark command. So you might want to use a specific branch or tag. At the time of writing this post, the most recent stable branchv2.82.0
.
Then run the rake command provided with the project:
rake partial_benchmark
To find out which rake
commands are available you can run rake -T
from inside the project.
The output we get is the following:
Warming up --------------------------------------
component 476.000 i/100ms
inline 565.000 i/100ms
partial 86.000 i/100ms
Calculating -------------------------------------
component 4.634k (± 2.9%) i/s - 46.648k in 10.074332s
inline 5.402k (± 3.8%) i/s - 54.240k in 10.055319s
partial 870.345 (± 2.8%) i/s - 8.772k in 10.087184s
Comparison:
inline: 5402.4 i/s
component: 4634.4 i/s - 1.17x (± 0.00) slower
partial: 870.3 i/s - 6.21x (± 0.00) slower
According to this, partials are 6.2x slower that inline ViewComponents. That is not 10x! But also, you’re more likely to use regular ViewComponents as opposed to inline ones. So how do partials compare to that?
You can try to use the numbers provided and calculate the difference, or more easily, just update the benchmark file to remove inline components from the comparison:
# performance/partial_benchmark.rb
# ...
Benchmark.ips do |x|
x.time = 10
x.warmup = 2
x.report("component") { controller_view.render(Performance::NameComponent.new(name: "Fox Mulder")) }
# x.report("inline") { controller_view.render(Performance::InlineComponent.new(name: "Fox Mulder")) }
x.report("partial") { controller_view.render("partial", name: "Fox Mulder") }
x.compare!
end
and then re-run the rake
task. The numbers I got are:
Warming up --------------------------------------
component 480.000 i/100ms
partial 83.000 i/100ms
Calculating -------------------------------------
component 4.665k (± 2.0%) i/s - 47.040k in 10.087090s
partial 870.022 (± 3.1%) i/s - 8.715k in 10.027468s
Comparison:
component: 4665.3 i/s
partial: 870.0 i/s - 5.36x (± 0.00) slower
So ViewComponents are actually 5x faster than partials. That’s half of 10x. But that’s still a significant speed improvement.
Is it possible that this improvement in speed comes at a cost? Perhaps in terms of memory, since ViewComponents are extra ruby classes and HTML templates that you wouldn’t have in a typical Rails application? That’s easy to test. We can improve the benchmarks to include memory usage.
Let’s add the benchmark-memory
gem as a new development dependency to the project, run bundle
to install it, and update the performance script, by adding the following:
require "benchmark/memory"
Benchmark.memory do |x|
x.report("component") { controller_view.render(Performance::NameComponent.new(name: "Fox Mulder")) }
x.report("partial") { controller_view.render("partial", name: "Fox Mulder") }
x.compare!
end
We get the following results:
Calculating -------------------------------------
component 42.256k memsize ( 232.000 retained)
510.000 objects ( 3.000 retained)
2.000 strings ( 0.000 retained)
partial 143.437k memsize ( 80.000 retained)
1.682k objects ( 2.000 retained)
18.000 strings ( 0.000 retained)
Comparison:
component: 42256 allocated
partial: 143437 allocated - 3.39x more
So partials are 5x slower and use up at least 3x more memory? Wow. That’s definitely a good value proposition to consider.
I do have to say that there are a few things I do not like about ViewComponent, but it’s mostly a superficial thing. I do not like that we have a new directory to add view logic under app/view_components
(in addition to app/helpers
and app/views
). I also don’t like that by default every component is going to have 2 new files, all organized at the same level under the app/view_components
directory, like so:
app/view_components/
-- view_component1.html
-- view_component1.rb
-- view_component2.html
-- view_component2.rb
-- view_component3.html
-- view_component3.rb
-- view_component4.html
-- view_component4.rb
...
-- view_componentX.html
-- view_componentX.rb
That just becomes tough on the eyes.
I’d like to see the performance and memory improvements somehow introduced to Rails proper. And it would be much nicer if there was consolidation on the view end of things, like perhaps getting rid of view helpers in favor of view components. That said, ViewComponent might be worth checking out if you have a complex UI. I’d recommend waiting to do so until it’s warranted. Your application might not need the performance or memory improvements. So weigh the pros and cons of introducing a new library to the projects with the benefits it provides. It could very well be that with some caching and a bit of optimization in some areas of your app, the performance needs are negated, and thus using ViewComponent not clearly beneficial.
One last thing to add: It’s still early for ViewComponent. I can see extra benefits surfacing as the project progresses. For example, Primer is a very nascent project that introduces reusable components, similar to how Bootstrap or Flowbit/Tailwind CSS introduce UI components. Once it’s mature enough, it might help developers speed up their development time for complex UI components in Rails.
So be on the lookout for new features, new benefits, and other wonderful things from the Github team and ViewComponent.
Have you used ViewComponent? If so, how do you like it?