import { Controller } from "@hotwired/stimulus"
export default class extends Controller {
static values = { delay: { type: Number, default: 250 } }
connect() {
this.timeout = null
}
schedule() {
clearTimeout(this.timeout)
this.timeout = setTimeout(() => {
const form = this.element.closest("form")
if (form) form.requestSubmit()
}, this.delayValue)
}
}
<%= form_with url: snips_path, method: :get, data: { turbo_frame: "snips" } do |f| %>
<%= f.search_field :q, value: params[:q], placeholder: "Search…",
data: { controller: "search", action: "input->search#schedule" } %>
<% end %>
<%= turbo_frame_tag "snips" do %>
<%= render @snips %>
<% end %>
Client-side debounce is best done in Stimulus (not in view helpers). This controller submits the nearest form after a short pause, while letting Turbo handle the navigation and frame replacement.