<?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>Forem: hersoftsyntax</title>
    <description>The latest articles on Forem by hersoftsyntax (@hersoftsyntax).</description>
    <link>https://forem.com/hersoftsyntax</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%2F3361456%2F8a971b4b-9692-4d2a-8a97-14b11305c0f4.jpg</url>
      <title>Forem: hersoftsyntax</title>
      <link>https://forem.com/hersoftsyntax</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/hersoftsyntax"/>
    <language>en</language>
    <item>
      <title>How to Serve Multiple Themes in Rails Using Sass and esbuild</title>
      <dc:creator>hersoftsyntax</dc:creator>
      <pubDate>Sun, 05 Oct 2025 20:49:22 +0000</pubDate>
      <link>https://forem.com/hersoftsyntax/how-to-serve-multiple-themes-in-rails-using-sass-and-esbuild-5a68</link>
      <guid>https://forem.com/hersoftsyntax/how-to-serve-multiple-themes-in-rails-using-sass-and-esbuild-5a68</guid>
      <description>&lt;p&gt;In this article, I'll show you one way to create multiple themes using a server-side variable that automatically renders a theme in Rails (with &lt;code&gt;esbuild&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;What's the use case?&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Your page has a parent resource that dictates your app's branding&lt;/li&gt;
&lt;li&gt;You don't want spaghetti javascript to handle themes.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Before jumping in, let's understand how the compilation works&lt;br&gt;
Rails has its own default asset pipeline (Sprockets). Its job is to pre-process, concatenate, minify, and serve these files (goes inside &lt;code&gt;public/assets&lt;/code&gt; folder). Shifting to &lt;code&gt;esbuild&lt;/code&gt; or &lt;code&gt;webpack&lt;/code&gt; turns the approach to be more modern (easy to manage dependencies separately from the back-end), faster (written in lower level languages like Go) and flexible (we can configure each step of the process the way we want).&lt;/p&gt;



&lt;p&gt;For our approach we will create structure like this:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;1 Sass file with all your classes and common imports&lt;/li&gt;
&lt;li&gt;1 Sass file with common variables/mixins&lt;/li&gt;
&lt;li&gt;1 Sass file per theme with the variables you want to customize&lt;/li&gt;
&lt;li&gt;1 method to control the theme to be rendered&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  Stylesheets
&lt;/h2&gt;

&lt;p&gt;Starting with the common classes and imports, you need to create a partial, meaning, a file starting with an underscore(&lt;code&gt;_&lt;/code&gt;). Give it a generic name like &lt;code&gt;styles&lt;/code&gt; or &lt;code&gt;global&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="o"&gt;//&lt;/span&gt; &lt;span class="nt"&gt;app&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nt"&gt;assets&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nt"&gt;stylesheets&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nt"&gt;_global&lt;/span&gt;&lt;span class="nc"&gt;.scss&lt;/span&gt; 
&lt;span class="k"&gt;@import&lt;/span&gt; &lt;span class="s1"&gt;"bootstrap/scss/bootstrap"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="o"&gt;//&lt;/span&gt; &lt;span class="nt"&gt;your&lt;/span&gt; &lt;span class="nt"&gt;other&lt;/span&gt; &lt;span class="nt"&gt;imports&lt;/span&gt;

&lt;span class="o"&gt;//&lt;/span&gt; &lt;span class="nt"&gt;your&lt;/span&gt; &lt;span class="nt"&gt;classes&lt;/span&gt;
&lt;span class="nf"&gt;#some-id&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;background&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="n"&gt;primary&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nc"&gt;.some-class&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;background-color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="n"&gt;primary&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;Then we can create a Sass partial to accommodate common variables which will be needed across all themes.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="o"&gt;//&lt;/span&gt; &lt;span class="nt"&gt;app&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nt"&gt;assets&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nt"&gt;stylesheets&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nt"&gt;_common_variables&lt;/span&gt;&lt;span class="nc"&gt;.scss&lt;/span&gt;

&lt;span class="o"&gt;//&lt;/span&gt;
&lt;span class="o"&gt;//&lt;/span&gt; &lt;span class="nt"&gt;Color&lt;/span&gt; &lt;span class="nt"&gt;system&lt;/span&gt;
&lt;span class="o"&gt;//&lt;/span&gt;

&lt;span class="o"&gt;//&lt;/span&gt; &lt;span class="nt"&gt;GRAY&lt;/span&gt; &lt;span class="nt"&gt;SCALE&lt;/span&gt;
&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="nt"&gt;white&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;  &lt;span class="nf"&gt;#fff&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nt"&gt;default&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="nt"&gt;gray-100&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;#d5efd5&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nt"&gt;default&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="nt"&gt;gray-200&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;#e9ecef&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nt"&gt;default&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="nt"&gt;gray-300&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;#dee2e6&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nt"&gt;default&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="nt"&gt;gray-400&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;#ced4da&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nt"&gt;default&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="nt"&gt;gray-500&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;#adb5bd&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nt"&gt;default&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="nt"&gt;gray-600&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="err"&gt;#85&lt;/span&gt;&lt;span class="nt"&gt;ba91&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nt"&gt;default&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="nt"&gt;gray-700&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;#a8dac4&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nt"&gt;default&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="nt"&gt;gray-800&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="err"&gt;#343&lt;/span&gt;&lt;span class="nt"&gt;a40&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nt"&gt;default&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="nt"&gt;gray-900&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="err"&gt;#212529&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nt"&gt;default&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="nt"&gt;black&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;  &lt;span class="err"&gt;#000&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nt"&gt;default&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="o"&gt;//&lt;/span&gt; &lt;span class="nt"&gt;DEFAULT&lt;/span&gt; &lt;span class="nt"&gt;COLORS&lt;/span&gt;
&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="nt"&gt;blue&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;    &lt;span class="err"&gt;#0&lt;/span&gt;&lt;span class="nt"&gt;d6efd&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="nt"&gt;indigo&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;  &lt;span class="err"&gt;#6610&lt;/span&gt;&lt;span class="nt"&gt;f2&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="nt"&gt;purple&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;  &lt;span class="err"&gt;#6&lt;/span&gt;&lt;span class="nt"&gt;f42c1&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="nt"&gt;pink&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;    &lt;span class="nf"&gt;#d63384&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="nt"&gt;red&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;     &lt;span class="nf"&gt;#dc3545&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="nt"&gt;orange&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;  &lt;span class="nf"&gt;#fd7e14&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="nt"&gt;yellow&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;  &lt;span class="nf"&gt;#ffc107&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="nt"&gt;green&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;   &lt;span class="err"&gt;#198754&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="nt"&gt;teal&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;    &lt;span class="err"&gt;#20&lt;/span&gt;&lt;span class="nt"&gt;c997&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="nt"&gt;cyan&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;    &lt;span class="err"&gt;#0&lt;/span&gt;&lt;span class="nt"&gt;dcaf0&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="nt"&gt;light-red&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;#f6d4d6&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="o"&gt;//&lt;/span&gt; &lt;span class="nt"&gt;SYSTEM&lt;/span&gt; &lt;span class="nt"&gt;COLORS&lt;/span&gt;
&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="nt"&gt;primary&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="err"&gt;#0&lt;/span&gt;&lt;span class="nt"&gt;C1439&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="nt"&gt;dark&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="err"&gt;#0&lt;/span&gt;&lt;span class="nt"&gt;C1439&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="nt"&gt;secondary&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;     &lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="nt"&gt;gray-600&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="nt"&gt;success&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;       &lt;span class="err"&gt;#00&lt;/span&gt;&lt;span class="nt"&gt;B554&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="nt"&gt;info&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;          &lt;span class="err"&gt;#5&lt;/span&gt;&lt;span class="nt"&gt;D97D1&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="nt"&gt;warning&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;       &lt;span class="nf"&gt;#DB7000&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="nt"&gt;danger&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;        &lt;span class="nf"&gt;#D5113B&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="nt"&gt;light&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;         &lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="nt"&gt;gray-100&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="nt"&gt;primary-50&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="err"&gt;#0&lt;/span&gt;&lt;span class="nt"&gt;C143905&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nt"&gt;default&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="nt"&gt;primary-100&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="err"&gt;#0&lt;/span&gt;&lt;span class="nt"&gt;C143910&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nt"&gt;default&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="nt"&gt;primary-200&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="err"&gt;#0&lt;/span&gt;&lt;span class="nt"&gt;C143920&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nt"&gt;default&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="nt"&gt;primary-300&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="err"&gt;#0&lt;/span&gt;&lt;span class="nt"&gt;C143930&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nt"&gt;default&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="nt"&gt;primary-400&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="err"&gt;#0&lt;/span&gt;&lt;span class="nt"&gt;C143940&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nt"&gt;default&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="nt"&gt;primary-500&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="err"&gt;#0&lt;/span&gt;&lt;span class="nt"&gt;C143950&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nt"&gt;default&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="nt"&gt;primary-600&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="err"&gt;#0&lt;/span&gt;&lt;span class="nt"&gt;C143960&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nt"&gt;default&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="nt"&gt;primary-700&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="err"&gt;#0&lt;/span&gt;&lt;span class="nt"&gt;C143970&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nt"&gt;default&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="nt"&gt;primary-800&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="err"&gt;#0&lt;/span&gt;&lt;span class="nt"&gt;C143980&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nt"&gt;default&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="nt"&gt;primary-900&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="err"&gt;#0&lt;/span&gt;&lt;span class="nt"&gt;C143990&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nt"&gt;default&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, for our theme files, we want to follow a structure like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="o"&gt;//&lt;/span&gt; &lt;span class="nt"&gt;import&lt;/span&gt; &lt;span class="nt"&gt;common&lt;/span&gt; &lt;span class="nt"&gt;variables&lt;/span&gt;

&lt;span class="o"&gt;//&lt;/span&gt; &lt;span class="nt"&gt;define&lt;/span&gt; &lt;span class="nt"&gt;theme&lt;/span&gt; &lt;span class="nt"&gt;variables&lt;/span&gt;

&lt;span class="o"&gt;//&lt;/span&gt; &lt;span class="nt"&gt;import&lt;/span&gt; &lt;span class="nt"&gt;styles&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Why do we need to split this way?&lt;/em&gt;&lt;br&gt;
All these styles need to be precompiled, meaning when you serve the application your &lt;strong&gt;SCSS&lt;/strong&gt; turns into a single, ready-to-use compiled CSS file (&lt;em&gt;the browser doesn't understand Sass&lt;/em&gt;). While I'm using bootstrap for this example, override variables need to be defined before importing it. As well, before our classes.  Each theme file will need to have ALL definitions. This might sound redundant, but in reality it just allows the system to serve one single file, instead of all of them, which makes it a bit more performant. Adding to the fact that being precompiled we can't change it dynamically. Any other way would be to create duplicated classes and add a prefix of the theme, &lt;em&gt;but in my opinion that sounds worse&lt;/em&gt;. Because you would need to compute for each HTML element what theme you would like to use.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Usually you start your project with an &lt;code&gt;application.scss&lt;/code&gt; aggregating  all your styles for your app. &lt;br&gt;
In this case, our application will be the default theme to be precompiled.&lt;/p&gt;

&lt;p&gt;The default theme:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="o"&gt;//&lt;/span&gt; &lt;span class="nt"&gt;app&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nt"&gt;assets&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nt"&gt;stylesheets&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nt"&gt;application&lt;/span&gt;&lt;span class="nc"&gt;.scss&lt;/span&gt;
&lt;span class="o"&gt;//&lt;/span&gt;
&lt;span class="o"&gt;//&lt;/span&gt; &lt;span class="nt"&gt;Start&lt;/span&gt; &lt;span class="nt"&gt;Variables&lt;/span&gt;
&lt;span class="o"&gt;//&lt;/span&gt;
&lt;span class="k"&gt;@import&lt;/span&gt; &lt;span class="s1"&gt;"common_variables"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="nt"&gt;primary&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="err"&gt;#0&lt;/span&gt;&lt;span class="nt"&gt;C1439&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="nt"&gt;dark&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="err"&gt;#0&lt;/span&gt;&lt;span class="nt"&gt;C1439&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="nt"&gt;secondary&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="nt"&gt;gray-600&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="nt"&gt;success&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="err"&gt;#00&lt;/span&gt;&lt;span class="nt"&gt;B554&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="nt"&gt;info&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="err"&gt;#5&lt;/span&gt;&lt;span class="nt"&gt;D97D1&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="nt"&gt;warning&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;  &lt;span class="nf"&gt;#DB7000&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="nt"&gt;danger&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;  &lt;span class="nf"&gt;#D5113B&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="nt"&gt;light&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="nt"&gt;gray-100&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="o"&gt;//&lt;/span&gt;
&lt;span class="o"&gt;//&lt;/span&gt; &lt;span class="nt"&gt;End&lt;/span&gt; &lt;span class="nt"&gt;Variables&lt;/span&gt;
&lt;span class="o"&gt;//&lt;/span&gt;

&lt;span class="k"&gt;@import&lt;/span&gt; &lt;span class="s1"&gt;"global"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;One theme, let's call it &lt;code&gt;halloween&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="o"&gt;//&lt;/span&gt; &lt;span class="nt"&gt;app&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nt"&gt;assets&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nt"&gt;stylesheets&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nt"&gt;halloween&lt;/span&gt;&lt;span class="nc"&gt;.scss&lt;/span&gt;

&lt;span class="o"&gt;//&lt;/span&gt;
&lt;span class="o"&gt;//&lt;/span&gt; &lt;span class="nt"&gt;Start&lt;/span&gt; &lt;span class="nt"&gt;Variables&lt;/span&gt;
&lt;span class="o"&gt;//&lt;/span&gt;
&lt;span class="k"&gt;@import&lt;/span&gt; &lt;span class="s1"&gt;"common_variables"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="nt"&gt;primary&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;#d5810e&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="nt"&gt;dark&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="err"&gt;#292929&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="nt"&gt;secondary&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="err"&gt;#590606&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="nt"&gt;success&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;#d36546&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="nt"&gt;info&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;#cfc2a1&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="nt"&gt;warning&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;#d3804c&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="nt"&gt;danger&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="err"&gt;#9&lt;/span&gt;&lt;span class="nt"&gt;A0539FF&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="nt"&gt;light&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;#dac3a8&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="o"&gt;//&lt;/span&gt;
&lt;span class="o"&gt;//&lt;/span&gt; &lt;span class="nt"&gt;End&lt;/span&gt; &lt;span class="nt"&gt;Variables&lt;/span&gt;
&lt;span class="o"&gt;//&lt;/span&gt;

&lt;span class="k"&gt;@import&lt;/span&gt; &lt;span class="s1"&gt;"global"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Another theme and let's call it &lt;code&gt;xmas&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="o"&gt;//&lt;/span&gt; &lt;span class="nt"&gt;app&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nt"&gt;assets&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nt"&gt;stylesheets&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nt"&gt;xmas&lt;/span&gt;&lt;span class="nc"&gt;.scss&lt;/span&gt;

&lt;span class="o"&gt;//&lt;/span&gt;
&lt;span class="o"&gt;//&lt;/span&gt; &lt;span class="nt"&gt;Start&lt;/span&gt; &lt;span class="nt"&gt;Variables&lt;/span&gt;
&lt;span class="o"&gt;//&lt;/span&gt;
&lt;span class="k"&gt;@import&lt;/span&gt; &lt;span class="s1"&gt;"common_variables"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="nt"&gt;primary&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;#b80606&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="nt"&gt;dark&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="err"&gt;#0&lt;/span&gt;&lt;span class="nt"&gt;d3903&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="nt"&gt;secondary&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;#d3d3d3&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="nt"&gt;success&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="err"&gt;#5&lt;/span&gt;&lt;span class="nt"&gt;da104&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="nt"&gt;info&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="err"&gt;#87&lt;/span&gt;&lt;span class="nt"&gt;b6b6&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="nt"&gt;warning&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;#dd4b2a&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="nt"&gt;danger&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="err"&gt;#700909&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="nt"&gt;light&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;#ffffbe&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="o"&gt;//&lt;/span&gt;
&lt;span class="o"&gt;//&lt;/span&gt; &lt;span class="nt"&gt;End&lt;/span&gt; &lt;span class="nt"&gt;Variables&lt;/span&gt;
&lt;span class="o"&gt;//&lt;/span&gt;

&lt;span class="k"&gt;@import&lt;/span&gt; &lt;span class="s1"&gt;"global"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Compilation
&lt;/h2&gt;

&lt;p&gt;This alone doesn't do anything.&lt;/p&gt;

&lt;p&gt;When you decide to use Sass in your styles, you will need to compile them into a single .css files, which will be served and read in the browser.&lt;/p&gt;

&lt;p&gt;Using &lt;code&gt;esbuild&lt;/code&gt;, we need to map each &lt;strong&gt;SCSS&lt;/strong&gt; file to its corresponding compiled &lt;strong&gt;CSS&lt;/strong&gt; file.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;You need to make sure you have &lt;code&gt;sass&lt;/code&gt; installed in your packages.&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="err"&gt;//&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nl"&gt;"build:css"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"sass ./app/assets/stylesheets/application.scss:./app/assets/builds/application.css ./app/assets/stylesheets/halloween.scss:./app/assets/builds/halloween.css ./app/assets/stylesheets/xmas.scss:./app/assets/builds/xmas.css --no-source-map --load-path=node_modules"&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="err"&gt;//&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nl"&gt;"build:css:watch"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"nodemon --watch ./app/assets/stylesheets/ --ext scss --exec &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;yarn build:css&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Map your scss files into the expected css files &lt;/li&gt;
&lt;li&gt;A command (using &lt;code&gt;nodemon&lt;/code&gt;, you'll need to add it as well) to watch for changes and compile them&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Compile it using &lt;code&gt;yarn build:css&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;To see it in action, you can change manually the name of the stylesheet in your main layout, by replacing "application" with "xmas" or "halloween".&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;head&amp;gt;&lt;/span&gt;
  &lt;span class="c"&gt;&amp;lt;!-- ... --&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="err"&gt;%=&lt;/span&gt; &lt;span class="na"&gt;stylesheet_link_tag&lt;/span&gt; &lt;span class="err"&gt;"&lt;/span&gt;&lt;span class="na"&gt;xmas&lt;/span&gt;&lt;span class="err"&gt;",&lt;/span&gt; &lt;span class="err"&gt;"&lt;/span&gt;&lt;span class="na"&gt;data-turbo-track&lt;/span&gt;&lt;span class="err"&gt;"&lt;/span&gt;&lt;span class="na"&gt;:&lt;/span&gt; &lt;span class="err"&gt;"&lt;/span&gt;&lt;span class="na"&gt;reload&lt;/span&gt;&lt;span class="err"&gt;"&lt;/span&gt; &lt;span class="err"&gt;%&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="c"&gt;&amp;lt;!-- ... --&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/head&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;If your styles aren't compiling in development, make sure you don't have any '&lt;strong&gt;builds&lt;/strong&gt;' or '&lt;strong&gt;assets&lt;/strong&gt;' inside &lt;code&gt;public/&lt;/code&gt;. Rails prioritises this folder when you precompile using Rails engine instead of esbuild. You can read more about &lt;a href="https://guides.rubyonrails.org/asset_pipeline.html#precompiling-assets" rel="noopener noreferrer"&gt;here&lt;/a&gt; it has a small explanation at the end of the section.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Theme selector
&lt;/h2&gt;

&lt;p&gt;Next, we need to automatically swap the theme based on some logic. In this case, I created a resource  where I'm expecting a &lt;code&gt;:party&lt;/code&gt; parameter and I will use it to determine which theme I want to serve.&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;# routes.rb&lt;/span&gt;
&lt;span class="n"&gt;resources&lt;/span&gt; &lt;span class="ss"&gt;:parties&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;only: &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="ss"&gt;:show&lt;/span&gt; &lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="ss"&gt;param: :party&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;path: &lt;/span&gt;&lt;span class="s2"&gt;"/"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In our &lt;code&gt;ApplicationController&lt;/code&gt; or any other base controller, you need to create a function that looks at this parameter and determines the correct theme.&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;# application_controller.rb&lt;/span&gt;
&lt;span class="n"&gt;helper_method&lt;/span&gt; &lt;span class="ss"&gt;:current_theme&lt;/span&gt;

&lt;span class="c1"&gt;# this is not private&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;current_theme&lt;/span&gt;
  &lt;span class="n"&gt;party&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:party&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;to_s&lt;/span&gt;
  &lt;span class="sx"&gt;%w[xmas halloween]&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;include?&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;party&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="n"&gt;party&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"application"&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;By using &lt;code&gt;helper_method&lt;/code&gt; we're allowing this method to be called from the view.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Basically, we are expecting a parameter that matches the name of one of our stylesheet files; if it doesn't, we'll render our default theme.&lt;/p&gt;

&lt;p&gt;Now, we just need to call this function in our main layout &lt;code&gt;application.html.erb&lt;/code&gt;, by replacing the string that defines the stylesheet we want to use.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Instead of a url parameter, we could eventually have a button in the UI, save the selection in the session and read from it in the &lt;code&gt;helper_method&lt;/code&gt; we just created.&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;head&amp;gt;&lt;/span&gt;
  &lt;span class="c"&gt;&amp;lt;!-- ... --&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="err"&gt;%=&lt;/span&gt; &lt;span class="na"&gt;stylesheet_link_tag&lt;/span&gt; &lt;span class="na"&gt;current_theme&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt; &lt;span class="err"&gt;"&lt;/span&gt;&lt;span class="na"&gt;data-turbo-track&lt;/span&gt;&lt;span class="err"&gt;"&lt;/span&gt;&lt;span class="na"&gt;:&lt;/span&gt; &lt;span class="err"&gt;"&lt;/span&gt;&lt;span class="na"&gt;reload&lt;/span&gt;&lt;span class="err"&gt;"&lt;/span&gt; &lt;span class="err"&gt;%&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
 &lt;span class="c"&gt;&amp;lt;!-- ... --&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/head&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you look at your Network tab in the browser's inspector, you will see that only one file is downloaded and not all of them.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fp8lfw6j4a21ikcz9kvz8.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fp8lfw6j4a21ikcz9kvz8.gif" alt="Showcase of multiple teams in a gif" width="760" height="388"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Happy Coding :)&lt;/em&gt;&lt;/p&gt;

</description>
      <category>rails</category>
      <category>tutorial</category>
      <category>webdev</category>
      <category>frontend</category>
    </item>
    <item>
      <title>1 Modal to Rule them All: Rails x Turbo x Stimulus</title>
      <dc:creator>hersoftsyntax</dc:creator>
      <pubDate>Sat, 20 Sep 2025 18:23:57 +0000</pubDate>
      <link>https://forem.com/hersoftsyntax/1-modal-to-rule-them-all-rails-x-turbo-x-stimulus-ln8</link>
      <guid>https://forem.com/hersoftsyntax/1-modal-to-rule-them-all-rails-x-turbo-x-stimulus-ln8</guid>
      <description>&lt;p&gt;When implementing modals, it’s common to create them individually in many different places. This can lead to repetitive code cluttering the &lt;strong&gt;DOM&lt;/strong&gt;. One good approach would be to use a strategy similar to flash messages: have just &lt;strong&gt;one modal element&lt;/strong&gt; and use the &lt;a href="https://hotwired.dev/" rel="noopener noreferrer"&gt;Hotwire ecosystem&lt;/a&gt;(Turbo + Stimulus) to dynamically and reactively change its content.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhut2rlvdmdyl4d829fa2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhut2rlvdmdyl4d829fa2.png" alt="captionless image" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Dependencies needed
&lt;/h2&gt;

&lt;p&gt;I will be assuming you have everything bellow installed and working as intended.&lt;/p&gt;

&lt;p&gt;Gems: &lt;code&gt;rails +8&lt;/code&gt; , &lt;code&gt;turbo-rails +2.0&lt;/code&gt; , &lt;code&gt;stimulus-rails +1.3&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Packages: &lt;code&gt;@hotwired/stimulus +3.2&lt;/code&gt; , &lt;code&gt;@hotwired/turbo-rails +8.0&lt;/code&gt; , &lt;code&gt;bootstrap +5.3&lt;/code&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Create a modal with dynamic content
&lt;/h2&gt;

&lt;p&gt;Let's just create a simple modal, with a &lt;code&gt;turbo_frame_tag&lt;/code&gt; wrapping your &lt;code&gt;body&lt;/code&gt; content with a simple bootstrap close button&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="c"&gt;&amp;lt;!-- views/shared/_modal.html.erb --&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;"modal fade"&lt;/span&gt;&lt;span class="nt"&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;"modal-dialog modal-dialog-centered"&lt;/span&gt;&lt;span class="nt"&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;"modal-content d-inline rounded-4"&lt;/span&gt;&lt;span class="nt"&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;"modal-body p-4"&lt;/span&gt;&lt;span class="nt"&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;"position-absolute end-0 px-4"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"btn-close"&lt;/span&gt; &lt;span class="na"&gt;data-bs-dismiss=&lt;/span&gt;&lt;span class="s"&gt;"modal"&lt;/span&gt; &lt;span class="na"&gt;aria-label=&lt;/span&gt;&lt;span class="s"&gt;"Close"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="err"&gt;%=&lt;/span&gt; &lt;span class="na"&gt;turbo_frame_tag&lt;/span&gt; &lt;span class="err"&gt;"&lt;/span&gt;&lt;span class="na"&gt;modal-body&lt;/span&gt;&lt;span class="err"&gt;"&lt;/span&gt; &lt;span class="na"&gt;do&lt;/span&gt; &lt;span class="err"&gt;%&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="err"&gt;%&lt;/span&gt; &lt;span class="na"&gt;end&lt;/span&gt; &lt;span class="err"&gt;%&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and render it in your application's layout&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="o"&gt;&amp;lt;!--&lt;/span&gt; &lt;span class="n"&gt;views&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;layouts&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;application&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;html&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;erb&lt;/span&gt; &lt;span class="o"&gt;--&amp;gt;&lt;/span&gt;
&lt;span class="o"&gt;&amp;lt;!--&lt;/span&gt; &lt;span class="o"&gt;...&lt;/span&gt; &lt;span class="o"&gt;--&amp;gt;&lt;/span&gt;
&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sx"&gt;%= render "shared/modal" %&amp;gt;
  &amp;lt;%=&lt;/span&gt; &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="sx"&gt;%&amp;gt;
&amp;lt;/body&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, in routes, inside your controller you need to add a new route responsible for updating the modal content.&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;# routes.rb &lt;/span&gt;
&lt;span class="n"&gt;resources&lt;/span&gt; &lt;span class="ss"&gt;:modal_test&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;only: &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="ss"&gt;:index&lt;/span&gt; &lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;collection&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;get&lt;/span&gt; &lt;span class="ss"&gt;:modal&lt;/span&gt; &lt;span class="c1"&gt;#new endpoint&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;p&gt;In your controller create the matching method&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;# controllers/modal_test_controller.rb&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ModalTestController&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="c1"&gt;# ...&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;modal&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;p&gt;Let's create the content for your modal's body&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="c"&gt;&amp;lt;!-- views/modal_test/_modal_body.html.erb --&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;div&amp;gt;&lt;/span&gt;
  Modal Content A
&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then we need a turbo stream file matching the method we just created in our controller, which will be responsible for updating the body content on the turbo frame we added in our &lt;code&gt;_modal.html.erb&lt;/code&gt;.&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="o"&gt;&amp;lt;!--&lt;/span&gt; &lt;span class="n"&gt;views&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;modal_test&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;modal&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;turbo_stream&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;erb&lt;/span&gt; &lt;span class="o"&gt;--&amp;gt;&lt;/span&gt;
&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sx"&gt;%= turbo_stream.update("modal-body", partial: "modal_test/modal_body") %&amp;gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now you just need to add the button to trigger the modal&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="c"&gt;&amp;lt;!-- views/modal_test/index.html.erb --&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;"container d-flex  flex-column  justify-content-center align-items-center mt-5"&lt;/span&gt; &lt;span class="nt"&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;"d-flex flex-column justify-content-center align-items-center"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
   &lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="err"&gt;%=&lt;/span&gt; &lt;span class="na"&gt;form_tag&lt;/span&gt;&lt;span class="err"&gt;(&lt;/span&gt;&lt;span class="na"&gt;modal_modal_test_index_path&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt; &lt;span class="na"&gt;method:&lt;/span&gt; &lt;span class="na"&gt;:get&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt; &lt;span class="na"&gt;class:&lt;/span&gt; &lt;span class="err"&gt;"&lt;/span&gt;&lt;span class="na"&gt;d-inline-block&lt;/span&gt;&lt;span class="err"&gt;",&lt;/span&gt; &lt;span class="na"&gt;data:&lt;/span&gt; &lt;span class="err"&gt;{&lt;/span&gt; &lt;span class="na"&gt;turbo_frame:&lt;/span&gt; &lt;span class="err"&gt;"&lt;/span&gt;&lt;span class="na"&gt;modal-body&lt;/span&gt;&lt;span class="err"&gt;",&lt;/span&gt; &lt;span class="na"&gt;turbo_stream:&lt;/span&gt; &lt;span class="na"&gt;true&lt;/span&gt; &lt;span class="err"&gt;})&lt;/span&gt; &lt;span class="na"&gt;do&lt;/span&gt; &lt;span class="err"&gt;%&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
       &lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="err"&gt;%=&lt;/span&gt; &lt;span class="na"&gt;button_tag&lt;/span&gt; &lt;span class="err"&gt;"&lt;/span&gt;&lt;span class="na"&gt;Modal&lt;/span&gt; &lt;span class="na"&gt;A&lt;/span&gt;&lt;span class="err"&gt;",&lt;/span&gt; &lt;span class="na"&gt;type:&lt;/span&gt; &lt;span class="na"&gt;:submit&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt; &lt;span class="na"&gt;class:&lt;/span&gt; &lt;span class="err"&gt;"&lt;/span&gt;&lt;span class="na"&gt;btn&lt;/span&gt; &lt;span class="na"&gt;btn-primary&lt;/span&gt; &lt;span class="na"&gt;px-4&lt;/span&gt; &lt;span class="na"&gt;py-2&lt;/span&gt;&lt;span class="err"&gt;"&lt;/span&gt; &lt;span class="err"&gt;%&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
   &lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="err"&gt;%&lt;/span&gt; &lt;span class="na"&gt;end&lt;/span&gt; &lt;span class="err"&gt;%&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To make the interaction happen we need to build a stimulus controller. Let's call it &lt;code&gt;modal_controller.js&lt;/code&gt; to keep the semantics, and import the necessary classes from &lt;code&gt;stimulus&lt;/code&gt;and &lt;code&gt;bootstrap&lt;/code&gt; libraries&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;// app/javascript/controllers/modal_controller.js&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Controller&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@hotwired/stimulus&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Modal&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;bootstrap&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&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="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;Don't forget to add it on the&lt;/em&gt; &lt;code&gt;_app/javascript/controllers/index.js_&lt;/code&gt; &lt;em&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Now let's add our &lt;code&gt;connect&lt;/code&gt; function and create our modal object that will be invoking this controller (which we haven't done yet) and add 2 listeners.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;code&gt;turbo:before-stream-render&lt;/code&gt; to listen to turbo changes before rendering it&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;&lt;em&gt;Why&lt;/em&gt;&lt;/strong&gt; &lt;code&gt;**_turbo:before-stream-render_**&lt;/code&gt; &lt;strong&gt;&lt;em&gt;?&lt;/em&gt;&lt;/strong&gt;_&lt;br&gt;
We are triggering the modal and the turbo action at the same time, which can lead to race conditions. Without this, you will be opening the modal with empty content before the render happens. The goal here is to open the modal only when the render actually happens. We will be processing turbo stream actions, not navigations, so for instance_&lt;br&gt;
&lt;code&gt;turbo:*-frame-*&lt;/code&gt; &lt;em&gt;in the documentation says:&lt;/em&gt; "Turbo Frames emit events during their &lt;strong&gt;navigation life cycle&lt;/strong&gt;"_&lt;br&gt;
Listening to the selected event is the only option we have for streams close to when they are triggered._&lt;/p&gt;
&lt;/blockquote&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;code&gt;hidden.bs.modal&lt;/code&gt; to look at bootstrap closing modal event
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// app/javascript/controllers/modal_controller.js&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Controller&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@hotwired/stimulus&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Modal&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;bootstrap&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&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="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;modal&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Modal&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="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;turbo:before-stream-render&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="cm"&gt;/*some function A*/&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="nf"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;hidden.bs.modal&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="cm"&gt;/*some function B*/&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="nf"&gt;disconnect&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;removeEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;turbo:before-stream-render&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="cm"&gt;/*some function A*/&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="nf"&gt;removeEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;hidden.bs.modal&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="cm"&gt;/*some function B*/&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;We need to identify the modal by adding a dynamic ID as a turbo value and initialise it. This will allow us to have multiple modals in the same page and also to execute this code restrictedly for those modals only.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;This controller will be instantiated once for&lt;/em&gt; &lt;strong&gt;&lt;em&gt;each distinct modal&lt;/em&gt;&lt;/strong&gt; &lt;em&gt;rendered on the page. So if you’re including multiple modals with different layouts or IDs, each will have its own instance of the controller. We’ll address this behaviour later in the article.&lt;/em&gt;&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;// app/javascript/controllers/modal_controller.js&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="c1"&gt;// new value to store the frameId aka the body id of the modal&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;frameId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&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;modal&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Modal&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="c1"&gt;// store the reference to the modal&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;frame&lt;/span&gt; &lt;span class="o"&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;hasFrameIdValue&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getElementById&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;frameIdValue&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; 

        &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;turbo:before-stream-render&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="cm"&gt;/*some function A*/&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="nf"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;hidden.bs.modal&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="cm"&gt;/*some function B*/&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="c1"&gt;// ...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We can go back to our modal and make sure we call this controller and add the &lt;code&gt;frameId&lt;/code&gt; value matching the frame tag from the body as well.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="c"&gt;&amp;lt;!-- views/shared/_modal.html.erb --&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;"modal fade"&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"modal"&lt;/span&gt; &lt;span class="na"&gt;tabindex=&lt;/span&gt;&lt;span class="s"&gt;"-1"&lt;/span&gt; &lt;span class="na"&gt;data-controller=&lt;/span&gt;&lt;span class="s"&gt;"modal"&lt;/span&gt; &lt;span class="na"&gt;data-turbo-permanent&lt;/span&gt; &lt;span class="na"&gt;data-modal-frame-id-value=&lt;/span&gt;&lt;span class="s"&gt;"modal-body"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
 &lt;span class="c"&gt;&amp;lt;!-- ... --&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;data-turbo-permanent:&lt;/strong&gt; this allows the modal to be permanent throughout the pages and ignore turbo navigations — avoiding duplications.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Now we need to build the function that will look at stream changes and toggle the modal via Bootstrap API. And we can also create the function to reset the frame's content once the modal is closed.&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;// app/javascript/controllers/modal_controller.js&lt;/span&gt;
&lt;span class="c1"&gt;// update connect&lt;/span&gt;
&lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;turbo:before-stream-render&lt;/span&gt;&lt;span class="dl"&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;toggleModalOnStreamRender&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;bind&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="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="nf"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;hidden.bs.modal&lt;/span&gt;&lt;span class="dl"&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;resetContent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;bind&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="c1"&gt;// update disconnect&lt;/span&gt;
 &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;removeEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;turbo:before-stream-render&lt;/span&gt;&lt;span class="dl"&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;toggleModalOnStreamRender&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;bind&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="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="nf"&gt;removeEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;hidden.bs.modal&lt;/span&gt;&lt;span class="dl"&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;resetContent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;bind&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="c1"&gt;// new function&lt;/span&gt;
&lt;span class="nx"&gt;toggleModalOnStreamRender&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// get the target name&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;renderedFrame&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;detail&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;newStream&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; 
    &lt;span class="c1"&gt;// check if it maches with the frameId of the modal&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;renderedFrame&lt;/span&gt; &lt;span class="o"&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;frameIdValue&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&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;modal&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;show&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="c1"&gt;// new function&lt;/span&gt;
&lt;span class="nx"&gt;resetContent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if &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;frame&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;frame&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;innerHTML&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&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;h2&gt;
  
  
   Close modal on submit:turbo action
&lt;/h2&gt;

&lt;p&gt;Modals are typically used to perform an action — such as submitting a form — and are then closed automatically once the controller processes the request. If the form submission leads to a full page refresh, there’s usually no need to handle the modal closing manually. However, in many cases, you may be using Turbo Streams to update part of the page dynamically after the modal submits, in which case you’ll need to explicitly handle closing the modal in your response.&lt;/p&gt;

&lt;p&gt;For this approach, we will make the &lt;strong&gt;submit action to trigger the turbo event&lt;/strong&gt; we have been listening in the stimulus controller ( &lt;code&gt;turbo:before-stream-render&lt;/code&gt;). Let's add a button inside the body content we want to render in the modal, which will call a new action from our 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="c1"&gt;# routes.rb &lt;/span&gt;
&lt;span class="n"&gt;resources&lt;/span&gt; &lt;span class="ss"&gt;:modal_test&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;only: &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="ss"&gt;:index&lt;/span&gt; &lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;collection&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;get&lt;/span&gt; &lt;span class="ss"&gt;:modal&lt;/span&gt;
    &lt;span class="n"&gt;post&lt;/span&gt; &lt;span class="ss"&gt;:modal_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;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# controllers/modal_test_controller.rb&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="o"&gt;...&lt;/span&gt;
  &lt;span class="c1"&gt;# ...&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;modal_action&lt;/span&gt;
    &lt;span class="vi"&gt;@some_parameter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:some_parameter&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; 
  &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="c1"&gt;# ...&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="c"&gt;&amp;lt;!-- views/modal_test/_modal_body.html.erb --&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;div&amp;gt;&lt;/span&gt;
  Modal content A
  &lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="err"&gt;%=&lt;/span&gt; &lt;span class="na"&gt;button_to&lt;/span&gt; &lt;span class="err"&gt;"&lt;/span&gt;&lt;span class="na"&gt;Yes&lt;/span&gt;&lt;span class="err"&gt;",&lt;/span&gt;
                &lt;span class="na"&gt;modal_action_modal_test_index_url&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt;
                &lt;span class="na"&gt;method:&lt;/span&gt; &lt;span class="na"&gt;:post&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt;
                &lt;span class="na"&gt;params:&lt;/span&gt; &lt;span class="err"&gt;{&lt;/span&gt; &lt;span class="na"&gt;some_parameter:&lt;/span&gt; &lt;span class="err"&gt;"&lt;/span&gt;&lt;span class="na"&gt;some_value_a&lt;/span&gt;&lt;span class="err"&gt;"&lt;/span&gt; &lt;span class="err"&gt;},&lt;/span&gt;
                &lt;span class="na"&gt;class:&lt;/span&gt; &lt;span class="err"&gt;"&lt;/span&gt;&lt;span class="na"&gt;btn&lt;/span&gt; &lt;span class="na"&gt;btn-primary&lt;/span&gt; &lt;span class="na"&gt;px-4&lt;/span&gt; &lt;span class="na"&gt;py-2&lt;/span&gt;&lt;span class="err"&gt;",&lt;/span&gt;
                &lt;span class="na"&gt;form_class:&lt;/span&gt; &lt;span class="err"&gt;"&lt;/span&gt;&lt;span class="na"&gt;d-inline-block&lt;/span&gt;&lt;span class="err"&gt;"&lt;/span&gt; &lt;span class="err"&gt;%&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Build action for modal submit
&lt;/h3&gt;

&lt;p&gt;Now we will create an isolated turbo stream to trigger the closing behaviour. This segregation allow us to call this close action in any controller responses where we are managing modals.&lt;/p&gt;

&lt;p&gt;This action will be responsible for appending a "meta" element which will trigger, again, &lt;code&gt;turbo:before-stream-render&lt;/code&gt; event. In this case, we need to add a flag(&lt;code&gt;data-close='true'&lt;/code&gt;) — by adding a custom attribute to the main element inside of this html. By doing this we can intercept it in our javascript function and react on it to close the modal.&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;# views/shared/_close_modal.turbo_stream.erb&lt;/span&gt;
&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sx"&gt;%= turbo_stream.append("&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;local_assigns&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:modal_body_id&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sx"&gt;", html: "&amp;lt;span data-close=&lt;/span&gt;&lt;span class="s1"&gt;'true'&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/span&amp;gt;".html_safe) %&amp;gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To make it work we need to adapt &lt;code&gt;toggleModalOnStreamRender&lt;/code&gt; in our stimulus controller in order to retrieve the custom attribute &lt;code&gt;data-close&lt;/code&gt; and whenever it's present we hide the modal.&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="nx"&gt;toggleModalOnStreamRender&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;renderedFrame&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;detail&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;newStream&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;renderedFrame&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="o"&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;frameIdValue&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="c1"&gt;// new line here to get the close-action signal&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;isCloseAction&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;renderedFrame&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;templateContent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;querySelector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;[data-close]&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)?.&lt;/span&gt;&lt;span class="nx"&gt;dataset&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;close&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="c1"&gt;// close modal if action is present&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;isCloseAction&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;modal&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;hide&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;else&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;modal&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;show&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;Lets add a frame in our index so we can see things changing after submitting the modal.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="c"&gt;&amp;lt;!-- views/modal_test/index.html.erb --&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;"container d-flex  flex-column  justify-content-center align-items-center mt-5"&lt;/span&gt; &lt;span class="nt"&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;"d-flex flex-column justify-content-center align-items-center"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
   &lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="err"&gt;%=&lt;/span&gt; &lt;span class="na"&gt;form_tag&lt;/span&gt;&lt;span class="err"&gt;(&lt;/span&gt;&lt;span class="na"&gt;modal_modal_test_index_path&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt; &lt;span class="na"&gt;method:&lt;/span&gt; &lt;span class="na"&gt;:get&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt; &lt;span class="na"&gt;class:&lt;/span&gt; &lt;span class="err"&gt;"&lt;/span&gt;&lt;span class="na"&gt;d-inline-block&lt;/span&gt;&lt;span class="err"&gt;",&lt;/span&gt; &lt;span class="na"&gt;data:&lt;/span&gt; &lt;span class="err"&gt;{&lt;/span&gt; &lt;span class="na"&gt;turbo_frame:&lt;/span&gt; &lt;span class="err"&gt;"&lt;/span&gt;&lt;span class="na"&gt;modal-body&lt;/span&gt;&lt;span class="err"&gt;",&lt;/span&gt; &lt;span class="na"&gt;turbo_stream:&lt;/span&gt; &lt;span class="na"&gt;true&lt;/span&gt; &lt;span class="err"&gt;})&lt;/span&gt; &lt;span class="na"&gt;do&lt;/span&gt; &lt;span class="err"&gt;%&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
     &lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="err"&gt;%=&lt;/span&gt; &lt;span class="na"&gt;button_tag&lt;/span&gt; &lt;span class="err"&gt;"&lt;/span&gt;&lt;span class="na"&gt;Modal&lt;/span&gt; &lt;span class="na"&gt;A&lt;/span&gt;&lt;span class="err"&gt;",&lt;/span&gt; &lt;span class="na"&gt;type:&lt;/span&gt; &lt;span class="na"&gt;:submit&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt; &lt;span class="na"&gt;class:&lt;/span&gt; &lt;span class="err"&gt;"&lt;/span&gt;&lt;span class="na"&gt;btn&lt;/span&gt; &lt;span class="na"&gt;btn-primary&lt;/span&gt; &lt;span class="na"&gt;px-4&lt;/span&gt; &lt;span class="na"&gt;py-2&lt;/span&gt;&lt;span class="err"&gt;"&lt;/span&gt; &lt;span class="err"&gt;%&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
   &lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="err"&gt;%&lt;/span&gt; &lt;span class="na"&gt;end&lt;/span&gt; &lt;span class="err"&gt;%&lt;/span&gt;&lt;span class="nt"&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;!-- New frame--&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;div&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;h3&amp;gt;&lt;/span&gt; Action update: &lt;span class="nt"&gt;&amp;lt;/h3&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="err"&gt;%=&lt;/span&gt; &lt;span class="na"&gt;turbo_frame_tag&lt;/span&gt; &lt;span class="err"&gt;"&lt;/span&gt;&lt;span class="na"&gt;action-change-modal&lt;/span&gt;&lt;span class="err"&gt;"&lt;/span&gt;  &lt;span class="na"&gt;do&lt;/span&gt;&lt;span class="err"&gt;%&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="err"&gt;%&lt;/span&gt; &lt;span class="na"&gt;end&lt;/span&gt; &lt;span class="err"&gt;%&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Finally, our controller needs to return a Turbo Stream response from the &lt;code&gt;modal_action&lt;/code&gt; method (triggered by the button inside the modal). This response should both update the target frame on the page and trigger the modal to close.&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;# controllers/model_test/modal_action.turbo_stream.erb&lt;/span&gt;
&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sx"&gt;%= turbo_stream.update("action-change-modal", html: "&amp;lt;span&amp;gt; &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="vi"&gt;@some_parameter&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sx"&gt;&amp;lt;/span&amp;gt;".html_safe) %&amp;gt;
&amp;lt;%=&lt;/span&gt; &lt;span class="n"&gt;render&lt;/span&gt; &lt;span class="s2"&gt;"shared/close_modal"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="ss"&gt;modal_body_id: &lt;/span&gt;&lt;span class="s2"&gt;"modal-body"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;%&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Multiple places calling the same modal
&lt;/h2&gt;

&lt;p&gt;Let's say that now, you have another use case where a modal is needed. Instead implementing it from scratch, you just need to reuse the same approach, like this:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  Create a new partial with the expected content of this new use case
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="c"&gt;&amp;lt;!-- views/modal_test/_modal_body_b.html.erb --&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;div&amp;gt;&lt;/span&gt;
  Modal content B
  &lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="err"&gt;%=&lt;/span&gt; &lt;span class="na"&gt;button_to&lt;/span&gt; &lt;span class="err"&gt;"&lt;/span&gt;&lt;span class="na"&gt;Yes&lt;/span&gt;&lt;span class="err"&gt;",&lt;/span&gt;
                &lt;span class="na"&gt;modal_action_modal_test_index_url&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt;
                &lt;span class="na"&gt;method:&lt;/span&gt; &lt;span class="na"&gt;:post&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt;
                &lt;span class="na"&gt;params:&lt;/span&gt; &lt;span class="err"&gt;{&lt;/span&gt; &lt;span class="na"&gt;some_parameter:&lt;/span&gt; &lt;span class="err"&gt;"&lt;/span&gt;&lt;span class="na"&gt;some_value_b&lt;/span&gt;&lt;span class="err"&gt;"&lt;/span&gt; &lt;span class="err"&gt;},&lt;/span&gt;
                &lt;span class="na"&gt;class:&lt;/span&gt; &lt;span class="err"&gt;"&lt;/span&gt;&lt;span class="na"&gt;btn&lt;/span&gt; &lt;span class="na"&gt;btn-primary&lt;/span&gt; &lt;span class="na"&gt;px-4&lt;/span&gt; &lt;span class="na"&gt;py-2&lt;/span&gt;&lt;span class="err"&gt;",&lt;/span&gt;
                &lt;span class="na"&gt;form_class:&lt;/span&gt; &lt;span class="err"&gt;"&lt;/span&gt;&lt;span class="na"&gt;d-inline-block&lt;/span&gt;&lt;span class="err"&gt;"&lt;/span&gt; &lt;span class="err"&gt;%&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;  Adapt controller turbo responses or add a new controller method&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;On the controller side, you can either use the same endpoint if you are on the same page or adding a new route to be called by the turbo-action.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Following the first approach,&lt;/strong&gt; you can send a value within the buttons identifying which desirable content our controller needs to call to render inside the modal.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="err"&gt;%=&lt;/span&gt; &lt;span class="na"&gt;form_tag&lt;/span&gt;&lt;span class="err"&gt;(&lt;/span&gt;&lt;span class="na"&gt;modal_modal_test_index_path&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt; &lt;span class="na"&gt;method:&lt;/span&gt; &lt;span class="na"&gt;:get&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt; &lt;span class="na"&gt;class:&lt;/span&gt; &lt;span class="err"&gt;"&lt;/span&gt;&lt;span class="na"&gt;d-inline-block&lt;/span&gt;&lt;span class="err"&gt;",&lt;/span&gt; &lt;span class="na"&gt;data:&lt;/span&gt; &lt;span class="err"&gt;{&lt;/span&gt; &lt;span class="na"&gt;turbo_frame:&lt;/span&gt; &lt;span class="err"&gt;"&lt;/span&gt;&lt;span class="na"&gt;modal-body&lt;/span&gt;&lt;span class="err"&gt;",&lt;/span&gt; &lt;span class="na"&gt;turbo_stream:&lt;/span&gt; &lt;span class="na"&gt;true&lt;/span&gt; &lt;span class="err"&gt;})&lt;/span&gt; &lt;span class="na"&gt;do&lt;/span&gt; &lt;span class="err"&gt;%&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="err"&gt;%=&lt;/span&gt; &lt;span class="na"&gt;button_tag&lt;/span&gt; &lt;span class="err"&gt;"&lt;/span&gt;&lt;span class="na"&gt;Modal&lt;/span&gt; &lt;span class="na"&gt;A&lt;/span&gt;&lt;span class="err"&gt;",&lt;/span&gt; &lt;span class="na"&gt;name:&lt;/span&gt; &lt;span class="err"&gt;"&lt;/span&gt;&lt;span class="na"&gt;modal_type&lt;/span&gt;&lt;span class="err"&gt;",&lt;/span&gt; &lt;span class="na"&gt;type:&lt;/span&gt; &lt;span class="na"&gt;:submit&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt; &lt;span class="na"&gt;value:&lt;/span&gt; &lt;span class="err"&gt;"&lt;/span&gt;&lt;span class="na"&gt;modal_a&lt;/span&gt;&lt;span class="err"&gt;",&lt;/span&gt; &lt;span class="na"&gt;class:&lt;/span&gt; &lt;span class="err"&gt;"&lt;/span&gt;&lt;span class="na"&gt;btn&lt;/span&gt; &lt;span class="na"&gt;btn-primary&lt;/span&gt; &lt;span class="na"&gt;px-4&lt;/span&gt; &lt;span class="na"&gt;py-2&lt;/span&gt;&lt;span class="err"&gt;"&lt;/span&gt; &lt;span class="err"&gt;%&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="err"&gt;%=&lt;/span&gt; &lt;span class="na"&gt;button_tag&lt;/span&gt; &lt;span class="err"&gt;"&lt;/span&gt;&lt;span class="na"&gt;Modal&lt;/span&gt; &lt;span class="na"&gt;B&lt;/span&gt;&lt;span class="err"&gt;",&lt;/span&gt; &lt;span class="na"&gt;name:&lt;/span&gt; &lt;span class="err"&gt;"&lt;/span&gt;&lt;span class="na"&gt;modal_type&lt;/span&gt;&lt;span class="err"&gt;",&lt;/span&gt; &lt;span class="na"&gt;type:&lt;/span&gt; &lt;span class="na"&gt;:submit&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt; &lt;span class="na"&gt;value:&lt;/span&gt; &lt;span class="err"&gt;"&lt;/span&gt;&lt;span class="na"&gt;modal_b&lt;/span&gt;&lt;span class="err"&gt;",&lt;/span&gt; &lt;span class="na"&gt;class:&lt;/span&gt; &lt;span class="err"&gt;"&lt;/span&gt;&lt;span class="na"&gt;btn&lt;/span&gt; &lt;span class="na"&gt;btn-warning&lt;/span&gt; &lt;span class="na"&gt;px-4&lt;/span&gt; &lt;span class="na"&gt;py-2&lt;/span&gt;&lt;span class="err"&gt;"&lt;/span&gt; &lt;span class="err"&gt;%&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="err"&gt;%&lt;/span&gt; &lt;span class="na"&gt;end&lt;/span&gt; &lt;span class="err"&gt;%&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# controllers/model_test_controller.rb&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ModalTestController&lt;/span&gt;
  &lt;span class="c1"&gt;# ...&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;modal&lt;/span&gt;
    &lt;span class="vi"&gt;@modal_type&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:modal_type&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="c1"&gt;#...&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# views/modal_test/modal.turbo_stream.erb&lt;/span&gt;
&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sx"&gt;% modal_type_partial &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="vi"&gt;@modal_type&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;"modal_a"&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="s2"&gt;"modal_body_a"&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"modal_body_b"&lt;/span&gt;  &lt;span class="o"&gt;%&amp;gt;&lt;/span&gt;
&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sx"&gt;%= turbo_stream.update("modal-body", partial: "modal_test/&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;modal_type_partial&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sx"&gt;") %&amp;gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;…or by if inside a new controller (because it might be in a different page)…&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="err"&gt;%=&lt;/span&gt; &lt;span class="na"&gt;form_tag&lt;/span&gt;&lt;span class="err"&gt;(&lt;/span&gt;&lt;span class="na"&gt;modal_modal_test_index_path&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt; &lt;span class="na"&gt;method:&lt;/span&gt; &lt;span class="na"&gt;:get&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt; &lt;span class="na"&gt;class:&lt;/span&gt; &lt;span class="err"&gt;"&lt;/span&gt;&lt;span class="na"&gt;d-inline-block&lt;/span&gt;&lt;span class="err"&gt;",&lt;/span&gt; &lt;span class="na"&gt;data:&lt;/span&gt; &lt;span class="err"&gt;{&lt;/span&gt; &lt;span class="na"&gt;turbo_frame:&lt;/span&gt; &lt;span class="err"&gt;"&lt;/span&gt;&lt;span class="na"&gt;modal-body&lt;/span&gt;&lt;span class="err"&gt;",&lt;/span&gt; &lt;span class="na"&gt;turbo_stream:&lt;/span&gt; &lt;span class="na"&gt;true&lt;/span&gt; &lt;span class="err"&gt;})&lt;/span&gt; &lt;span class="na"&gt;do&lt;/span&gt; &lt;span class="err"&gt;%&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
   &lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="err"&gt;%=&lt;/span&gt; &lt;span class="na"&gt;button_tag&lt;/span&gt; &lt;span class="err"&gt;"&lt;/span&gt;&lt;span class="na"&gt;Modal&lt;/span&gt; &lt;span class="na"&gt;A&lt;/span&gt;&lt;span class="err"&gt;",&lt;/span&gt; &lt;span class="na"&gt;type:&lt;/span&gt; &lt;span class="na"&gt;:submit&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt; &lt;span class="na"&gt;class:&lt;/span&gt; &lt;span class="err"&gt;"&lt;/span&gt;&lt;span class="na"&gt;btn&lt;/span&gt; &lt;span class="na"&gt;btn-primary&lt;/span&gt; &lt;span class="na"&gt;px-4&lt;/span&gt; &lt;span class="na"&gt;py-2&lt;/span&gt;&lt;span class="err"&gt;"&lt;/span&gt; &lt;span class="err"&gt;%&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="err"&gt;%&lt;/span&gt; &lt;span class="na"&gt;end&lt;/span&gt; &lt;span class="err"&gt;%&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="err"&gt;%=&lt;/span&gt; &lt;span class="na"&gt;form_tag&lt;/span&gt;&lt;span class="err"&gt;(&lt;/span&gt;&lt;span class="na"&gt;modal_different_controller_index_path&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt; &lt;span class="na"&gt;method:&lt;/span&gt; &lt;span class="na"&gt;:get&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt; &lt;span class="na"&gt;class:&lt;/span&gt; &lt;span class="err"&gt;"&lt;/span&gt;&lt;span class="na"&gt;d-inline-block&lt;/span&gt;&lt;span class="err"&gt;",&lt;/span&gt; &lt;span class="na"&gt;data:&lt;/span&gt; &lt;span class="err"&gt;{&lt;/span&gt; &lt;span class="na"&gt;turbo_frame:&lt;/span&gt; &lt;span class="err"&gt;"&lt;/span&gt;&lt;span class="na"&gt;modal-body&lt;/span&gt;&lt;span class="err"&gt;",&lt;/span&gt; &lt;span class="na"&gt;turbo_stream:&lt;/span&gt; &lt;span class="na"&gt;true&lt;/span&gt; &lt;span class="err"&gt;})&lt;/span&gt; &lt;span class="na"&gt;do&lt;/span&gt; &lt;span class="err"&gt;%&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
   # hidden_tag ...
   &lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="err"&gt;%=&lt;/span&gt; &lt;span class="na"&gt;button_tag&lt;/span&gt; &lt;span class="err"&gt;"&lt;/span&gt;&lt;span class="na"&gt;Modal&lt;/span&gt; &lt;span class="na"&gt;B&lt;/span&gt;&lt;span class="err"&gt;",&lt;/span&gt; &lt;span class="na"&gt;type:&lt;/span&gt; &lt;span class="na"&gt;:submit&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt; &lt;span class="na"&gt;class:&lt;/span&gt; &lt;span class="err"&gt;"&lt;/span&gt;&lt;span class="na"&gt;btn&lt;/span&gt; &lt;span class="na"&gt;btn-warning&lt;/span&gt; &lt;span class="na"&gt;px-4&lt;/span&gt; &lt;span class="na"&gt;py-2&lt;/span&gt;&lt;span class="err"&gt;"&lt;/span&gt; &lt;span class="err"&gt;%&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="err"&gt;%&lt;/span&gt; &lt;span class="na"&gt;end&lt;/span&gt; &lt;span class="err"&gt;%&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# views/modal_test/modal.turbo_stream.erb&lt;/span&gt;
&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sx"&gt;%= turbo_stream.update("modal-body", partial: "modal_test/modal_body_a") %&amp;gt;

# views/different_controller/modal.turbo_stream.erb
&amp;lt;%=&lt;/span&gt; &lt;span class="n"&gt;turbo_stream&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"modal-body"&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;"modal_test/modal_body_b"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;%&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# controllers/model_test_controller.rb&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ModalTest&lt;/span&gt;
  &lt;span class="c1"&gt;# ...&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;modal&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="c1"&gt;#...&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="c1"&gt;# controllers/different_controller.rb&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;DifferentController&lt;/span&gt;
  &lt;span class="c1"&gt;# ...&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;modal&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="c1"&gt;#...&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Add a new modal with a different layout
&lt;/h2&gt;

&lt;p&gt;Let's say now your designer decides to create a different layout that you can't manipulate in your original one. We built the foundation for our modals, so now we just need to recreate a few steps.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; Create the baseline of your modal, with a different ID in our &lt;code&gt;frameId&lt;/code&gt; matching the same ID as the body
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&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;"modal fade"&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"different-modal"&lt;/span&gt; &lt;span class="na"&gt;tabindex=&lt;/span&gt;&lt;span class="s"&gt;"-1"&lt;/span&gt; &lt;span class="na"&gt;data-controller=&lt;/span&gt;&lt;span class="s"&gt;"modal"&lt;/span&gt; &lt;span class="na"&gt;data-turbo-permanent&lt;/span&gt; &lt;span class="na"&gt;data-modal-frame-id-value=&lt;/span&gt;&lt;span class="s"&gt;"modal-different-layout-body"&lt;/span&gt;&lt;span class="nt"&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;"modal-dialog modal-dialog-centered"&lt;/span&gt;&lt;span class="nt"&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;"modal-content d-inline rounded-5"&lt;/span&gt;&lt;span class="nt"&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;"modal-header bg-warning rounded-top-5"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        Title
        &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;"position-absolute end-0 px-4"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"btn-close"&lt;/span&gt; &lt;span class="na"&gt;data-bs-dismiss=&lt;/span&gt;&lt;span class="s"&gt;"modal"&lt;/span&gt; &lt;span class="na"&gt;aria-label=&lt;/span&gt;&lt;span class="s"&gt;"Close"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;/div&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;"modal-body p-4 d-flex flex-column align-items-center"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        This is a modal with a different layout
        &lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="err"&gt;%=&lt;/span&gt; &lt;span class="na"&gt;turbo_frame_tag&lt;/span&gt; &lt;span class="err"&gt;"&lt;/span&gt;&lt;span class="na"&gt;modal-different-layout-body&lt;/span&gt;&lt;span class="err"&gt;"&lt;/span&gt; &lt;span class="na"&gt;do&lt;/span&gt; &lt;span class="err"&gt;%&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="err"&gt;%&lt;/span&gt; &lt;span class="na"&gt;end&lt;/span&gt; &lt;span class="err"&gt;%&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Render it on the application layout
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;body&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="err"&gt;%=&lt;/span&gt; &lt;span class="na"&gt;render&lt;/span&gt; &lt;span class="err"&gt;"&lt;/span&gt;&lt;span class="na"&gt;shared&lt;/span&gt;&lt;span class="err"&gt;/&lt;/span&gt;&lt;span class="na"&gt;modal&lt;/span&gt;&lt;span class="err"&gt;"&lt;/span&gt; &lt;span class="err"&gt;%&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="err"&gt;%=&lt;/span&gt; &lt;span class="na"&gt;render&lt;/span&gt; &lt;span class="err"&gt;"&lt;/span&gt;&lt;span class="na"&gt;shared&lt;/span&gt;&lt;span class="err"&gt;/&lt;/span&gt;&lt;span class="na"&gt;modal_different_layout&lt;/span&gt;&lt;span class="err"&gt;"&lt;/span&gt; &lt;span class="err"&gt;%&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="err"&gt;%=&lt;/span&gt; &lt;span class="na"&gt;yield&lt;/span&gt; &lt;span class="err"&gt;%&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/body&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Create the body content (in this example we are pointing to the original controller but it could be anything)
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="c"&gt;&amp;lt;!-- views/modal_test/modal_body_different_layout.html.erb --&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;div&amp;gt;&lt;/span&gt;
  Different Layout
  &lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="err"&gt;%=&lt;/span&gt; &lt;span class="na"&gt;button_to&lt;/span&gt; &lt;span class="err"&gt;"&lt;/span&gt;&lt;span class="na"&gt;Yes&lt;/span&gt;&lt;span class="err"&gt;",&lt;/span&gt;
                &lt;span class="na"&gt;modal_action_modal_test_index_url&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt;
                &lt;span class="na"&gt;method:&lt;/span&gt; &lt;span class="na"&gt;:post&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt;
                &lt;span class="na"&gt;params:&lt;/span&gt; &lt;span class="err"&gt;{&lt;/span&gt; &lt;span class="na"&gt;some_parameter:&lt;/span&gt; &lt;span class="err"&gt;"&lt;/span&gt;&lt;span class="na"&gt;some_value_different&lt;/span&gt;&lt;span class="err"&gt;"&lt;/span&gt; &lt;span class="err"&gt;},&lt;/span&gt;
                &lt;span class="na"&gt;class:&lt;/span&gt; &lt;span class="err"&gt;"&lt;/span&gt;&lt;span class="na"&gt;btn&lt;/span&gt; &lt;span class="na"&gt;btn-primary&lt;/span&gt; &lt;span class="na"&gt;px-4&lt;/span&gt; &lt;span class="na"&gt;py-2&lt;/span&gt;&lt;span class="err"&gt;",&lt;/span&gt;
                &lt;span class="na"&gt;form_class:&lt;/span&gt; &lt;span class="err"&gt;"&lt;/span&gt;&lt;span class="na"&gt;d-inline-block&lt;/span&gt;&lt;span class="err"&gt;"&lt;/span&gt; &lt;span class="err"&gt;%&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Add the button to open the new modal
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&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;"container d-flex  flex-column  justify-content-center align-items-center mt-5"&lt;/span&gt; &lt;span class="nt"&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;"d-flex flex-column justify-content-center align-items-center"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="err"&gt;%=&lt;/span&gt; &lt;span class="na"&gt;form_tag&lt;/span&gt;&lt;span class="err"&gt;(&lt;/span&gt;&lt;span class="na"&gt;modal_modal_test_index_path&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt; &lt;span class="na"&gt;method:&lt;/span&gt; &lt;span class="na"&gt;:get&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt; &lt;span class="na"&gt;class:&lt;/span&gt; &lt;span class="err"&gt;"&lt;/span&gt;&lt;span class="na"&gt;d-inline-block&lt;/span&gt;&lt;span class="err"&gt;",&lt;/span&gt; &lt;span class="na"&gt;data:&lt;/span&gt; &lt;span class="err"&gt;{&lt;/span&gt; &lt;span class="na"&gt;turbo_frame:&lt;/span&gt; &lt;span class="err"&gt;"&lt;/span&gt;&lt;span class="na"&gt;modal-body&lt;/span&gt;&lt;span class="err"&gt;",&lt;/span&gt; &lt;span class="na"&gt;turbo_stream:&lt;/span&gt; &lt;span class="na"&gt;true&lt;/span&gt; &lt;span class="err"&gt;})&lt;/span&gt; &lt;span class="na"&gt;do&lt;/span&gt; &lt;span class="err"&gt;%&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="err"&gt;%=&lt;/span&gt; &lt;span class="na"&gt;button_tag&lt;/span&gt; &lt;span class="err"&gt;"&lt;/span&gt;&lt;span class="na"&gt;Modal&lt;/span&gt; &lt;span class="na"&gt;A&lt;/span&gt;&lt;span class="err"&gt;",&lt;/span&gt; &lt;span class="na"&gt;name:&lt;/span&gt; &lt;span class="err"&gt;"&lt;/span&gt;&lt;span class="na"&gt;modal_type&lt;/span&gt;&lt;span class="err"&gt;",&lt;/span&gt; &lt;span class="na"&gt;type:&lt;/span&gt; &lt;span class="na"&gt;:submit&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt; &lt;span class="na"&gt;value:&lt;/span&gt; &lt;span class="err"&gt;"&lt;/span&gt;&lt;span class="na"&gt;modal_a&lt;/span&gt;&lt;span class="err"&gt;",&lt;/span&gt; &lt;span class="na"&gt;class:&lt;/span&gt; &lt;span class="err"&gt;"&lt;/span&gt;&lt;span class="na"&gt;btn&lt;/span&gt; &lt;span class="na"&gt;btn-primary&lt;/span&gt; &lt;span class="na"&gt;px-4&lt;/span&gt; &lt;span class="na"&gt;py-2&lt;/span&gt;&lt;span class="err"&gt;"&lt;/span&gt; &lt;span class="err"&gt;%&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="err"&gt;%=&lt;/span&gt; &lt;span class="na"&gt;button_tag&lt;/span&gt; &lt;span class="err"&gt;"&lt;/span&gt;&lt;span class="na"&gt;Modal&lt;/span&gt; &lt;span class="na"&gt;B&lt;/span&gt;&lt;span class="err"&gt;",&lt;/span&gt; &lt;span class="na"&gt;name:&lt;/span&gt; &lt;span class="err"&gt;"&lt;/span&gt;&lt;span class="na"&gt;modal_type&lt;/span&gt;&lt;span class="err"&gt;",&lt;/span&gt; &lt;span class="na"&gt;type:&lt;/span&gt; &lt;span class="na"&gt;:submit&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt; &lt;span class="na"&gt;value:&lt;/span&gt; &lt;span class="err"&gt;"&lt;/span&gt;&lt;span class="na"&gt;modal_b&lt;/span&gt;&lt;span class="err"&gt;",&lt;/span&gt; &lt;span class="na"&gt;class:&lt;/span&gt; &lt;span class="err"&gt;"&lt;/span&gt;&lt;span class="na"&gt;btn&lt;/span&gt; &lt;span class="na"&gt;btn-warning&lt;/span&gt; &lt;span class="na"&gt;px-4&lt;/span&gt; &lt;span class="na"&gt;py-2&lt;/span&gt;&lt;span class="err"&gt;"&lt;/span&gt; &lt;span class="err"&gt;%&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="err"&gt;%&lt;/span&gt; &lt;span class="na"&gt;end&lt;/span&gt; &lt;span class="err"&gt;%&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;

     &lt;span class="c"&gt;&amp;lt;!-- this step --&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="err"&gt;%=&lt;/span&gt; &lt;span class="na"&gt;form_tag&lt;/span&gt;&lt;span class="err"&gt;(&lt;/span&gt;&lt;span class="na"&gt;modal_different_layout_modal_test_index_url&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt; &lt;span class="na"&gt;method:&lt;/span&gt; &lt;span class="na"&gt;:get&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt; &lt;span class="na"&gt;class:&lt;/span&gt; &lt;span class="err"&gt;"&lt;/span&gt;&lt;span class="na"&gt;d-inline-block&lt;/span&gt; &lt;span class="na"&gt;mt-3&lt;/span&gt;&lt;span class="err"&gt;",&lt;/span&gt; &lt;span class="na"&gt;data:&lt;/span&gt; &lt;span class="err"&gt;{&lt;/span&gt; &lt;span class="na"&gt;turbo_frame:&lt;/span&gt; &lt;span class="err"&gt;"&lt;/span&gt;&lt;span class="na"&gt;modal-body&lt;/span&gt;&lt;span class="err"&gt;",&lt;/span&gt; &lt;span class="na"&gt;turbo_stream:&lt;/span&gt; &lt;span class="na"&gt;true&lt;/span&gt; &lt;span class="err"&gt;})&lt;/span&gt; &lt;span class="na"&gt;do&lt;/span&gt; &lt;span class="err"&gt;%&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="err"&gt;%=&lt;/span&gt; &lt;span class="na"&gt;button_tag&lt;/span&gt; &lt;span class="err"&gt;"&lt;/span&gt;&lt;span class="na"&gt;Modal&lt;/span&gt; &lt;span class="na"&gt;Different&lt;/span&gt; &lt;span class="na"&gt;Layout&lt;/span&gt;&lt;span class="err"&gt;",&lt;/span&gt; &lt;span class="na"&gt;type:&lt;/span&gt; &lt;span class="na"&gt;:submit&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt; &lt;span class="na"&gt;class:&lt;/span&gt; &lt;span class="err"&gt;"&lt;/span&gt;&lt;span class="na"&gt;btn&lt;/span&gt; &lt;span class="na"&gt;btn-danger&lt;/span&gt; &lt;span class="na"&gt;px-4&lt;/span&gt; &lt;span class="na"&gt;py-2&lt;/span&gt;&lt;span class="err"&gt;"&lt;/span&gt; &lt;span class="err"&gt;%&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="err"&gt;%&lt;/span&gt; &lt;span class="na"&gt;end&lt;/span&gt; &lt;span class="err"&gt;%&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;

  &lt;span class="nt"&gt;&amp;lt;div&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;h3&amp;gt;&lt;/span&gt; Action update: &lt;span class="nt"&gt;&amp;lt;/h3&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="err"&gt;%=&lt;/span&gt; &lt;span class="na"&gt;turbo_frame_tag&lt;/span&gt; &lt;span class="err"&gt;"&lt;/span&gt;&lt;span class="na"&gt;action-change-modal&lt;/span&gt;&lt;span class="err"&gt;"&lt;/span&gt;  &lt;span class="na"&gt;do&lt;/span&gt;&lt;span class="err"&gt;%&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="err"&gt;%&lt;/span&gt; &lt;span class="na"&gt;end&lt;/span&gt; &lt;span class="err"&gt;%&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Create an action on the controller side to render the content of that modal
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# controllers/model_test_controller.rb&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ModalTestC&lt;/span&gt;
  &lt;span class="c1"&gt;# ...&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;modal_different_layout&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="c1"&gt;#...&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# controllers/modal_different_layout.turbo_stream.rb&lt;/span&gt;
&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sx"&gt;%= turbo_stream.update("modal-different-layout-body", partial: "modal_test/modal_body_different_layout") %&amp;gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;If you are using turbo actions inside the modal, you just need to call the &lt;code&gt;close_modal&lt;/code&gt; partial done previously on this article.
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sx"&gt;%= turbo_stream.update("action-change-modal", html: "&amp;lt;span&amp;gt; &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="vi"&gt;@some_parameter&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sx"&gt;&amp;lt;/span&amp;gt;".html_safe) %&amp;gt;
&amp;lt;%=&lt;/span&gt; &lt;span class="n"&gt;render&lt;/span&gt; &lt;span class="s2"&gt;"shared/close_modal"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="ss"&gt;modal_body_id: &lt;/span&gt;&lt;span class="s2"&gt;"modal-body"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;%&amp;gt;&lt;/span&gt;
&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sx"&gt;%= render "shared/close_modal", { modal_body_id: "modal-different-layout-body" } %&amp;gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Everything together
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0d4ppuubd6hymk9frpg6.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0d4ppuubd6hymk9frpg6.gif" alt="captionless image" width="760" height="438"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Sequence diagram
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2ll3x2yvtr3asocrmnpf.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2ll3x2yvtr3asocrmnpf.png" alt="Diagram illustrating the modal approach (using Mermaid language)" width="800" height="613"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This is just a different way to handle modals, using turbo-streams. There's probably plenty of approaches you can apply accordingly with what suits you better on your use case.&lt;/p&gt;

&lt;p&gt;Feel free to share on the comments, what strategies do you apply to handle modals?&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Happy coding!&lt;/em&gt;&lt;/p&gt;

</description>
      <category>rails</category>
      <category>webdev</category>
      <category>ruby</category>
      <category>frontend</category>
    </item>
  </channel>
</rss>
