Boring Rails

Tip: Accessing Rails environment variables from a StimulusJS Controller

:fire: Tiny Tips

Environment variables are a great way to configure your Rails apps. You can use the Rails.env variable to conditionally change behavior when you are in development/test or production. And you can add your own application specific variables for things like API tokens or global settings.

While Stimulus has a values API to read from HTML data attributes, sometimes you need data that you don’t want to pass in every time you create a controller.

One alternative is to use <meta> tags when rendering your view. Then, inside your Stimulus Javascript code, you can query the <meta> DOM element to retrieve the value.

Usage

First, add the <meta> tag to your application template inside the <head> section. For this example, let’s say we want to know the Rails.env because we want to change a setting when running the app in test mode (I recently had to do this to change a setting in a 3rd-party library to work with headless system tests).

You can use the Rails tag helper to avoid a bunch of string interpolation:

# Inside the <head> tag of your application layout

<%= tag :meta, name: :rails_env, content: Rails.env %>

This will render a <meta> tag like:

<meta name="rails_env" content="development">

Next, in your Stimulus controller, query for the tag and read the content.

import { Controller } from '@hotwired/stimulus'

export default class extends Controller {
  connect() {
    if (this.isTestEnvironment) {
      // Do something for testing
    } else {
      // Do something for dev/prod
    }
  }

  get isTestEnvironment() {
    return document.head.querySelector("meta[name=rails_env]").content === "test"
  }
}

You can also extract this into a helper if you want – here is a utility function from the Rails request.js library which uses this same <meta> tag pattern to access the Rails CSRF token when sending Javascript requests.

function metaContent (name) {
  const element = document.head.querySelector(`meta[name="${name}"]`)
  return element && element.content
}

Remember that this data is visible in your page source! Unlike environment variables that only exist on the server, putting data into your HTML makes it accessible to end-users. Make sure you are only exposing data that is safe to be public.

References

MDN: metadata element

If you like these tips, you'll love my Twitter account. All killer, no filler.