Skip to main content

Extending Storefront with JavaScript

Spree Storefront can be easily extended with custom JavaScript. Most of the JavaScript in the storefront is powered by a framework called Stimulus.js which is the Ruby on Rails default. It’s a very simple and minimalistic framework only enhancing our server-side rendered HTML with a bit of interactivity.

3rd party JavaScript libraries

Spree Storefront comes with a few 3rd party JavaScript libraries already included. Main libraries we’re using: You can find them in the app/assets/javascripts/vendor directory.

Managing JavaScript dependencies

You’re probably wondering why these libraries are in the vendor directory, and not in node_modules. That’s because we’re not using Node.js at all. So no Yarn or npm. We’re using a different approach to manage dependencies. We’re using a tool called Importmaps to manage dependencies which is the Ruby on Rails default.
If you want to use a different JavaScript package manager you can do so by using jsbundling-rails gem.

Install dependencies

To install dependencies you need to run the following command:
bin/importmap pin react
This will install the dependencies, download the files to your vendor/javascript directory and add them to your config/importmap.rb file, eg.
pin "react" # @19.1.0
Now you can just import the library in your application entry point, eg. application.js:
import "react";

Application entry point

Mentioned above, the application entry point is the application.js file. It’s located in the app/javascript directory. If you want to add custom JavaScript to your Spree storefront, you can do so by adding a new file to the app/javascript directory.

Stimulus Controllers

Stimulus controllers are a way to add interactivity to your Spree storefront. Storefront controllers can be found in the Spree repository in the storefront/app/javascript/spree/storefront/controllers directory. You can find more information about Stimulus controllers in the Stimulus.js documentation. To add a new controller you need to create a new file in the app/javascript/controllers directory. It will be automatically picked up by the application.
// app/javascript/controllers/hello_controller.js
import { Controller } from '@hotwired/stimulus'

export default class extends Controller {
  connect() {
    console.log("Hello Spree Commerce Stimulus!", this.element);
  }
}
To use the controller in your HTML you need to add the data-controller attribute to your element, eg.
<div data-controller="hello">
  Hello Spree Commerce Stimulus!
</div>

JavaScript snippets

If you need to add a small piece of JavaScript code (eg. tracking, marketing, analytics, customer service etc.) you can do this by:
  1. Declaring a new head partial in the config/initializers/spree.rb file, eg.
    • Spree 5.2+
    • Spree 5.1 and below
    config/initializers/spree.rb
    Spree.storefront.partials.head << 'spree/shared/my_tracking_code'
    
  2. Creating a new file in the app/views/spree/shared/my_tracking_code.html.erb, where you can add your tracking code, eg.
    app/views/spree/shared/my_tracking_code.html.erb
    <script>
      console.log("I'm a spy!");
    </script>
    
Check out the Integrations for existing integrations you can plug into your storefront.

Extending Existing Stimulus Controllers

You can extend or override existing Storefront Stimulus controllers:
// app/javascript/controllers/cart_controller.js
import CartController from 'spree/storefront/controllers/cart_controller'

export default class extends CartController {
  connect() {
    super.connect()
    console.log('Extended cart controller connected!')
  }

  // Override existing method
  addItem(event) {
    // Custom logic before adding
    console.log('Adding item...')
    super.addItem(event)
    // Custom logic after adding
  }
}

Turbo Events

Spree Storefront uses Turbo for navigation. You can hook into Turbo events:
// app/javascript/application.js
document.addEventListener('turbo:load', () => {
  console.log('Page loaded via Turbo')
})

document.addEventListener('turbo:before-visit', (event) => {
  console.log('About to visit:', event.detail.url)
})

document.addEventListener('turbo:frame-load', (event) => {
  console.log('Turbo frame loaded:', event.target.id)
})