{"id":13073,"date":"2025-08-04T06:47:10","date_gmt":"2025-08-04T06:47:10","guid":{"rendered":"https:\/\/www.bacancytechnology.com\/qanda\/?p=13073"},"modified":"2025-08-05T12:06:20","modified_gmt":"2025-08-05T12:06:20","slug":"phlex-multiple-yield","status":"publish","type":"post","link":"https:\/\/www.bacancytechnology.com\/qanda\/ruby-on-rails\/phlex-multiple-yield","title":{"rendered":"Phlex Multiple Yield"},"content":{"rendered":"<p>Is it possible for a Phlex component to yield more than once? For example:<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"ruby\">class Component &lt; Phlex::HTML\r\n def template\r\n   div do\r\n     yield\r\n     span { plain \"component\" }\r\n     yield\r\n   end\r\n end\r\nend\r\n\r\n# Expected usage:\r\n&lt;%= render Component.new do %&gt;\r\n &lt;div&gt;foo&lt;\/div&gt;\r\n &lt;!-- \"component\" span should appear here --&gt;\r\n &lt;div&gt;bar&lt;\/div&gt;\r\n&lt;% end %&gt;\r\n\r\n<\/pre>\n<p>Obviously, this doesn&#8217;t work &#8211; the whole block just gets repeated. But is there a way to make it behave as expected, ideally without manually passing the content in pieces?<\/p>\n<h2>Why This Doesn&#8217;t Work:<\/h2>\n<p>Ruby blocks are <strong>not generators.<\/strong> When you yield, Ruby calls the entire block &#8211; it doesn\u2019t pause and resume like JavaScript&#8217;s yield or Python&#8217;s yield in generators.<\/p>\n<p>So yielding multiple times simply <strong>re-renders the full block<\/strong> each time.<\/p>\n<h2>Solution 1: Split the Content Using Named Blocks<\/h2>\n<p>You can simulate multiple-yield behavior by splitting your content manually into named lambdas and passing them to your component:<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"ruby\">class Component &lt; Phlex::HTML\r\n def initialize(first:, last:)\r\n   @first = first\r\n   @last = last\r\n end\r\n\r\n def template\r\n   div do\r\n     instance_exec(&amp;@first)\r\n     span { \"component\" }\r\n     instance_exec(&amp;@last)\r\n   end\r\n end\r\nend\r\n<\/pre>\n<p><strong>Usage:<\/strong><\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"ruby\">&lt;%= render Component.new( first: -&gt; { div { \"foo\" } },\r\n last:  -&gt; { div { \"bar\" } }\r\n) %&gt;\r\n<\/pre>\n<p><strong>Clean and reusable.<\/strong> Great if your component needs to handle dynamic regions or slots.<\/p>\n<h2>Solution 2: Yield self and Call Helper Methods Inside the Block<\/h2>\n<p>This is a clean Phlex-friendly pattern: let the block yield the component itself, so users can call methods like content at specific points.<\/p>\n<p><strong>Component:<\/strong><\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"ruby\">class Component &lt; Phlex::HTML\r\n def template\r\n   div do\r\n     yield(self)\r\n   end\r\n end\r\n\r\n def content\r\n   span { \"component\" }\r\n end\r\nend\r\n<\/pre>\n<p><strong>Usage:<\/strong><\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"ruby\">&lt;%= render Component.new do |c| %&gt;\r\n<div>foo<\/div>\r\n&lt;%= c.content %&gt;\r\n<div>bar<\/div>\r\n&lt;% end %&gt;\r\n<\/pre>\n<p>This keeps your components tidy and gives callers full control over when to render specific bits of UI.<\/p>\n<h2>Solution 3: Use capture and unsafe_raw in ERB (Rails Only)<\/h2>\n<p>For Rails + ERB users, you can pre-capture content and pass it to your component:<\/p>\n<p><strong>Component:<\/strong><\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"ruby\">class Component &lt; Phlex::HTML\r\n def initialize(first:, last:)\r\n   @first = first\r\n   @last = last\r\n end\r\n\r\n def template\r\n   div do\r\n     unsafe_raw @first\r\n     span { \"component\" }\r\n     unsafe_raw @last\r\n   end\r\n end\r\nend\r\n<\/pre>\n<p><strong>Usage:<\/strong><\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"ruby\">&lt;% first = capture do %&gt;\r\n<div>foo<\/div>\r\n&lt;% end %&gt; &lt;% last = capture do %&gt;\r\n<div>bar<\/div>\r\n&lt;% end %&gt; &lt;%= render Component.new(first: first, last: last) %&gt;\r\n<\/pre>\n<p>Only use unsafe_raw if you&#8217;re confident the content is safe from injection.<\/p>\n<h2>My Recommended Solution<\/h2>\n<p>Use yield(self) and let the caller insert helper methods inside the block.<\/p>\n<p>This gives the developer the flexibility to insert your reusable bits (span, content, etc.) <strong>at any point<\/strong>, while keeping a very readable and testable API:<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"ruby\">class Component &lt; Phlex::HTML\r\n def template\r\n   div do\r\n     yield(self)\r\n   end\r\n end\r\n def content\r\n   span { \"component\" }\r\n end\r\nend\r\n<\/pre>\n<p>It&#8217;s concise, scalable, and aligns with how Ruby and Phlex both operate.<\/p>\n<h2>Summary Table<\/h2>\n<table class=\"table table-striped\">\n<tbody>\n<tr>\n<td><b>Approach<\/b><\/td>\n<td><b>Multiple Content Points<\/b><\/td>\n<td><b>Phlex DSL Friendly<\/b><\/td>\n<td><b>Readability<\/b><\/td>\n<td><b>Comments<\/b><\/td>\n<\/tr>\n<tr>\n<td>Multiple yields to same block<\/td>\n<td>\u274c (Repeats content)<\/td>\n<td>\u2705<\/td>\n<td>\u2705<\/td>\n<td>Not recommended<\/td>\n<\/tr>\n<tr>\n<td>Named lambdas<\/td>\n<td>\u2705<\/td>\n<td>\u2705<\/td>\n<td>\u2705<\/td>\n<td>Best for slots-style API<\/td>\n<\/tr>\n<tr>\n<td>capture + unsafe_raw<\/td>\n<td>\u2705<\/td>\n<td>\u26a0\ufe0f<\/td>\n<td>\u26a0\ufe0f<\/td>\n<td>Rails-only, careful with safety<\/td>\n<\/tr>\n<tr>\n<td>yield(self) + helper method<\/td>\n<td>\u2705<\/td>\n<td>\u2705<\/td>\n<td>\u2705\u2705<\/td>\n<td>\u2705 Recommended<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p><\/p>\n<h2>Final Thoughts<\/h2>\n<p>While Ruby blocks can\u2019t pause and resume like generators, Phlex gives you enough flexibility to work around it elegantly. Whether you use lambdas, yield the component, or go with slots, you\u2019ve got great tools to keep your view logic clean and composable.<\/p>\n<p>If you&#8217;re building something more advanced like modal slots, tabs, or layout wrappers, consider building on the &#8220;named lambda&#8221; or yield(self) pattern, they scale really well.<\/p>\n<div class=\"qanda-read-box\"><div class=\"bg-light read-more-icon\"><img decoding=\"async\" src=\"https:\/\/assets.bacancytechnology.com\/qanda\/wp-content\/uploads\/2025\/04\/24061434\/read-txt.png\" alt=\"Also Read\"><p><\/p><h3>Also Read:<\/h3><a href=\"https:\/\/www.bacancytechnology.com\/blog\/design-patterns-in-ruby-on-rails\" target=\"_blank\">Design Patterns in Rails<\/a><\/div><\/div>\n","protected":false},"excerpt":{"rendered":"<p>Is it possible for a Phlex component to yield more than once? For example: class Component &lt; Phlex::HTML def template div do yield span { plain &#8220;component&#8221; } yield end end end # Expected usage: &lt;%= render Component.new do %&gt; &lt;div&gt;foo&lt;\/div&gt; &lt;!&#8211; &#8220;component&#8221; span should appear here &#8211;&gt; &lt;div&gt;bar&lt;\/div&gt; &lt;% end %&gt; Obviously, this doesn&#8217;t [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":13074,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"inline_featured_image":false,"footnotes":""},"categories":[11],"tags":[],"class_list":["post-13073","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-ruby-on-rails"],"acf":[],"_links":{"self":[{"href":"https:\/\/www.bacancytechnology.com\/qanda\/wp-json\/wp\/v2\/posts\/13073"}],"collection":[{"href":"https:\/\/www.bacancytechnology.com\/qanda\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.bacancytechnology.com\/qanda\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.bacancytechnology.com\/qanda\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.bacancytechnology.com\/qanda\/wp-json\/wp\/v2\/comments?post=13073"}],"version-history":[{"count":5,"href":"https:\/\/www.bacancytechnology.com\/qanda\/wp-json\/wp\/v2\/posts\/13073\/revisions"}],"predecessor-version":[{"id":13095,"href":"https:\/\/www.bacancytechnology.com\/qanda\/wp-json\/wp\/v2\/posts\/13073\/revisions\/13095"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.bacancytechnology.com\/qanda\/wp-json\/wp\/v2\/media\/13074"}],"wp:attachment":[{"href":"https:\/\/www.bacancytechnology.com\/qanda\/wp-json\/wp\/v2\/media?parent=13073"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.bacancytechnology.com\/qanda\/wp-json\/wp\/v2\/categories?post=13073"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.bacancytechnology.com\/qanda\/wp-json\/wp\/v2\/tags?post=13073"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}