Upgrade to Rails 7. Javascript code refactoring

Hi! I’m working on upgrade my app from Rails 6 to Rails 7.
Currently my app uses js in 3 different ways:

  1. Small React App compiled by Webpack (app/javascript/packs)
  2. JS in assets app/assets/javascript
  3. Inline JS code in .html.erb files

#1 React App was converted into a Stimulus controller. Webpack was replaced by importmaps. Easy, no issues here.
#2 Some of these js files could be moved to /vendor/javascript. Some of these could be converted into Stimulus Controllers.
#3 I have a lot of inline js code in .html.erb files. Code is usualy simple. It initializes something or calls some functions from #2.
Here is an example:

  <script type='text/javascript'>
    $(function() {
      $("form select").select2({placeholder: "Select a product", width: '100%' });

I was thinking to create a Stimulus controller for select2, but some of the selects should have their own logic. Like:

  • trigger some other js functions on change
  • hide/show some elements on page
  • fetch data from api

It seems that I will need to create too many stimulus controllers just for select2. And I will have dozen of Stimulus controllers.
Maybe it is a good idea to leave inline code? But I don’t think that I could use it with importmaps.
Is it still ok to have js in app/assets/javascript?
What would you suggest?

Congrats on modernizing your JS. Looks like you are on the right track.

I don’t see any issue with having a dozen or more Stimulus controllers, so long as they are reusable and easy to understand from reading the html.

Another thought would be to have one larger Stimulus controller that handles multiple input types. You can attach the controller to the select element and have conditional logic that executes depending on the attributes of that element.

Also, keep in mind that a lot of the work of replacing/hiding/showing elements on the page can be done via Turbo. For example, you can have a Stimulus controller action that fires off a GET turbo stream request, and then you can rely on Turbo to find the element it’s supposed to replace/hide/show.

You can Make a generic method for stimulus controller select2
here is an example

window.select2drop = function (klass, model) {
    ajax: {
      url: `/${model}s/`,
      dataType: 'json',
      data: function (params) {
        return {
          search: params.term,
          one_id: $(`#${klass}_one_id`).val(),
          two_id: $(`#${klass}_two_id`).val(),
          three_id: $(`#${klass}_three_id`).val(),
      processResults: function (data) {
        return { results: data };
      cache: false,
    allowClear: true,
    placeholder: `Select ${model}...`.replace(/_/g, ' '),
    templateResult: optionTemplate,
    templateSelection: selectionTemplate,
and  after that, you have to simply add in targetConnected call back method some code according to your page inputs
    select2drop('call', 'one');
    select2drop('call', 'two');
    select2drop('call', 'three');

it is an example you can modify according to your need