2026'da Rails Stimulus ve Importmaps: Build Araçları Olmadan Modern JavaScript

Rails 8'de Stimulus ve Importmaps için kapsamlı bir rehber - webpack, esbuild veya herhangi bir bundler olmadan interaktif web uygulamaları geliştirme.

2026'da Rails Stimulus ve Importmaps: Build Araçları Olmadan Modern JavaScript

Rails her zaman yapılandırma yerine konvansiyon ilkesini benimsemiş bir framework olmuştur ve JavaScript yaklaşımı da bunun bir istisnası değildir. 2026 yılında Rails 8, webpack, esbuild veya herhangi bir bundler yapılandırması gerektirmeden interaktif uygulamalar oluşturmak için olgun bir teknoloji yığını sunmaktadır. Stimulus ve Importmaps, Rails ekosisteminde modern JavaScript yaklaşımının temelini oluşturmaktadır.

Stimulus ve Importmaps, her yeni Rails 8 uygulamasında varsayılan olarak etkindir. Bu, sıfır yapılandırma anlamına gelir - rails new komutunu çalıştırmak ve interaktif JavaScript kodu yazmaya başlamak yeterlidir.

Importmaps Nedir ve Neden Kullanılmalı

Importmaps, JavaScript modül adlarını doğrudan URL'lere eşlemeye olanak tanıyan yerleşik bir tarayıcı özelliğidir. Tüm dosyaları tek bir büyük dosyada paketlemek yerine tarayıcı, modülleri talep üzerine indirir. Rails, bu teknolojiyi importmap-rails gem'i aracılığıyla kullanmaktadır.

Bu yaklaşımın temel avantajları arasında build adımının ortadan kaldırılması, geliştirme sırasında anında yenileme ve yapılandırma basitliği yer almaktadır. Node.js kurulumu veya npm paket yönetimi gerekmemektedir.

ruby
# config/importmap.rb
pin "application"
pin "@hotwired/stimulus", to: "stimulus.min.js"
pin "@hotwired/stimulus-loading", to: "stimulus-loading.js"
pin_all_from "app/javascript/controllers", under: "controllers"

Yukarıdaki yapılandırma, tüm gerekli modülleri tanımlamaktadır. pin direktifi bir modül adını belirli bir dosyaya eşlerken, pin_all_from belirtilen dizindeki tüm Stimulus controller'larını otomatik olarak kaydetmektedir.

Harici Kütüphaneler Ekleme

JavaScript kütüphaneleri eklemek, geleneksel bundler'larda olduğu kadar basittir. bin/importmap pin komutu, CDN'den kütüphaneyi indirir ve yapılandırmaya uygun girişi ekler.

bash
# lodash kütüphanesini ekleme
bin/importmap pin lodash

# Belirli bir sürümle kütüphane ekleme
bin/importmap pin lodash@4.17.21

# Mevcut pin'leri görüntüleme
bin/importmap pins

Komut çalıştırıldıktan sonra kütüphane, JavaScript kodunda import için hemen kullanılabilir hale gelmektedir:

app/javascript/application.jsjavascript
import _ from "lodash"

console.log(_.chunk([1, 2, 3, 4, 5], 2))
// Output: [[1, 2], [3, 4], [5]]

Stimulus'a Giriş

Stimulus, sunucu tarafından oluşturulan HTML ile çalışmak için özel olarak tasarlanmış bir JavaScript framework'üdür. React veya Vue'nun aksine Stimulus, render işlemini devralmazken mevcut HTML'i interaktivite ile zenginleştirmektedir.

Stimulus'un temel yapı taşları controller'lardır. Her controller, sayfadaki belirli bir davranıştan sorumlu bir JavaScript sınıfıdır.

app/javascript/controllers/hello_controller.jsjavascript
import { Controller } from "@hotwired/stimulus"

export default class extends Controller {
  static targets = ["name", "output"]

  greet() {
    this.outputTarget.textContent = `Merhaba, ${this.nameTarget.value}!`
  }
}

Yukarıdaki controller, iki target (DOM öğesi) ve output öğesinin içeriğini güncelleyen bir greet metodu tanımlamaktadır. Controller'ı HTML'e bağlamak için data attribute'ları kullanılmaktadır:

html
<div data-controller="hello">
  <input data-hello-target="name" type="text" placeholder="İsminizi girin">
  <button data-action="click->hello#greet">Selamla</button>
  <p data-hello-target="output"></p>
</div>

Stimulus Controller'ları Oluşturma

Rails, yeni Stimulus controller'ları oluşturmak için bir generator sağlamaktadır. Bu, proje yapısı tutarlılığını korumaya ve controller'ı sisteme otomatik olarak kaydetmeye yardımcı olmaktadır.

bash
# Yeni bir controller oluşturma
bin/rails generate stimulus dropdown

# Sonuç:
# create  app/javascript/controllers/dropdown_controller.js

Oluşturulan controller, genişletilmeye hazır temel bir yapı içermektedir:

app/javascript/controllers/dropdown_controller.jsjavascript
import { Controller } from "@hotwired/stimulus"

export default class extends Controller {
  connect() {
    // Controller DOM'a bağlandığında çağrılır
  }
}

Stimulus'ta Lifecycle Callback'leri

Stimulus controller'ları, controller yaşam döngüsünün belirli anlarında çağrılan bir dizi lifecycle metodu sunmaktadır. Bu callback'leri anlamak, durum ve kaynakların düzgün yönetimi için kritik öneme sahiptir.

app/javascript/controllers/timer_controller.jsjavascript
import { Controller } from "@hotwired/stimulus"

export default class extends Controller {
  static values = { interval: { type: Number, default: 1000 } }
  static targets = ["display"]

  connect() {
    this.count = 0
    this.startTimer()
  }

  disconnect() {
    this.stopTimer()
  }

  startTimer() {
    this.timer = setInterval(() => {
      this.count++
      this.displayTarget.textContent = this.count
    }, this.intervalValue)
  }

  stopTimer() {
    if (this.timer) {
      clearInterval(this.timer)
    }
  }
}

connect() metodu, controller'lı öğe DOM'a eklendiğinde çağrılırken, disconnect() öğe kaldırıldığında çağrılmaktadır. Bunlar, başlatma ve temizleme işlemleri için ideal yerlerdir.

Values - Reaktif Özellikler

Stimulus Values, yapılandırma verilerini doğrudan HTML attribute'larında saklamak için bir mekanizmadır. Values, veri türlerini otomatik olarak ayrıştırır ve değişiklik callback'leri aracılığıyla reaktivite sunmaktadır.

app/javascript/controllers/slideshow_controller.jsjavascript
import { Controller } from "@hotwired/stimulus"

export default class extends Controller {
  static values = {
    index: { type: Number, default: 0 },
    autoplay: { type: Boolean, default: false },
    images: Array
  }
  static targets = ["slide"]

  indexValueChanged() {
    this.showCurrentSlide()
  }

  autoplayValueChanged() {
    if (this.autoplayValue) {
      this.startAutoplay()
    } else {
      this.stopAutoplay()
    }
  }

  next() {
    if (this.indexValue < this.imagesValue.length - 1) {
      this.indexValue++
    } else {
      this.indexValue = 0
    }
  }

  previous() {
    if (this.indexValue > 0) {
      this.indexValue--
    } else {
      this.indexValue = this.imagesValue.length - 1
    }
  }

  showCurrentSlide() {
    this.slideTarget.src = this.imagesValue[this.indexValue]
  }

  startAutoplay() {
    this.autoplayTimer = setInterval(() => this.next(), 3000)
  }

  stopAutoplay() {
    if (this.autoplayTimer) {
      clearInterval(this.autoplayTimer)
    }
  }
}

HTML yapılandırması aşağıdaki gibi görünmektedir:

html
<div data-controller="slideshow"
     data-slideshow-index-value="0"
     data-slideshow-autoplay-value="true"
     data-slideshow-images-value='["img1.jpg", "img2.jpg", "img3.jpg"]'>
  <img data-slideshow-target="slide" src="">
  <button data-action="slideshow#previous">Önceki</button>
  <button data-action="slideshow#next">Sonraki</button>
</div>

Outlets - Controller'lar Arası İletişim

Outlet'ler, controller'ların sayfadaki diğer controller'lara referans vermesine olanak tanımaktadır. Bu, bileşenler arasında karmaşık etkileşimler oluşturmanın zarif bir yoludur.

app/javascript/controllers/search_controller.jsjavascript
import { Controller } from "@hotwired/stimulus"

export default class extends Controller {
  static outlets = ["results"]
  static targets = ["input"]

  search() {
    const query = this.inputTarget.value
    if (this.hasResultsOutlet) {
      this.resultsOutlet.filter(query)
    }
  }
}
app/javascript/controllers/results_controller.jsjavascript
import { Controller } from "@hotwired/stimulus"

export default class extends Controller {
  static targets = ["item"]

  filter(query) {
    this.itemTargets.forEach(item => {
      const visible = item.textContent.toLowerCase().includes(query.toLowerCase())
      item.classList.toggle("hidden", !visible)
    })
  }
}

Bağlantı yapılandırması outlet attribute'u aracılığıyla gerçekleştirilmektedir:

html
<div data-controller="search" data-search-results-outlet="#search-results">
  <input data-search-target="input" data-action="input->search#search">
</div>

<div id="search-results" data-controller="results">
  <div data-results-target="item">Öğe 1</div>
  <div data-results-target="item">Öğe 2</div>
  <div data-results-target="item">Öğe 3</div>
</div>

Pratik Örnek: Formlu Modal

Aşağıdaki örnek, animasyonlar ve doğrulama dahil olmak üzere formlu bir modal'ı işleyen daha karmaşık bir controller'ı göstermektedir.

app/javascript/controllers/modal_controller.jsjavascript
import { Controller } from "@hotwired/stimulus"

export default class extends Controller {
  static targets = ["dialog", "form"]
  static values = { open: Boolean }

  connect() {
    this.boundHandleKeydown = this.handleKeydown.bind(this)
  }

  open() {
    this.openValue = true
    this.dialogTarget.showModal()
    document.addEventListener("keydown", this.boundHandleKeydown)
    this.dialogTarget.classList.add("opacity-100")
    this.dialogTarget.classList.remove("opacity-0")
  }

  close() {
    this.openValue = false
    this.dialogTarget.classList.add("opacity-0")
    this.dialogTarget.classList.remove("opacity-100")
    setTimeout(() => {
      this.dialogTarget.close()
    }, 200)
    document.removeEventListener("keydown", this.boundHandleKeydown)
  }

  handleKeydown(event) {
    if (event.key === "Escape") {
      this.close()
    }
  }

  submitForm(event) {
    if (!this.formTarget.checkValidity()) {
      event.preventDefault()
      this.formTarget.reportValidity()
    }
  }

  clickOutside(event) {
    if (event.target === this.dialogTarget) {
      this.close()
    }
  }
}
html
<div data-controller="modal">
  <button data-action="modal#open">Formu Aç</button>

  <dialog data-modal-target="dialog"
          data-action="click->modal#clickOutside"
          class="opacity-0 transition-opacity duration-200 backdrop:bg-black/50">
    <form data-modal-target="form" data-action="submit->modal#submitForm">
      <h2>İletişim Formu</h2>
      <input type="email" required placeholder="E-posta">
      <textarea required placeholder="Mesaj"></textarea>
      <div>
        <button type="button" data-action="modal#close">İptal</button>
        <button type="submit">Gönder</button>
      </div>
    </form>
  </dialog>
</div>

Turbo ile Entegrasyon

Stimulus, Hotwire'ın ikinci temel bileşeni olan Turbo ile mükemmel bir şekilde çalışmaktadır. Turbo, sayfa navigasyonunu ve güncellemelerini yönetirken, Stimulus istemci tarafı interaktivitesini eklemektedir.

app/javascript/controllers/form_controller.jsjavascript
import { Controller } from "@hotwired/stimulus"

export default class extends Controller {
  static targets = ["submit", "spinner"]

  submitting() {
    this.submitTarget.disabled = true
    this.spinnerTarget.classList.remove("hidden")
  }

  submitted() {
    this.submitTarget.disabled = false
    this.spinnerTarget.classList.add("hidden")
  }
}
erb
<%= form_with model: @contact,
              data: {
                controller: "form",
                action: "turbo:submit-start->form#submitting turbo:submit-end->form#submitted"
              } do |f| %>
  <%= f.text_field :name %>
  <%= f.email_field :email %>
  <%= f.submit "Gönder", data: { form_target: "submit" } %>
  <span data-form-target="spinner" class="hidden">Gönderiliyor...</span>
<% end %>

Ruby on Rails mülakatlarında başarılı olmaya hazır mısın?

İnteraktif simülatörler, flashcards ve teknik testlerle pratik yap.

Hata Ayıklama ve Geliştirici Araçları

Geliştirme sırasında Stimulus controller'larının hata ayıklaması için araçlar oldukça faydalıdır. Uygulamada debug modu etkinleştirilebilir:

app/javascript/application.jsjavascript
import { Application } from "@hotwired/stimulus"

const application = Application.start()

// Debug modunu etkinleştirme
application.debug = true

// Tarayıcı konsolundan uygulamaya erişim
window.Stimulus = application

Debug modu etkinleştirildiğinde, tarayıcı konsolu controller'ların bağlanması ve bağlantısının kesilmesi hakkında bilgi görüntüler, bu da sorunların teşhisini önemli ölçüde kolaylaştırmaktadır.

Stimulus Controller'larını Test Etme

Stimulus controller'larının test edilmesi, JavaScript test sistemi veya Rails sistem testleri kullanılarak gerçekleştirilebilir.

test/javascript/controllers/hello_controller.test.jsjavascript
import { Application } from "@hotwired/stimulus"
import HelloController from "controllers/hello_controller"

describe("HelloController", () => {
  let application

  beforeEach(() => {
    document.body.innerHTML = `
      <div data-controller="hello">
        <input data-hello-target="name" value="Ahmet">
        <button data-action="click->hello#greet">Selamla</button>
        <p data-hello-target="output"></p>
      </div>
    `
    application = Application.start()
    application.register("hello", HelloController)
  })

  afterEach(() => {
    application.stop()
  })

  it("tıklandığında selamlama görüntüler", () => {
    const button = document.querySelector("button")
    const output = document.querySelector("[data-hello-target='output']")

    button.click()

    expect(output.textContent).toBe("Merhaba, Ahmet!")
  })
})

Büyük Projelerde Kod Organizasyonu

Büyük uygulamalarda controller'ları işlevselliğe göre alt dizinlerde organize etmek faydalıdır. Importmaps, iç içe geçmiş yapıları otomatik olarak işlemektedir.

ruby
# config/importmap.rb
pin_all_from "app/javascript/controllers", under: "controllers"
pin_all_from "app/javascript/controllers/admin", under: "controllers/admin"
pin_all_from "app/javascript/controllers/shared", under: "controllers/shared"

Dizin yapısı:

text
app/javascript/controllers/
├── application.js
├── index.js
├── hello_controller.js
├── admin/
│   ├── dashboard_controller.js
│   └── users_controller.js
└── shared/
    ├── modal_controller.js
    └── dropdown_controller.js

HTML'de alt dizinlerden controller'lar önek ile referans verilmektedir:

html
<div data-controller="admin--dashboard">...</div>
<div data-controller="shared--modal">...</div>

Sonuç

Rails 8'de Stimulus ve Importmaps, geleneksel JavaScript bundler'larının karmaşıklığı olmadan interaktif web uygulamaları oluşturmak için zarif bir çözüm sunmaktadır. Bu yaklaşım, Rails felsefesine mükemmel bir şekilde uymaktadır - yapılandırma yerine konvansiyon.

Temel faydalar arasında sıfır yapılandırma, geliştirme sırasında anında yenileme, sunucu tarafından oluşturulan HTML ile tam uyumluluk ve Hotwire yığınının diğer öğeleriyle mükemmel entegrasyon yer almaktadır.

Rails ile çalışan ekipler için Stimulus, mevcut mimarinin doğal bir uzantısını temsil etmekte ve uygulamayı SPA framework'üne yeniden yazmadan kademeli olarak interaktivite eklemeye olanak tanımaktadır. Turbo ile birleştirildiğinde, modern web uygulamaları oluşturmak için eksiksiz bir araç seti oluşturmaktadır.

Günün meydan okuması

Ruby on Rails kodundaki hatayı bulabilir misin?

Gerçek bir kod parçası, gizli bir hata, günde bir deneme. Denemek için hesap gerekmez.

Anthony Fillion-Maillet

Yazan:

Anthony Fillion-Maillet

SharpSkill kurucusu

10 yılı aşkın süredir fullstack geliştirici. SharpSkill’i yönetiyor ve burada yayımlanan her şeyden sorumlu.

29 Ağustos 2026 tarihinde güncellendi

Paylaş

İlgili makaleler