Rails Stimulus та Importmaps у 2026: Сучасний JavaScript без інструментів збірки
Повний посібник зі Stimulus та Importmaps у Rails 8 - як створювати інтерактивні веб-застосунки без webpack, esbuild чи будь-яких бандлерів.

Rails завжди дотримувався принципу конвенції над конфігурацією, і підхід до JavaScript не є винятком. У 2026 році Rails 8 пропонує повністю зрілий стек для створення інтерактивних застосунків без необхідності налаштування webpack, esbuild чи будь-якого іншого бандлера. Stimulus разом з Importmaps формують основу сучасного підходу до JavaScript в екосистемі Rails.
Stimulus та Importmaps увімкнені за замовчуванням у кожному новому застосунку Rails 8. Це означає нульову конфігурацію - достатньо запустити rails new і можна починати писати інтерактивний JavaScript код.
Що таке Importmaps і чому варто їх використовувати
Importmaps - це вбудована функціональність браузерів, що дозволяє відображати назви JavaScript модулів безпосередньо на URL-адреси. Замість того, щоб об'єднувати всі файли в один великий файл, браузер завантажує модулі на вимогу. Rails використовує цю технологію через gem importmap-rails.
Ключові переваги цього підходу включають усунення кроку збірки, миттєве оновлення під час розробки та простоту конфігурації. Немає потреби встановлювати Node.js або керувати пакетами npm.
# 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"Наведена конфігурація визначає всі необхідні модулі. Директива pin відображає назву модуля на конкретний файл, а pin_all_from автоматично реєструє всі контролери Stimulus із зазначеної директорії.
Додавання зовнішніх бібліотек
Додавання JavaScript бібліотек таке ж просте, як і в традиційних бандлерах. Команда bin/importmap pin завантажує бібліотеку з CDN та додає відповідний запис до конфігурації.
# Додавання бібліотеки lodash
bin/importmap pin lodash
# Додавання бібліотеки з конкретною версією
bin/importmap pin lodash@4.17.21
# Відображення поточних пінів
bin/importmap pinsПісля виконання команди бібліотека одразу доступна для імпорту в JavaScript коді:
import _ from "lodash"
console.log(_.chunk([1, 2, 3, 4, 5], 2))
// Output: [[1, 2], [3, 4], [5]]Вступ до Stimulus
Stimulus - це JavaScript фреймворк, спеціально розроблений для роботи з HTML, що генерується сервером. На відміну від React чи Vue, Stimulus не перебирає контроль над рендерингом - натомість він збагачує існуючий HTML інтерактивністю.
Основними будівельними блоками Stimulus є контролери. Кожен контролер - це JavaScript клас, відповідальний за певну поведінку на сторінці.
import { Controller } from "@hotwired/stimulus"
export default class extends Controller {
static targets = ["name", "output"]
greet() {
this.outputTarget.textContent = `Привіт, ${this.nameTarget.value}!`
}
}Наведений контролер визначає два targets (елементи DOM) та метод greet, який оновлює вміст елемента output. Щоб з'єднати контролер з HTML, використовуються data атрибути:
<div data-controller="hello">
<input data-hello-target="name" type="text" placeholder="Введіть ім'я">
<button data-action="click->hello#greet">Привітати</button>
<p data-hello-target="output"></p>
</div>Генерація контролерів Stimulus
Rails надає генератор для створення нових контролерів Stimulus. Це допомагає підтримувати узгодженість структури проєкту та автоматично реєструє контролер у системі.
# Генерація нового контролера
bin/rails generate stimulus dropdown
# Результат:
# create app/javascript/controllers/dropdown_controller.jsЗгенерований контролер містить базову структуру, готову до розширення:
import { Controller } from "@hotwired/stimulus"
export default class extends Controller {
connect() {
// Викликається, коли контролер приєднується до DOM
}
}Lifecycle callbacks у Stimulus
Контролери Stimulus пропонують набір lifecycle методів, які викликаються в певні моменти життєвого циклу контролера. Розуміння цих callbacks є критичним для правильного управління станом та ресурсами.
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() викликається, коли елемент з контролером додається до DOM, тоді як disconnect() - коли елемент видаляється. Це ідеальні місця для ініціалізації та очищення ресурсів.
Values - реактивні властивості
Stimulus Values - це механізм зберігання конфігураційних даних безпосередньо в HTML атрибутах. Values автоматично парсять типи даних і пропонують реактивність через callbacks змін.
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 виглядає наступним чином:
<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">Попередній</button>
<button data-action="slideshow#next">Наступний</button>
</div>Outlets - комунікація між контролерами
Outlets дозволяють контролерам посилатися на інші контролери на сторінці. Це елегантний спосіб побудови складних взаємодій між компонентами.
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)
}
}
}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)
})
}
}Конфігурація з'єднання відбувається через outlet атрибут:
<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">Елемент 1</div>
<div data-results-target="item">Елемент 2</div>
<div data-results-target="item">Елемент 3</div>
</div>Практичний приклад: модальне вікно з формою
Наступний приклад демонструє більш складний контролер, що обробляє модальне вікно з формою, включаючи анімації та валідацію.
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()
}
}
}<div data-controller="modal">
<button data-action="modal#open">Відкрити форму</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>Контактна форма</h2>
<input type="email" required placeholder="Email">
<textarea required placeholder="Повідомлення"></textarea>
<div>
<button type="button" data-action="modal#close">Скасувати</button>
<button type="submit">Надіслати</button>
</div>
</form>
</dialog>
</div>Інтеграція з Turbo
Stimulus чудово працює з Turbo, другим стовпом Hotwire. Turbo обробляє навігацію та оновлення сторінки, тоді як Stimulus додає інтерактивність на стороні клієнта.
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")
}
}<%= 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 "Надіслати", data: { form_target: "submit" } %>
<span data-form-target="spinner" class="hidden">Надсилання...</span>
<% end %>Готовий до співбесід з Ruby on Rails?
Практикуйся з нашими інтерактивними симуляторами, flashcards та технічними тестами.
Налагодження та інструменти розробника
Під час розробки корисними є інструменти для налагодження контролерів Stimulus. Можна увімкнути режим debug у застосунку:
import { Application } from "@hotwired/stimulus"
const application = Application.start()
// Увімкнення налагодження
application.debug = true
// Доступ до застосунку з консолі браузера
window.Stimulus = applicationЗ увімкненим налагодженням консоль браузера відображає інформацію про приєднання та від'єднання контролерів, що значно полегшує діагностику проблем.
Тестування контролерів Stimulus
Тестування контролерів Stimulus можна проводити за допомогою системи JavaScript тестів або системних тестів Rails.
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="Іван">
<button data-action="click->hello#greet">Привітати</button>
<p data-hello-target="output"></p>
</div>
`
application = Application.start()
application.register("hello", HelloController)
})
afterEach(() => {
application.stop()
})
it("відображає привітання після кліку", () => {
const button = document.querySelector("button")
const output = document.querySelector("[data-hello-target='output']")
button.click()
expect(output.textContent).toBe("Привіт, Іван!")
})
})Організація коду у великих проєктах
У великих застосунках варто організовувати контролери в підкаталоги за функціональністю. Importmaps автоматично обробляють вкладені структури.
# 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"Структура каталогів:
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 контролери з підкаталогів посилаються з префіксом:
<div data-controller="admin--dashboard">...</div>
<div data-controller="shared--modal">...</div>Підсумок
Stimulus та Importmaps у Rails 8 пропонують елегантне рішення для створення інтерактивних веб-застосунків без складності традиційних JavaScript бандлерів. Цей підхід ідеально відповідає філософії Rails - конвенція над конфігурацією.
Ключові переваги включають нульову конфігурацію, миттєве оновлення під час розробки, повну сумісність з HTML, що генерується сервером, та відмінну інтеграцію з іншими елементами стеку Hotwire.
Для команд, що працюють з Rails, Stimulus є природним розширенням існуючої архітектури, дозволяючи поступово додавати інтерактивність без переписування застосунку на SPA фреймворк. У поєднанні з Turbo він формує повний набір інструментів для створення сучасних веб-застосунків.
Чи знайдеш ти помилку в Ruby on Rails?
Справжній фрагмент коду, прихована помилка, одна спроба на день. Щоб спробувати, акаунт не потрібен.

Автор:
Anthony Fillion-MailletЗасновник SharpSkill
Fullstack-розробник понад 10 років. Керує SharpSkill і відповідає за все, що тут публікується.
Оновлено 29 серпня 2026 р.
Поділитися
Пов'язані статті

Rails GraphQL API у 2026: graphql-ruby, підписки та питання на співбесіді
Повний посібник зі створення продакшн-рівня GraphQL API з Rails 8 та graphql-ruby. Проєктування схеми, мутації, підписки з ActionCable та підготовка до співбесіди.

Фонові Завдання Rails 2026: Sidekiq vs Good Job — Порівняння та Питання на Співбесідах
Комплексне порівняння Sidekiq, Good Job та Solid Queue у Rails 8. Аналіз продуктивності, функціональності та найпоширеніші питання на співбесідах щодо background jobs у Ruby on Rails.

Rails Active Storage у 2026: Завантаження Файлів, Інтеграція з S3 та Питання на Співбесідах
Повний посібник з Rails Active Storage у 2026 році. Налаштування S3, пряме завантаження, обробка зображень та найпоширеніші питання на технічних співбесідах.