Referencing Assets

Propshaft provides mechanisms to reference assets from within other assets, such as referencing an image from a CSS file. It automatically rewrites these references to point to the correct digested asset paths during compilation.

In CSS

Propshaft includes a default compiler that processes CSS files and replaces standard url() functions with digested asset paths.

For example, if you have a CSS file at app/assets/stylesheets/main.css with the following content:

body {
  background-image: url("../images/background.png");
}

.icon {
  background-image: url("/icons/user.svg");
}

Propshaft will process this and produce output similar to this:

body {
  background-image: url("/assets/images/background-a1b2c3d4.png");
}

.icon {
  background-image: url("/assets/icons/user-e5f6a7b8.svg");
}

Path Resolution Rules:

  • Relative Paths (../images/background.png): Resolved relative to the location of the CSS file itself.
  • Absolute Paths (/icons/user.svg): Resolved from the root of the configured asset load paths.

This transformation is automatic for all CSS files processed by Propshaft.

In JavaScript

For JavaScript assets, the transformation is not automatic. You must explicitly mark the asset URLs you want Propshaft to process using the RAILS_ASSET_URL pseudo-method. This signals to the JavaScript compiler that the string should be replaced with a digested asset path.

For example:

// app/javascript/controllers/avatar_controller.js
import { Controller } from "@hotwired/stimulus"

export default class extends Controller {
  connect() {
    this.element.src = RAILS_ASSET_URL("/images/default-avatar.png")
  }
}

After compilation, this will be transformed into:

// In public/assets after precompilation
import { Controller } from "@hotwired/stimulus"

export default class extends Controller {
  connect() {
    this.element.src = "/assets/images/default-avatar-a1b2c3d4.png"
  }
}

This explicit approach prevents the compiler from accidentally replacing strings that look like asset paths but are not intended to be.

Bypassing the Digest Step

In some cases, you may have assets that are already digested or that need to reference each other with stable filenames (e.g., a JavaScript file and its source map). To tell Propshaft to skip the digesting step for a specific file, you can use a special naming convention.

Propshaft looks for the pattern -[digest].digested.<extension> as an indicator that a file has already been processed. Files matching this pattern will be copied to the output directory with their filenames unchanged.

For example, if you have a file named my-library-a1b2c3d4.digested.js, Propshaft will:

  1. Recognize it as already digested.
  2. Not add another digest hash to its filename.
  3. Copy it to public/assets as my-library-a1b2c3d4.digested.js.