DEV Community

Scott Barrow
Scott Barrow

Posted on

20 5

Rails ViewComponent helper

Ruby Dev's like me don't like verbosity no matter how small, syntactic sugar is one of the underlying attractions of Ruby.

Rails ViewComponents are a superb addition to Rails especially when paired with utility style CSS like Tailwind.
However, repeatedly writing these render's gnaws at my OCD.
Constants in a view template, :shock:, the overloaded render method, all those parens!

render(SomeAwesomeComponent.new) do |component|
  Render(AlertComponent.new(type: 'success') do |component|
    render(ButtonComponent.new(classes: 'justify-end ml-2 items-start')
  end
end
Enter fullscreen mode Exit fullscreen mode

What if I told you, you could create a helper, obvious right?

more awesome, more lowercase, less parens, easier on the eyes

component :some_awesome_component do |awesome|
  component :alert, type: 'success' do |alert|
    component :button, classes: 'justify-end ml-2 items-start'
  end
end
Enter fullscreen mode Exit fullscreen mode

I chose to use component but if you want to go super succinct, you could even do

vc :some_awesome_component do |awesome|
  vc :alert, type: 'success' do |alert|
    vc :button, classes: 'justify-end ml-2 items-start'
  end
end
Enter fullscreen mode Exit fullscreen mode

This could be yours for the low low price of... reading below.

  def component(name, context: nil, **args, &block)
    return render_component_in(context, name, **args, &block) if context

    render component_class_for(name).new(args), &block
  end

  def render_component_in(context, name, **args, &block)
    component_class_for(name).new(args).render_in(context, &block)
  end

  private

  def component_class_for(path)
    name, namespace = path.to_s.split('/').reverse

    file_name = "#{name}_component"
    component_name = file_name.classify
    namespace ||= namespace(file_name)
    return "#{namespace.capitalize}::#{component_name}".constantize unless namespace == 'components'

    component_name.constantize
  end
Enter fullscreen mode Exit fullscreen mode

What's more if you follow some design pattern like atomic, and structure your components as such, you can create a separate namespace method to discover the component path allowing you to communicate your design system like

organism :some_awesome_component do |awesome|
  molecule :alert, type: 'success' do |alert|
    atom :button, classes: 'justify-end ml-2 items-start'
  end
end
Enter fullscreen mode Exit fullscreen mode

You can find the RailsByte here https://railsbytes.com/templates/xjNsDY

Billboard image

The Next Generation Developer Platform

Coherence is the first Platform-as-a-Service you can control. Unlike "black-box" platforms that are opinionated about the infra you can deploy, Coherence is powered by CNC, the open-source IaC framework, which offers limitless customization.

Learn more

Top comments (2)

Collapse
 
muriloime profile image
Murilo Vasconcelos

Hello @scottbarrow , Great article, although I dont think the rails bytes code works when passing blocks. However, the article's code render component_class_for(name).new(args), &block does the trick .
cheers

Collapse
 
leastbad profile image
leastbad

You can keep your Tailwind classes, but I'm totally using these helpers. Great work.

A Workflow Copilot. Tailored to You.

Pieces.app image

Our desktop app, with its intelligent copilot, streamlines coding by generating snippets, extracting code from screenshots, and accelerating problem-solving.

Read the docs

👋 Kindness is contagious

Please leave a ❤️ or a friendly comment on this post if you found it helpful!

Okay