<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Gabriel Martinez</title>
    <description>The latest articles on DEV Community by Gabriel Martinez (@masterofnull).</description>
    <link>https://dev.to/masterofnull</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F145842%2F8d34e157-4901-4ada-b8e0-4bc6ba3e4927.jpeg</url>
      <title>DEV Community: Gabriel Martinez</title>
      <link>https://dev.to/masterofnull</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/masterofnull"/>
    <language>en</language>
    <item>
      <title>Layered, Dynamic Turbo Frame Drawers</title>
      <dc:creator>Gabriel Martinez</dc:creator>
      <pubDate>Fri, 03 Oct 2025 19:54:48 +0000</pubDate>
      <link>https://dev.to/masterofnull/layered-dynamic-turbo-frame-drawers-11ep</link>
      <guid>https://dev.to/masterofnull/layered-dynamic-turbo-frame-drawers-11ep</guid>
      <description>&lt;h1&gt;
  
  
  Layered Turbo Frame Drawers: A Simple Pattern for Nested Navigation in Rails
&lt;/h1&gt;

&lt;p&gt;When building modern Rails applications with Hotwire, you'll often need drawers (or modals) that can open on top of each other. Think of drilling down through related records: &lt;strong&gt;Inventory Item → Parts History → Component Details&lt;/strong&gt;. &lt;/p&gt;

&lt;p&gt;The naive approach of hardcoding &lt;code&gt;drawer-2&lt;/code&gt;, &lt;code&gt;drawer-3&lt;/code&gt;, etc. quickly becomes unmaintainable. Here's a clean, Rails-idiomatic solution that scales.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Problem
&lt;/h2&gt;

&lt;p&gt;You have a drawer system working perfectly with Turbo Frames:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight erb"&gt;&lt;code&gt;&lt;span class="c"&gt;&amp;lt;!-- Works great for a single drawer --&amp;gt;&lt;/span&gt;
&lt;span class="cp"&gt;&amp;lt;%=&lt;/span&gt; &lt;span class="n"&gt;link_to&lt;/span&gt; &lt;span class="s2"&gt;"View Details"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;item_path&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="vi"&gt;@item&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; 
    &lt;span class="ss"&gt;data: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="ss"&gt;turbo_frame: &lt;/span&gt;&lt;span class="s1"&gt;'drawer'&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;turbo-frame&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"drawer"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/turbo-frame&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But what happens when you need to open a second drawer &lt;em&gt;from within&lt;/em&gt; the first drawer? If you target the same &lt;code&gt;drawer&lt;/code&gt; frame, you replace the parent. If you hardcode &lt;code&gt;drawer-2&lt;/code&gt;, you're locked into exactly two layers.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Solution: Dynamic Frame Names
&lt;/h2&gt;

&lt;p&gt;The key insight is to &lt;strong&gt;let the caller specify the frame name&lt;/strong&gt; and pass it through the request context.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 1: Capture the Frame from Request Headers
&lt;/h3&gt;

&lt;p&gt;When Turbo makes a frame request, it automatically sends a &lt;code&gt;Turbo-Frame&lt;/code&gt; header. Use this in your controller:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;PartsHistoryController&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ApplicationController&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;show&lt;/span&gt;
    &lt;span class="vi"&gt;@turbo_frame&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"Turbo-Frame"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="c1"&gt;# Rest of your action...&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 2: Update Your Drawer Helper
&lt;/h3&gt;

&lt;p&gt;Make the frame name dynamic:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# app/helpers/application_helper.rb&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;drawer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:,&lt;/span&gt; &lt;span class="ss"&gt;position: &lt;/span&gt;&lt;span class="s2"&gt;"right"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;turbo_frame: &lt;/span&gt;&lt;span class="s2"&gt;"drawer"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;block&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;render&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="ss"&gt;partial: &lt;/span&gt;&lt;span class="s2"&gt;"shared/drawer"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="ss"&gt;locals: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="ss"&gt;title: &lt;/span&gt;&lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="ss"&gt;position: &lt;/span&gt;&lt;span class="n"&gt;position&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="ss"&gt;turbo_frame: &lt;/span&gt;&lt;span class="n"&gt;turbo_frame&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="ss"&gt;content: &lt;/span&gt;&lt;span class="n"&gt;capture&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;block&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 3: Use the Dynamic Frame in Your Drawer Partial
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight erb"&gt;&lt;code&gt;&lt;span class="c"&gt;&amp;lt;!-- app/views/shared/_drawer.html.erb --&amp;gt;&lt;/span&gt;
&lt;span class="cp"&gt;&amp;lt;%=&lt;/span&gt; &lt;span class="n"&gt;turbo_frame_tag&lt;/span&gt; &lt;span class="n"&gt;turbo_frame&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;dialog&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"drawer"&lt;/span&gt; &lt;span class="na"&gt;data-controller=&lt;/span&gt;&lt;span class="s"&gt;"drawer"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="c"&gt;&amp;lt;!-- Your drawer content --&amp;gt;&lt;/span&gt;
    &lt;span class="cp"&gt;&amp;lt;%=&lt;/span&gt; &lt;span class="n"&gt;content&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/dialog&amp;gt;&lt;/span&gt;
&lt;span class="cp"&gt;&amp;lt;%&lt;/span&gt; &lt;span class="k"&gt;end&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 4: Nest Frames in Parent Drawers
&lt;/h3&gt;

&lt;p&gt;In your parent drawer view, add a turbo-frame tag where the child drawer will render:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight erb"&gt;&lt;code&gt;&lt;span class="c"&gt;&amp;lt;!-- app/views/inventory_line_items/show.html.erb --&amp;gt;&lt;/span&gt;
&lt;span class="cp"&gt;&amp;lt;%=&lt;/span&gt; &lt;span class="n"&gt;drawer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;title: &lt;/span&gt;&lt;span class="s2"&gt;"Stock #&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="vi"&gt;@item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;number&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"drawer-content"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="c"&gt;&amp;lt;!-- Parent drawer content --&amp;gt;&lt;/span&gt;

    &lt;span class="cp"&gt;&amp;lt;%=&lt;/span&gt; &lt;span class="n"&gt;link_to&lt;/span&gt; &lt;span class="s2"&gt;"Show History"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
        &lt;span class="n"&gt;parts_history_path&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="vi"&gt;@item&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; 
        &lt;span class="ss"&gt;data: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="ss"&gt;turbo_frame: &lt;/span&gt;&lt;span class="s1"&gt;'parts-history-drawer'&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="ss"&gt;class: &lt;/span&gt;&lt;span class="s2"&gt;"button"&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;

  &lt;span class="c"&gt;&amp;lt;!-- Child drawer will render here --&amp;gt;&lt;/span&gt;
  &lt;span class="cp"&gt;&amp;lt;%=&lt;/span&gt; &lt;span class="n"&gt;turbo_frame_tag&lt;/span&gt; &lt;span class="s2"&gt;"parts-history-drawer"&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;
&lt;span class="cp"&gt;&amp;lt;%&lt;/span&gt; &lt;span class="k"&gt;end&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 5: Use the Frame Context in Child Drawers
&lt;/h3&gt;

&lt;p&gt;The child drawer automatically receives the frame name from the header:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight erb"&gt;&lt;code&gt;&lt;span class="c"&gt;&amp;lt;!-- app/views/parts_history/show.html.erb --&amp;gt;&lt;/span&gt;
&lt;span class="cp"&gt;&amp;lt;%=&lt;/span&gt; &lt;span class="n"&gt;drawer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="ss"&gt;title: &lt;/span&gt;&lt;span class="s2"&gt;"Parts History"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
  &lt;span class="ss"&gt;turbo_frame: &lt;/span&gt;&lt;span class="vi"&gt;@turbo_frame&lt;/span&gt;  &lt;span class="c1"&gt;# Uses the captured header value&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;
  &lt;span class="c"&gt;&amp;lt;!-- Child drawer content --&amp;gt;&lt;/span&gt;

  &lt;span class="c"&gt;&amp;lt;!-- Can go deeper if needed! --&amp;gt;&lt;/span&gt;
  &lt;span class="cp"&gt;&amp;lt;%=&lt;/span&gt; &lt;span class="n"&gt;turbo_frame_tag&lt;/span&gt; &lt;span class="s2"&gt;"component-details-drawer"&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;
&lt;span class="cp"&gt;&amp;lt;%&lt;/span&gt; &lt;span class="k"&gt;end&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  How It Works
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;User clicks a link with &lt;code&gt;data: { turbo_frame: 'parts-history-drawer' }&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Turbo sends a request with header &lt;code&gt;Turbo-Frame: parts-history-drawer&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Controller captures this: &lt;code&gt;@turbo_frame = request.headers["Turbo-Frame"]&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;View renders with the correct frame name&lt;/li&gt;
&lt;li&gt;Content appears in the nested &lt;code&gt;&amp;lt;turbo-frame id="parts-history-drawer"&amp;gt;&lt;/code&gt; tag&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Benefits of This Pattern
&lt;/h2&gt;

&lt;p&gt;✅ &lt;strong&gt;No hardcoded layer limits&lt;/strong&gt; - Works for 2, 3, 4+ levels of nesting&lt;br&gt;&lt;br&gt;
✅ &lt;strong&gt;Explicit frame names&lt;/strong&gt; - Easy to debug and understand&lt;br&gt;&lt;br&gt;
✅ &lt;strong&gt;Rails-idiomatic&lt;/strong&gt; - Uses request context naturally&lt;br&gt;&lt;br&gt;
✅ &lt;strong&gt;Minimal changes&lt;/strong&gt; - Works with existing drawer infrastructure&lt;br&gt;&lt;br&gt;
✅ &lt;strong&gt;Flexible&lt;/strong&gt; - Each drawer explicitly declares where its children render  &lt;/p&gt;
&lt;h2&gt;
  
  
  Going Further: Unique Frame Names
&lt;/h2&gt;

&lt;p&gt;For even more flexibility, generate unique frame names:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;drawer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:,&lt;/span&gt; &lt;span class="ss"&gt;position: &lt;/span&gt;&lt;span class="s2"&gt;"right"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;turbo_frame: &lt;/span&gt;&lt;span class="kp"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;block&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="c1"&gt;# Auto-generate if not provided&lt;/span&gt;
  &lt;span class="n"&gt;turbo_frame&lt;/span&gt; &lt;span class="o"&gt;||=&lt;/span&gt; &lt;span class="s2"&gt;"drawer-&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="no"&gt;SecureRandom&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;hex&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

  &lt;span class="n"&gt;render&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="ss"&gt;partial: &lt;/span&gt;&lt;span class="s2"&gt;"shared/drawer"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="ss"&gt;locals: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:,&lt;/span&gt; &lt;span class="n"&gt;position&lt;/span&gt;&lt;span class="p"&gt;:,&lt;/span&gt; &lt;span class="n"&gt;turbo_frame&lt;/span&gt;&lt;span class="p"&gt;:,&lt;/span&gt; &lt;span class="ss"&gt;content: &lt;/span&gt;&lt;span class="n"&gt;capture&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;block&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now you can nest drawers infinitely without thinking about naming:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight erb"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;%=&lt;/span&gt; &lt;span class="n"&gt;link_to&lt;/span&gt; &lt;span class="s2"&gt;"Details"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;item_path&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="vi"&gt;@item&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; 
    &lt;span class="ss"&gt;data: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="ss"&gt;turbo_frame: &lt;/span&gt;&lt;span class="s2"&gt;"drawer-&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="no"&gt;SecureRandom&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;hex&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Alternative: State Machine Approach
&lt;/h2&gt;

&lt;p&gt;For complex drawer hierarchies with history/navigation, consider a Stimulus controller that manages a drawer stack:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Tracks all open drawers and their relationships&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;extends&lt;/span&gt; &lt;span class="nx"&gt;Controller&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="nx"&gt;values&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;depth&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Number&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nf"&gt;connect&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;element&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;zIndex&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1040&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;depthValue&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But for most applications, the simple header-based approach is sufficient and easier to reason about.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Layered Turbo Frames don't require complex JavaScript state management. By leveraging the &lt;code&gt;Turbo-Frame&lt;/code&gt; request header and explicit frame nesting, you get a clean, maintainable pattern that scales naturally with your application's needs.&lt;/p&gt;

&lt;p&gt;The key principles:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Let the caller specify the target frame&lt;/strong&gt; via &lt;code&gt;data-turbo-frame&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Capture the frame name&lt;/strong&gt; from request headers in your controller&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Pass it through&lt;/strong&gt; to your view layer&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Nest frames explicitly&lt;/strong&gt; in parent views where children should appear&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This pattern works for drawers, modals, sidebars, or any nested Turbo Frame scenario.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Want to see a working example? Check out this GitHub repo with a complete implementation.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>rails</category>
      <category>hotwire</category>
      <category>turbo</category>
      <category>webdev</category>
    </item>
  </channel>
</rss>
