【完全版】Rails Propshaft の使い方|Sprocketsからの移行・基本設定・jsbundling/cssbundling連携

  • 作成日 2026.06.23
  • rails
【完全版】Rails Propshaft の使い方|Sprocketsからの移行・基本設定・jsbundling/cssbundling連携

Rails 8 で デフォルトのアセットパイプライン となった propshaft は、Sprockets の代替として開発されたミニマルな静的アセット配信ライブラリです。

# Gemfile
gem "propshaft"

しかし、Sprockets と比べて機能が大幅に削減されているため:

  • Sprocketsとの違いがよく分からない
  • 既存の *= require ディレクティブはどうなる?
  • Sass/SCSS のコンパイルは?
  • npm パッケージはどう取り込む?
  • View での image_tagjavascript_include_tag は同じように使える?
  • Sprockets からの移行に何が必要?
  • Tailwind や importmap との組み合わせは?

など、判断ポイントが多くあります。

本記事では、Propshaft のすべての使い方を、インストールから本番運用、Sprocketsからの移行まで実用視点で網羅します。基本構造、jsbundling-rails / cssbundling-rails / importmap-rails / Tailwind との組み合わせ、digest機構、移行手順、トラブル対処、FAQまで完全網羅。この1本で Propshaft を実戦投入できるようになります。


目次

結論:今すぐ知るべき3つのポイント

時間がない方向けに、ポイントを先に示します。

① Propshaft はファイル「配信専用」

Sprockets と違い、コンパイル・トランスパイル・連結を行わない。これらは外部ツール(esbuild、Sass等)の責務。

② 既存のSprocketsアプリは移行不要

Rails 8 にアップグレードしても、Sprockets はそのまま使い続けられる。移行はオプションであり、メリットがある場合のみ実施。

③ 組み合わせて使う

# Gemfile(典型構成例)
gem "propshaft"              # アセット配信
gem "jsbundling-rails"       # JS バンドル(esbuild等)
gem "cssbundling-rails"      # CSS バンドル(必要に応じて)
gem "tailwindcss-rails"      # Tailwindなら
gem "importmap-rails"        # importmap使うなら

詳細は以下で解説します。


まず押さえる:Propshaft とは

概要

Propshaft は DHH と Rails コアチームが開発したミニマルなアセット配信ライブラリです。

特徴内容
役割静的ファイル配信+digest 付与
コンパイル行わない(外部ツールに委ねる)
連結(concatenate)行わない
トランスパイル行わない
デフォルト採用Rails 8 から
対応Rails7.0+ で利用可、8.0 でデフォルト
コードサイズSprockets より圧倒的に小さい

Propshaft の哲学:「やらない」設計

Sprocketsが「何でもやる」だったのに対し、Propshaftは「やらないことを明確に」している:

機能SprocketsPropshaft
静的ファイル配信
digest 付きファイル名
複数パスからの集約
Sass/SCSS コンパイル❌(cssbundling任せ)
CoffeeScript
ERB アセット
ファイル連結✅(require)
Minification❌(外部任せ)
manifest.js
ソースマップ⚠️(外部ツール任せ)

なぜ「やらない」のか

HTTP/2 時代の現実:

  • HTTP/2 では複数ファイル同時配信が高速 → 連結の必要性が低下
  • 専用ツール(esbuild, Webpack, Vite)の方が高速で機能豊富 → 内部実装する意義が薄い
  • 設定とコードがシンプル → デプロイ・デバッグが容易

Propshaftはアセット配信に専念し、ビルドは専門ツールに任せる」という分業体制。


Sprockets と Propshaft の決定的な違い

1. ファイル連結(concatenation)

Sprockets

// app/assets/javascripts/application.js
//= require jquery
//= require_tree .

require ディレクティブで複数JSを1ファイルに連結。

Propshaft

// app/javascript/application.js
import "@hotwired/turbo-rails"
import "controllers"

ES Modules による import のみ。連結はesbuild等のバンドラーが行う。

2. プリプロセッサ

Sprockets

// app/assets/stylesheets/application.scss
@import "bootstrap";

Sass を自動的にCSSへコンパイル。

Propshaft

  • cssbundling-rails(dartsass-rails等)でビルドしてから配信
  • もしくは tailwindcss-rails で Tailwind 利用

3. アセットパス(CSS内のURL参照)

Sprockets(絶対パス)

/* app/assets/stylesheets/application.css */
.logo {
  background-image: url('logo.png');  /* /assets/logo-abc123.png */
}

Propshaft(相対パス)

.logo {
  background-image: url('/logo.png');  /* 先頭に / 必須 */
}

⚠️ Sprockets と挙動が違うため、移行時にCSS内のURLを書き換える必要があります。

4. asset_path ヘルパー

両者で動作は同じ:

<%= image_tag "logo.png" %>
<%= image_path "logo.png" %>
<%= asset_path "logo.png" %>

Propshaft はファイルをコピー&digest付与するだけ。


インストールとセットアップ

Rails 8 新規プロジェクト

rails new myapp
# Propshaft が既にデフォルトで含まれている

Gemfile を確認:

gem "propshaft"

既存 Sprockets アプリへの追加

bundle add propshaft
# Gemfile
gem "propshaft"
# gem "sprockets-rails"  ← 削除
bundle install

ディレクトリ構造

app/
├── assets/
│   ├── images/         # 画像(そのまま配信)
│   ├── builds/         # jsbundling/cssbundling が出力(gitignore)
│   ├── stylesheets/    # CSS(生のCSSなら直接配信、SCSSは builds 経由)
│   └── javascripts/    # 通常使わない(app/javascript推奨)
└── javascript/         # JS ソースコード
    ├── application.js
    └── controllers/

config/application.rb

# Propshaftのコンフィグ確認
config.assets.paths  # 配信対象のパス

通常はデフォルトで十分。

precompile 対象

Propshaft はすべての対象ファイルを自動で precompile します。config/initializers/assets.rb での個別指定は基本不要です(ただし config.assets.precompile = ... を残しても害はない)。


アセットの配置と配信

画像・フォントなど静的ファイル

app/assets/images/logo.png
app/assets/fonts/roboto.woff2

ビューから:

<%= image_tag "logo.png" %>
<%# /assets/logo-abc123def.png (digest付与) %>

CSS(生のCSS)

app/assets/stylesheets/application.css
<%= stylesheet_link_tag "application" %>

そのまま配信されます。

JavaScript(importmap or jsbundling)

importmap 使用時

app/javascript/application.js
<%= javascript_importmap_tags %>

jsbundling-rails 使用時

app/javascript/application.js → app/assets/builds/application.js(ビルド出力)
<%= javascript_include_tag "application", defer: true %>

jsbundling-rails との組み合わせ

JavaScript で npm パッケージを使う場合の典型構成。

セットアップ

bin/rails javascript:install:esbuild
# または bun
bin/rails javascript:install:bun

Gemfile に追加:

gem "jsbundling-rails"

package.json 生成:

{
  "scripts": {
    "build": "esbuild app/javascript/*.* --bundle --sourcemap --outdir=app/assets/builds --public-path=/assets"
  }
}

ビルド

# 開発(ファイル変更を監視)
yarn build --watch

# 本番ビルド
yarn build

bin/dev で同時起動

# Procfile.dev
web: bin/rails server -p 3000
js: yarn build --watch
css: yarn build:css --watch
bin/dev

これで Rails サーバー + JS監視 + CSS監視が同時起動。

Application.html.erb

<%= javascript_include_tag "application", defer: true %>

importmap-rails との組み合わせ

Node.js / バンドラー不要で JavaScript を扱う方法。

セットアップ

bin/rails importmap:install

Gemfile:

gem "importmap-rails"

config/importmap.rb

pin "application"
pin "@hotwired/turbo-rails", to: "turbo.min.js"
pin "@hotwired/stimulus", to: "stimulus.min.js"
pin_all_from "app/javascript/controllers", under: "controllers"

外部パッケージの追加

bin/importmap pin react
# config/importmap.rb に自動追加

View

<%= javascript_importmap_tags %>

利点・欠点

メリットデメリット
Node.js 不要古いブラウザ非対応
シンプルな構成大規模なJSプロジェクトでは管理困難
ビルドステップ不要TypeScript 等は使えない

小〜中規模アプリで Hotwire 中心なら importmap が最適。


cssbundling-rails との組み合わせ

Sass や Tailwind 以外でも CSS をビルドする場合。

セットアップ(Sass の場合)

bin/rails css:install:sass

Gemfile:

gem "cssbundling-rails"

package.json:

{
  "scripts": {
    "build:css": "sass ./app/assets/stylesheets/application.sass.scss:./app/assets/builds/application.css --no-source-map --load-path=node_modules"
  }
}

開発

yarn build:css --watch

View

<%= stylesheet_link_tag "application" %>
# /assets/builds/application-digest.css が配信

Tailwind CSS との組み合わせ

最も人気のあるTailwind統合。

セットアップ

bin/rails tailwindcss:install

Gemfile:

gem "tailwindcss-rails"

自動設定

  • app/assets/stylesheets/application.tailwind.css 生成
  • app/assets/builds/ への出力設定
  • Procfile.dev への監視タスク追加

ビルド

# 開発
bin/rails tailwindcss:watch

# bin/dev で自動
bin/dev

# 本番
bin/rails tailwindcss:build

View

<%= stylesheet_link_tag "tailwind", "inter-font" %>
<%= stylesheet_link_tag "application", data: { "turbo-track": "reload" } %>

Tailwind を採用すると、CSS は基本的に utility classes だけで書けるため、cssbundling-rails は不要になります。


digest(ファイル名ハッシュ化)の仕組み

Propshaftの主機能のひとつ。

digest の役割

ブラウザキャッシュ対策で、ファイル内容が変わったらファイル名も変える:

ソース: app/assets/builds/application.js
配信ファイル: /assets/application-abc123def456.js

ハッシュ部分(abc123def456)はファイル内容から計算されるため:

  • ファイルが同じ → 同じ digest → ブラウザキャッシュ有効
  • ファイルが変わった → 新しい digest → 新しいURLでDL

precompile

bin/rails assets:precompile

本番デプロイ時に実行。public/assets/ に digest 付きファイルがコピーされます:

public/assets/
├── application-abc123.js
├── application-def456.css
├── logo-ghi789.png
└── .manifest.json

.manifest.json

ファイル名 → digest名のマッピング:

{
  "application.js": "application-abc123.js",
  "logo.png": "logo-ghi789.png"
}

asset_path ヘルパーがこれを参照して正しい digest URL を返します。

キャッシュクリア

bin/rails assets:clobber

public/assets/ を削除。


View ヘルパー

Propshaft でも標準的なヘルパーがすべて使えます:

image_tag

<%= image_tag "logo.png" %>
<%# <img src="/assets/logo-abc123.png" /> %>

<%= image_tag "logo.png", alt: "Logo", class: "h-10" %>

asset_path / asset_url

<%= asset_path "logo.png" %>
<%# "/assets/logo-abc123.png" %>

<%= asset_url "logo.png" %>
<%# "https://example.com/assets/logo-abc123.png" %>

javascript_include_tag

<%= javascript_include_tag "application", defer: true %>

stylesheet_link_tag

<%= stylesheet_link_tag "application", "data-turbo-track": "reload" %>

image_path(パスだけ取得)

<%= image_path "logo.png" %>
<%# "/assets/logo-abc123.png" %>

CSS 内での画像参照

CSS の中で画像を参照する場合は 絶対パス(先頭スラッシュ) で:

/* ❌ Sprockets 時代の書き方 */
.logo { background-image: url('logo.png'); }

/* ✅ Propshaft */
.logo { background-image: url('/logo.png'); }

または asset_url を使う場合(ERBで処理):

/* app/assets/stylesheets/application.css.erb */
.logo {
  background-image: url('<%= asset_url("logo.png") %>');
}

ただし Propshaft では ERB アセットを推奨しません。CSS は静的に書くか、cssbundling 経由で扱うのが綺麗。


Sprockets からの移行手順

既存のSprocketsアプリを Propshaft に移行する完全手順。

事前判断:移行すべきか?

状況推奨
Sprockets が問題なく動いている移行不要(Sprockets 継続でOK)
importmap + 生CSSで運用したいPropshaft へ移行推奨
既に jsbundling/cssbundling 使用Propshaft へ移行が容易
Sass/SCSS を多用、ERBアセット多数移行はやや手間
CoffeeScript を使っている別の問題があるので先に解消

Propshaft 公式メンテナの推奨は「動いているなら無理に変えるな」。

ステップ1:使っている機能の調査

# Sprockets ディレクティブを探す
grep -rn "//= require" app/assets/javascripts/
grep -rn "*= require" app/assets/stylesheets/

# プリプロセッサ使用ファイル
find app/assets -name "*.scss" -o -name "*.sass" -o -name "*.coffee" -o -name "*.erb"

# ERBアセット
find app/assets -name "*.erb"

ステップ2:Gemfile の更新

# 削除
# gem "sprockets-rails"
# gem "sassc-rails"           # Sprockets用 Sass
# gem "jquery-rails"          # gem版 jQueryは廃止予定
# gem "bootstrap"             # gem版は推奨されない(npm版を使う)

# 追加
gem "propshaft"
gem "jsbundling-rails"       # JS バンドルが必要なら
gem "cssbundling-rails"      # CSS バンドルが必要なら(Sass使うなら)
gem "tailwindcss-rails"      # Tailwind 使うなら
# importmap-rails も選択肢
bundle install

ステップ3:JavaScript の移行

旧(Sprockets + require)

// app/assets/javascripts/application.js
//= require jquery
//= require_tree .

新(ES Modules)

// app/javascript/application.js
import "./controllers"
import "@hotwired/turbo-rails"
// jQuery 不要にするか
import "jquery"

npm パッケージは package.json で管理:

yarn add jquery @hotwired/turbo-rails

ステップ4:CSS の移行

Sassを使い続ける場合

# cssbundling-rails でSassビルド
bin/rails css:install:sass

app/assets/stylesheets/application.sass.scss:

@import "bootstrap";
@import "custom";

Tailwind に移行する場合(推奨)

bin/rails tailwindcss:install

生CSSの場合

# Sprockets のディレクティブを削除
# app/assets/stylesheets/application.css
# 旧:
#  *= require_tree .
# 新: ファイルを1つにまとめるか、複数 stylesheet_link_tag で読む

ステップ5:CSS 内 URL の修正

# 相対URLを絶対URLに置換
find app/assets/stylesheets -name "*.css" -exec sed -i '' "s|url('\\([^/]\\)|url('/\\1|g" {} +

例:

/* 旧 */
.logo { background-image: url('logo.png'); }

/* 新 */
.logo { background-image: url('/logo.png'); }

ステップ6:View ヘルパーの確認

ほとんどそのまま動きます:

<%= image_tag "logo.png" %>
<%= javascript_include_tag "application", defer: true %>
<%= stylesheet_link_tag "application" %>

javascript_importmap_tags を使うなら追加。

ステップ7:不要ファイルの削除

rm config/assets.rb            # Sprockets用
rm app/assets/config/manifest.js
rm -rf app/assets/javascripts/  # 移行完了なら

ステップ8:ビルド・確認

# 開発
bin/dev

# 本番ビルド確認
RAILS_ENV=production bin/rails assets:precompile

ステップ9:CI/CD 更新

# .github/workflows/ci.yml
- name: Install Node
  uses: actions/setup-node@v4
  with:
    node-version: 20

- name: Install dependencies
  run: yarn install

- name: Precompile assets
  run: bin/rails assets:precompile

Docker環境での precompile

Dockerfile 例

FROM ruby:3.3-slim AS base
WORKDIR /rails

# 必要なパッケージ
RUN apt-get update -qq && apt-get install -y \
    build-essential libpq-dev curl

# Node.js(jsbundling使う場合)
RUN curl -fsSL https://deb.nodesource.com/setup_20.x | bash - && \
    apt-get install -y nodejs

RUN npm install -g yarn

# Gemfile
COPY Gemfile Gemfile.lock ./
RUN bundle install

# package.json
COPY package.json yarn.lock ./
RUN yarn install

# アプリコード
COPY . .

# アセット precompile
RUN SECRET_KEY_BASE=dummy bin/rails assets:precompile

CMD ["bin/rails", "server", "-b", "0.0.0.0"]

マルチステージビルドで軽量化

FROM ruby:3.3-slim AS builder
# ... ビルド処理 ...
RUN SECRET_KEY_BASE=dummy bin/rails assets:precompile

FROM ruby:3.3-slim AS app
WORKDIR /rails
COPY --from=builder /rails /rails
COPY --from=builder /usr/local/bundle /usr/local/bundle
CMD ["bin/rails", "server", "-b", "0.0.0.0"]

最終イメージから Node.js を排除して軽量化。

Docker関連のディスク問題はDocker no space left on device の記事も参照。


CDN との連携

Cloudfront / その他CDN

# config/environments/production.rb
config.asset_host = "https://cdn.example.com"

これで image_tag 等が CDN の URL を返すようになる:

<%= image_tag "logo.png" %>
<%# https://cdn.example.com/assets/logo-abc123.png %>

CDN設定(CloudFront例)

CloudFront の Origin に Rails アプリを指定し、/assets/* パスを長期キャッシュする設定。

digest 付きファイル名のため、TTL は1年で問題ありません:

Cache-Control: public, max-age=31536000, immutable

Rails 側でのキャッシュヘッダー

# config/environments/production.rb
config.public_file_server.headers = {
  "Cache-Control" => "public, max-age=31536000, immutable"
}

Origin Pull で運用

CDN は初回アクセス時に Rails から取得し、以降キャッシュ。assets:precompile で生成されたファイルが配信される構成。


トラブルシューティング

ファイルが見つからない(404)

原因1:precompile し忘れ

RAILS_ENV=production bin/rails assets:precompile

原因2:パスが間違い

<%# ❌ %>
<%= image_tag "images/logo.png" %>

<%# ✅ images/ は省略 %>
<%= image_tag "logo.png" %>

原因3:CSSの相対パス

/* ❌ */
.logo { background-image: url('logo.png'); }

/* ✅ */
.logo { background-image: url('/logo.png'); }

digest 付きファイル名が古い

bin/rails assets:clobber
bin/rails assets:precompile

開発環境で変更が反映されない

Tailwind

bin/rails tailwindcss:watch
# または bin/dev で自動起動

jsbundling

yarn build --watch
# または bin/dev で自動起動

Sprockets 時代の require が残っている

grep -rn "//= require" app/

該当箇所を ES Modules の import に書き換え。

Manifest が生成されない

Propshaft はファイル変更時に自動で manifest を生成しますが、本番では precompile 時のみ:

bin/rails assets:precompile
ls public/assets/.manifest.json

CSS が反映されない

<%# Sass使ってる場合、必ず builds 経由 %>
<%= stylesheet_link_tag "application" %>

app/assets/builds/application.css が存在するか確認:

ls -l app/assets/builds/
yarn build:css   # 強制ビルド

Docker環境でアセットが古い

Dockerイメージのキャッシュが原因の可能性:

docker build --no-cache -t myapp .

Node.js バージョン不整合

node --version
# package.json の engines と一致しているか

パフォーマンス・最適化

ビルド速度比較

アセットパイプライン中規模アプリ初回ビルド
Sprockets + Sass20〜60秒
Propshaft + esbuild2〜8秒
Propshaft + Tailwind1〜3秒
Propshaft + importmap1秒未満

esbuildTailwind のビルド速度がそのまま反映される。

本番デプロイ時間短縮

# 並列precompile
RAILS_ENV=production bin/rails assets:precompile -P

HTTP/2 / HTTP/3 の活用

Propshaft は連結しないので、多数の小さいファイルが配信されます。HTTP/2 の多重化により、Sprockets 時代より高速になることが多い。

ファイル数削減

「あえて連結する」場合は jsbundling-rails でエントリポイントをまとめる:

// app/javascript/application.js
import "./module_a"
import "./module_b"
import "./module_c"
// esbuild で1ファイルにバンドル

よくある質問(FAQ)

Q1. Sprockets と Propshaft を併用できますか?

技術的には可能ですが推奨されません。両方有効化すると、設定が複雑化し、トラブルの元になります。どちらか一方に統一してください。

Q2. CoffeeScript はもう使えませんか?

Propshaft 単体では非対応。jsbundling-rails + esbuild の CoffeeScript プラグインで使えますが、JavaScript / TypeScript への移行を強く推奨。

Q3. ERBアセット (application.css.erb) は使えますか?

Propshaft では基本的に非推奨。動的に値を埋め込みたい場合は CSS Custom Properties や meta タグ経由が推奨。

Q4. asset_url を script タグの src で使うと?

<script src="<%= asset_url 'app.js' %>"></script>

問題なく動作します。asset_path 同様、digest 付き URL が返ります。

Q5. ファイルの digest を無効化したいです

# config/environments/production.rb
config.assets.digest = false   # 非推奨

ブラウザキャッシュ制御が困難になるため推奨されません

Q6. 画像が大量にあるアプリで遅くなりませんか?

Propshaft の precompile は全ファイルを処理しますが、digest 計算は高速。Sprockets よりむしろ速いです。

Q7. jsbundling-rails と importmap-rails を併用できますか?

併用可能ですが、複雑化するため通常はどちらかに統一推奨。「メインJSはesbuildでバンドル、補助スクリプトはimportmap」のような使い分けは可能。

Q8. Webpacker からの移行は?

Webpacker(Rails 7で非推奨化済み)からの移行手順:

  1. gem "webpacker" 削除
  2. gem "jsbundling-rails" または gem "importmap-rails" 追加
  3. app/javascript 配下のコードはそのまま使える(多くの場合)
  4. bin/rails javascript:install:esbuild 実行
  5. Sprockets が残っていれば、合わせて Propshaft 移行

Q9. Tailwind のJITモードはどう設定?

tailwindcss-rails ではデフォルトでJITモード。tailwind.config.jscontent に対象ファイルを指定:

module.exports = {
  content: [
    './app/views/**/*.html.erb',
    './app/helpers/**/*.rb',
    './app/javascript/**/*.js',
  ]
}

Q10. CI/CD でアセットビルドが失敗します

考えられる原因:

  • Node.js / yarn のバージョン不整合
  • node_modules キャッシュの問題
  • SECRET_KEY_BASE 未設定
# GitHub Actions 例
- run: SECRET_KEY_BASE=dummy bin/rails assets:precompile

Q11. ソースマップは生成できますか?

jsbundling-rails の esbuild 設定で生成可能:

{
  "scripts": {
    "build": "esbuild app/javascript/*.* --bundle --sourcemap --outdir=app/assets/builds"
  }
}

ただし本番ではソースマップを公開しない設定推奨(セキュリティ)。

Q12. assets:precompile がタイムアウトします

Docker環境でメモリ不足が原因のことが多い:

ENV NODE_OPTIONS="--max-old-space-size=4096"

または並列化:

bin/rails assets:precompile -j 2

参考リンク・関連資料

公式ドキュメント

関連 Gem

ビルドツール

関連記事(本サイト)


まとめ

Propshaft はRails 8 で標準採用されたミニマルなアセット配信ライブラリです。要点を再整理します。

  • 設計思想: アセット配信に専念、ビルドは外部ツールに委ねる
  • Sprockets との違い: 連結・トランスパイル・ERB処理を行わない
  • シンプル設定: gem "propshaft" で即動作
  • 典型構成:
    • 小規模: Propshaft + importmap-rails + Tailwind
    • 中〜大規模: Propshaft + jsbundling-rails + Tailwind/cssbundling
  • digest: 自動でファイル名にハッシュ付与、HTTP/2と相性◎
  • CSS内URL注意: 相対パスではなく絶対パス(/logo.png)が必要
  • Sprockets 移行: 動いているなら急ぐ必要なし、必要に応じて段階的に
  • CDN連携: config.asset_host で簡単
  • Docker / CI/CD: Node.js環境とprecompileのバランス調整

これらの知識は、Rails 8 アプリのフロントエンド設計・本番運用・既存アプリのモダナイゼーションあらゆる場面で活用できます。本記事をブックマークしておけば、Propshaft を実戦投入できるようになります。


本記事は2026年6月時点の情報をもとに、Ruby on Rails 8.0.5 / 8.1.3 / 8.2.x、Propshaft 最新版での動作確認・公式ドキュメントに基づき作成しています。Propshaft は活発に開発されており、設定方法が更新される可能性があるため、最新の情報は公式リポジトリもあわせてご確認ください。