shakapacker 9.3.0.beta.4 → 9.3.0.beta.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 2f2fafddfa10d364b8e62946786c16d9206ecffadf82b719e3ffeb2100608da0
4
- data.tar.gz: 0e0cdc08d53e64e8066d7232aba1022b6a7708067d56ff7ba738803bfbc49a78
3
+ metadata.gz: 9b7929f32b14b49a2fde4893b66809a37147796c685be9161c12631369105624
4
+ data.tar.gz: 2601ae85b072d8286700558a5d92f424d6e5989f7e4d990caf2b57c939b1962c
5
5
  SHA512:
6
- metadata.gz: dcbb4604f956d78acaea611bb760ecea97a1afcc15bc390b9c5a64c5d9654f5d08e83c57ed24da55aca0a1a98da074a0047a754cadf5f74ee8ba832fb6e93b3e
7
- data.tar.gz: 47b7d7887f770f5712b5f020afd260fcefcb4b09a0c707e1b2bc921c38d2944bf6045b6b9752295790806018d98cd95d43313f9079719bcb3c808ff9f6e6c2ad
6
+ metadata.gz: 9459cd0ec016ad83df86dec2ae879be51d1dc0dbd74fa9047b401ea0c0cb5d264438e43445487974d94bb4c82db5d5116ed9b3f5c3219e0d1f74cea65c7b46bc
7
+ data.tar.gz: 2f77e883a93ff0c80a2f22871d53646b16d94e1ffa648af78f167b699431cd026e8e9c5707a3379d57fd22e588ad7e526673afb324f4997cff5924bb5f2dceef
data/CHANGELOG.md CHANGED
@@ -11,7 +11,39 @@
11
11
 
12
12
  Changes since the last non-beta release.
13
13
 
14
- ## [v9.3.0-beta.0] - October 13, 2025
14
+ ### Added
15
+
16
+ - **Support for arbitrary output names in build configurations**. [PR #752](https://github.com/shakacode/shakapacker/pull/752) by [justin808](https://github.com/justin808).
17
+ - `outputs` array now accepts any custom names (e.g., `client-modern`, `client-legacy`, `server-bundle`)
18
+ - Previously limited to only `client`, `server`, and `all`
19
+ - Enables better organization of multi-config webpack builds
20
+ - **Enhanced error reporting in config exporter**. [PR #752](https://github.com/shakacode/shakapacker/pull/752) by [justin808](https://github.com/justin808).
21
+ - Shows detailed environment variable state when config functions fail
22
+ - Provides actionable suggestions based on error patterns (e.g., missing NODE_ENV)
23
+ - Improved formatting with clear sections for easier debugging
24
+ - **Config count validation for build outputs**. [PR #752](https://github.com/shakacode/shakapacker/pull/752) by [justin808](https://github.com/justin808).
25
+ - Validates webpack/rspack config array length matches `outputs` array
26
+ - Clear error messages when mismatch detected
27
+ - Suggests fixes with example configuration
28
+ - **HTTP 103 Early Hints support** for faster asset loading. [PR #722](https://github.com/shakacode/shakapacker/pull/722) by [justin808](https://github.com/justin808). Fixes [#721](https://github.com/shakacode/shakapacker/issues/721).
29
+ - **Automatic sending**: Early hints are sent automatically when `early_hints: enabled: true` in `shakapacker.yml`
30
+ - **Per-page configuration**: Configure hints per-controller/action with `preload`/`prefetch`/`none` options
31
+ - **Hero image/video preloading**: Use Rails' built-in `preload_link_tag` (automatically sends early hints)
32
+ - **Zero-config upgrade**: No changes to layouts or views needed - just enable the config!
33
+ - Works seamlessly with existing `append_javascript_pack_tag` / `append_stylesheet_pack_tag` pattern
34
+ - Automatically discovers packs from queues populated by views/partials
35
+ - New `configure_pack_early_hints` class method for controller-level configuration
36
+ - New `skip_send_pack_early_hints` helper to opt-out for specific controllers (e.g., JSON APIs)
37
+ - Optional manual control: `configure_early_hints` can be called in controllers or views
38
+ - Added `early_hints:` option to `javascript_pack_tag` for per-tag control
39
+ - New `early_hints` configuration in `shakapacker.yml` with per-environment settings (`enabled: false` by default)
40
+ - Requires Rails 5.2+ and HTTP/2-capable server (e.g., Puma 5+)
41
+ - Browser support: All modern browsers (Chrome/Edge/Firefox 103+, Safari 16.4+)
42
+ - Gracefully degrades if not supported
43
+ - **Performance note**: May improve or hurt page load performance depending on content - careful testing advised
44
+ - See [Early Hints Guide](docs/early_hints.md) for detailed usage and advanced patterns
45
+
46
+ ## [v9.3.0-beta.5] - October 18, 2025
15
47
 
16
48
  ### Added
17
49
 
@@ -33,6 +65,15 @@ Changes since the last non-beta release.
33
65
  - **Build timing logs** for webpack and rspack. [PR #706](https://github.com/shakacode/shakapacker/pull/706) by [justin808](https://github.com/justin808).
34
66
  - Shows duration of build operations
35
67
  - Helps identify performance bottlenecks
68
+ - **Named build configurations with `--build` flag**. [PR #728](https://github.com/shakacode/shakapacker/pull/728) by [justin808](https://github.com/justin808).
69
+ - Allows specifying custom build configurations: `bin/shakapacker --build=production` or `bin/shakapacker --build=test`
70
+ - Useful for creating specialized build configurations for different deployment environments
71
+ - **Build validation in `bin/export-bundler-config`**. [PR #717](https://github.com/shakacode/shakapacker/pull/717) by [justin808](https://github.com/justin808).
72
+ - Validates webpack/rspack configuration before export to catch errors early
73
+ - Provides clear error messages for configuration issues
74
+ - **Backward compatibility for rspack config in `config/webpack/`**. [PR #734](https://github.com/shakacode/shakapacker/pull/734) by [justin808](https://github.com/justin808).
75
+ - Rspack configurations can now be placed in `config/webpack/` directory for easier migration
76
+ - Maintains compatibility with existing project structures
36
77
  - Added Knip for detecting dead code to CI. [PR #675](https://github.com/shakacode/shakapacker/pull/675) by [justin808](https://github.com/justin808).
37
78
 
38
79
  ### Changed
@@ -60,6 +101,12 @@ Changes since the last non-beta release.
60
101
  - **Improved doctor command output** clarity and accuracy. [PR #682](https://github.com/shakacode/shakapacker/pull/682) by [justin808](https://github.com/justin808).
61
102
  - Better formatting and organization of diagnostic information
62
103
  - More actionable recommendations
104
+ - **Documented `content_for` pattern to prevent FOUC** with `stylesheet_pack_tag`. [PR #737](https://github.com/shakacode/shakapacker/pull/737) by [justin808](https://github.com/justin808).
105
+ - Added documentation on using `content_for` to prevent Flash of Unstyled Content
106
+ - Best practice patterns for managing stylesheet loading order
107
+ - **Improved upgrade documentation** to clarify dual Gemfile and package.json updates. [PR #731](https://github.com/shakacode/shakapacker/pull/731) by [justin808](https://github.com/justin808).
108
+ - Clearer instructions for upgrading both Ruby gem and NPM package
109
+ - Helps prevent version mismatch issues
63
110
  - Formatted all markdown files with prettier. [PR #673](https://github.com/shakacode/shakapacker/pull/673) by [justin808](https://github.com/justin808).
64
111
 
65
112
  ### Fixed
@@ -732,8 +779,8 @@ Note: [Rubygem is 6.3.0.pre.rc.1](https://rubygems.org/gems/shakapacker/versions
732
779
 
733
780
  See [CHANGELOG.md in rails/webpacker (up to v5.4.3)](https://github.com/rails/webpacker/blob/master/CHANGELOG.md)
734
781
 
735
- [Unreleased]: https://github.com/shakacode/shakapacker/compare/v9.3.0-beta.0...main
736
- [v9.3.0-beta.0]: https://github.com/shakacode/shakapacker/compare/v9.2.0...v9.3.0-beta.0
782
+ [Unreleased]: https://github.com/shakacode/shakapacker/compare/v9.3.0-beta.5...main
783
+ [v9.3.0-beta.5]: https://github.com/shakacode/shakapacker/compare/v9.2.0...v9.3.0-beta.5
737
784
  [v9.2.0]: https://github.com/shakacode/shakapacker/compare/v9.1.0...v9.2.0
738
785
  [v9.1.0]: https://github.com/shakacode/shakapacker/compare/v9.0.0...v9.1.0
739
786
  [v9.0.0]: https://github.com/shakacode/shakapacker/compare/v8.4.0...v9.0.0
data/CLAUDE.md CHANGED
@@ -27,6 +27,19 @@
27
27
  - Create small, focused PRs that are easy to review
28
28
  - Always create a PR immediately after pushing changes
29
29
 
30
+ ## Changelog
31
+
32
+ - **Update CHANGELOG.md for user-visible changes only**
33
+ - User-visible changes include: new features, bug fixes, breaking changes, deprecations, performance improvements
34
+ - **Do NOT add changelog entries for**: linting fixes, code formatting, internal refactoring, test updates, documentation fixes
35
+ - Non-user-visible changes don't need changelog entries even if they modify code
36
+ - **Format requirements**:
37
+ - Always link to the PR: `[PR #123](https://github.com/shakacode/shakapacker/pull/123)`
38
+ - Always link to the author: `by [username](https://github.com/username)`
39
+ - Keep formatting consistent with existing entries
40
+ - When releasing a version, update the version diff links at the bottom of CHANGELOG.md
41
+ - **For breaking changes**: Use bold formatting and link to migration documentation (e.g., `**Breaking**: Description. See [Migration Guide](docs/vX_upgrade.md)`)
42
+
30
43
  ## Shakapacker-Specific
31
44
 
32
45
  - This gem supports both webpack and rspack configurations
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- shakapacker (9.3.0.beta.4)
4
+ shakapacker (9.3.0.beta.6)
5
5
  activesupport (>= 5.2)
6
6
  package_json
7
7
  rack-proxy (>= 0.6.1)
data/README.md CHANGED
@@ -78,6 +78,7 @@ Read the [full review here](https://clutch.co/profile/shakacode#reviews?sort_by=
78
78
  - [View Helper: `image_pack_tag`](#view-helper-image_pack_tag)
79
79
  - [View Helper: `favicon_pack_tag`](#view-helper-favicon_pack_tag)
80
80
  - [View Helper: `preload_pack_asset`](#view-helper-preload_pack_asset)
81
+ - [View Helper: `send_pack_early_hints`](#view-helper-send_pack_early_hints)
81
82
  - [Images in Stylesheets](#images-in-stylesheets)
82
83
  - [Server-Side Rendering (SSR)](#server-side-rendering-ssr)
83
84
  - [Development](#development)
@@ -563,6 +564,24 @@ If you want to preload a static asset in your `<head>`, you can use the `preload
563
564
  <%= preload_pack_asset 'fonts/fa-regular-400.woff2' %>
564
565
  ```
565
566
 
567
+ #### HTTP 103 Early Hints
568
+
569
+ Automatically send early hints to browsers for faster asset loading. Supports `preload`/`prefetch`/`none` configuration per-page.
570
+
571
+ ```yaml
572
+ # config/shakapacker.yml
573
+ production:
574
+ early_hints:
575
+ enabled: true
576
+ debug: false # Enable to see what hints are sent (as HTML comments)
577
+ ```
578
+
579
+ ⚠️ **Important**: May improve or hurt performance depending on content. See the [Early Hints Guide](./docs/early_hints.md) for configuration, performance guidance, and examples.
580
+
581
+ **Troubleshooting**: Enable `debug: true` to see HTML comments showing what hints were sent or why they were skipped.
582
+
583
+ **Requirements:** Rails 5.2+, HTTP/2 server, modern browsers. Gracefully degrades if not supported.
584
+
566
585
  ### Images in Stylesheets
567
586
 
568
587
  If you want to use images in your stylesheets:
@@ -11,6 +11,8 @@ This guide covers all configuration options available in `config/shakapacker.yml
11
11
  - [Development Server](#development-server)
12
12
  - [Compilation Options](#compilation-options)
13
13
  - [Advanced Options](#advanced-options)
14
+ - [Subresource Integrity](#integrity)
15
+ - [Early Hints](#early_hints)
14
16
  - [Environment-Specific Configuration](#environment-specific-configuration)
15
17
  - [Build Configurations (config/shakapacker-builds.yml)](#build-configurations-configshakapacker-buildsyml)
16
18
 
@@ -485,6 +487,37 @@ integrity:
485
487
 
486
488
  See [Subresource Integrity Guide](subresource_integrity.md) for details.
487
489
 
490
+ ### `early_hints`
491
+
492
+ **Type:** `object`
493
+ **Default:** `{ enabled: false, css: "preload", js: "preload" }`
494
+ **Requires:** Rails 5.2+, HTTP/2 server
495
+
496
+ Automatically send HTTP 103 Early Hints for faster asset loading.
497
+
498
+ ```yaml
499
+ early_hints:
500
+ enabled: true # Master switch (default: false)
501
+ css: "preload" # 'preload' | 'prefetch' | 'none' (default: 'preload')
502
+ js: "preload" # 'preload' | 'prefetch' | 'none' (default: 'preload')
503
+ ```
504
+
505
+ **Options:**
506
+
507
+ - `enabled`: Enable/disable early hints (default: `false`)
508
+ - `css`: Hint type for CSS - `'preload'`, `'prefetch'`, or `'none'` (default: `'preload'`)
509
+ - `js`: Hint type for JS - `'preload'`, `'prefetch'`, or `'none'` (default: `'preload'`)
510
+
511
+ ⚠️ **Performance note**: May improve or hurt page load depending on content. Configure per-page for best results.
512
+
513
+ See the [Early Hints Guide](early_hints.md) for:
514
+
515
+ - Performance considerations and warnings
516
+ - Per-page configuration (`configure_pack_early_hints`)
517
+ - Dynamic configuration examples
518
+ - Hero image preloading with `preload_link_tag`
519
+ - Troubleshooting and testing recommendations
520
+
488
521
  ## Environment-Specific Configuration
489
522
 
490
523
  Shakapacker supports per-environment configuration with fallback logic:
@@ -728,4 +761,6 @@ See [Troubleshooting Guide](troubleshooting.md) for more help.
728
761
  - [Deployment Guide](deployment.md)
729
762
  - [CDN Setup Guide](cdn_setup.md)
730
763
  - [Precompile Hook Guide](precompile_hook.md)
764
+ - [Early Hints Guide](early_hints.md)
765
+ - [Subresource Integrity Guide](subresource_integrity.md)
731
766
  - [Troubleshooting Guide](troubleshooting.md)
@@ -0,0 +1,426 @@
1
+ # HTTP 103 Early Hints Guide
2
+
3
+ This guide shows you how to use HTTP 103 Early Hints with Shakapacker to optimize page load performance.
4
+
5
+ > **📚 Related Documentation:** For advanced manual control using `send_pack_early_hints` in controllers before expensive work, see [early_hints_manual_api.md](early_hints_manual_api.md).
6
+
7
+ ## What are Early Hints?
8
+
9
+ HTTP 103 Early Hints is emitted **after** Rails has finished rendering but **before** the final response is sent, allowing browsers to begin fetching resources (JS, CSS) prior to receiving the full HTML response. This may significantly improve page load performance or cause an equally significant regression, depending on the page's content.
10
+
11
+ ⚠️ **Critical**: Preloading JavaScript may hurt your LCP (Largest Contentful Paint) metric if you have large images, videos, or other content that should load first. **Careful experimentation and performance measurement is required.**
12
+
13
+ ### Priority Levels: Preload vs Prefetch vs None
14
+
15
+ Early hints let you control browser download priority for assets:
16
+
17
+ - **`preload`** - **Prioritize**: High priority, browser downloads immediately before HTML parsing. Use for critical assets needed for initial render.
18
+ - **`prefetch`** - **De-prioritize**: Low priority, browser downloads when idle (doesn't compete for bandwidth). Use for non-critical assets or future navigation.
19
+ - **`none`** - **Default behavior**: No hint sent. Browser discovers asset when parsing HTML (normal page load behavior).
20
+
21
+ ### Performance Considerations
22
+
23
+ ⚠️ **Important**: Different pages have different performance characteristics:
24
+
25
+ - **LCP Impact**: Preloading JS/CSS competes for bandwidth with images/videos, potentially delaying LCP
26
+ - **Hero Images**: Pages with large hero images usually perform **worse** with JS/CSS preload
27
+ - **Interactive Apps**: Dashboards and SPAs may benefit from aggressive JS preload
28
+ - **Content Sites**: Blogs and marketing sites often need conservative hints (prefetch or none)
29
+ - **Recommendation**: Configure hints **per-page** based on content, measure with real user data
30
+
31
+ ## Quick Start
32
+
33
+ ### 1. Global Configuration
34
+
35
+ ```yaml
36
+ # config/shakapacker.yml
37
+ production:
38
+ early_hints:
39
+ enabled: true # Master switch
40
+ css: "preload" # 'preload' | 'prefetch' | 'none'
41
+ js: "preload" # 'preload' | 'prefetch' | 'none'
42
+ ```
43
+
44
+ **Defaults**: When `enabled: true`, both `css` and `js` default to `'preload'` if not specified.
45
+
46
+ **Testing**: See the [Feature Testing Guide](feature_testing.md#http-103-early-hints) for detailed instructions on verifying early hints are working using browser DevTools or curl.
47
+
48
+ ### 2. Per-Page Configuration (Recommended)
49
+
50
+ Configure hints based on your page content:
51
+
52
+ ```ruby
53
+ class PostsController < ApplicationController
54
+ # Image-heavy landing page - don't compete with images
55
+ configure_pack_early_hints only: [:index], css: 'none', js: 'prefetch'
56
+
57
+ # Interactive post editor - JS is critical
58
+ configure_pack_early_hints only: [:edit], css: 'preload', js: 'preload'
59
+
60
+ # API endpoints - no hints needed
61
+ skip_send_pack_early_hints only: [:api_data]
62
+ end
63
+ ```
64
+
65
+ ### 3. Dynamic Configuration
66
+
67
+ Configure based on content:
68
+
69
+ ```ruby
70
+ class PostsController < ApplicationController
71
+ def show
72
+ @post = Post.find(params[:id])
73
+
74
+ if @post.has_hero_video?
75
+ # Video is LCP - don't compete
76
+ configure_pack_early_hints all: 'none'
77
+ elsif @post.interactive?
78
+ # JS needed for interactivity
79
+ configure_pack_early_hints css: 'prefetch', js: 'preload'
80
+ else
81
+ # Standard blog post
82
+ configure_pack_early_hints css: 'preload', js: 'prefetch'
83
+ end
84
+ end
85
+ end
86
+ ```
87
+
88
+ ### 4. Preloading Hero Images and Videos
89
+
90
+ Use Rails' built-in `preload_link_tag` to preload hero images, videos, and other LCP resources. Rails automatically sends these as early hints:
91
+
92
+ ```erb
93
+ <%# app/views/layouts/application.html.erb %>
94
+ <!DOCTYPE html>
95
+ <html>
96
+ <head>
97
+ <%= preload_link_tag image_path('hero.jpg'), as: 'image', type: 'image/jpeg' %>
98
+ <%= preload_link_tag video_path('intro.mp4'), as: 'video', type: 'video/mp4' %>
99
+ </head>
100
+ <body>
101
+ <%= yield %>
102
+ </body>
103
+ </html>
104
+ ```
105
+
106
+ **Dynamic preloading in views:**
107
+
108
+ ```erb
109
+ <%# app/views/posts/show.html.erb %>
110
+ <% if @post.hero_image_url.present? %>
111
+ <%= preload_link_tag @post.hero_image_url, as: 'image' %>
112
+ <% end %>
113
+ ```
114
+
115
+ **Benefits:**
116
+
117
+ - ✅ Standard Rails API - no custom Shakapacker code needed
118
+ - ✅ Automatically sends early hints when server supports it
119
+ - ✅ Works with `image_path`, `video_path`, `asset_path` helpers
120
+ - ✅ Supports all standard attributes: `as`, `type`, `crossorigin`, `integrity`
121
+
122
+ **When to preload images/videos:**
123
+
124
+ - Hero images that are LCP (Largest Contentful Paint) elements
125
+ - Above-the-fold images critical for initial render
126
+ - Background videos that play on page load
127
+
128
+ **Performance tip:** Don't over-preload! Each preload competes for bandwidth. Focus only on critical resources that improve LCP.
129
+
130
+ See [Rails preload_link_tag docs](https://api.rubyonrails.org/classes/ActionView/Helpers/AssetTagHelper.html#method-i-preload_link_tag) for full API.
131
+
132
+ ## Controller Configuration
133
+
134
+ #### Skip Early Hints Entirely
135
+
136
+ ```ruby
137
+ class ApiController < ApplicationController
138
+ # Skip for entire controller
139
+ skip_send_pack_early_hints
140
+ end
141
+
142
+ class PostsController < ApplicationController
143
+ # Skip for specific actions
144
+ skip_send_pack_early_hints only: [:api_endpoint, :feed]
145
+ end
146
+ ```
147
+
148
+ #### Configure Per Action (Class Method)
149
+
150
+ ```ruby
151
+ class PostsController < ApplicationController
152
+ # Configure specific actions
153
+ configure_pack_early_hints only: [:show], css: 'prefetch', js: 'preload'
154
+ configure_pack_early_hints only: [:gallery], css: 'none', js: 'none'
155
+
156
+ # Use 'all' shortcut
157
+ configure_pack_early_hints only: [:about], all: 'prefetch'
158
+
159
+ # Mix general and specific (specific wins)
160
+ configure_pack_early_hints only: [:dashboard], all: 'preload', css: 'prefetch'
161
+ # Result: css='prefetch', js='preload'
162
+ end
163
+ ```
164
+
165
+ #### Configure in Action Method
166
+
167
+ ```ruby
168
+ class PostsController < ApplicationController
169
+ def show
170
+ @post = Post.find(params[:id])
171
+
172
+ # Configure based on runtime logic
173
+ if @post.video_content?
174
+ configure_pack_early_hints css: 'none', js: 'none'
175
+ end
176
+ end
177
+ end
178
+ ```
179
+
180
+ #### Configure in Before Action
181
+
182
+ ```ruby
183
+ class PostsController < ApplicationController
184
+ before_action :optimize_for_images, only: [:gallery, :portfolio]
185
+
186
+ private
187
+
188
+ def optimize_for_images
189
+ configure_pack_early_hints css: 'prefetch', js: 'prefetch'
190
+ end
191
+ end
192
+ ```
193
+
194
+ ## Configuration Precedence
195
+
196
+ Settings are applied in this order (later overrides earlier):
197
+
198
+ 1. **Global** (shakapacker.yml) - project defaults
199
+ 2. **Controller class** (configure_pack_early_hints) - per-action defaults
200
+ 3. **Manual call** (send_pack_early_hints in view) - explicit override
201
+
202
+ Within a single configuration, `all:` is applied first, then specific `css:` and `js:` values override it.
203
+
204
+ ## Usage Examples by Scenario
205
+
206
+ ### Scenario 1: Image-Heavy Landing Page
207
+
208
+ **Problem**: Large hero image is LCP, JS/CSS hints compete for bandwidth and delay image loading
209
+
210
+ ```ruby
211
+ class HomeController < ApplicationController
212
+ def index
213
+ # Save bandwidth for hero image
214
+ configure_pack_early_hints css: 'none', js: 'prefetch'
215
+ end
216
+ end
217
+ ```
218
+
219
+ **Why**:
220
+
221
+ - `css: 'none'` - No hint sent, CSS discovered normally (saves bandwidth)
222
+ - `js: 'prefetch'` - Low priority hint, JS downloads when idle (doesn't compete)
223
+ - **Result**: Hero image gets full bandwidth priority for better LCP
224
+
225
+ ### Scenario 2: Interactive Dashboard
226
+
227
+ **Problem**: App is useless without JavaScript
228
+
229
+ ```ruby
230
+ class DashboardController < ApplicationController
231
+ # JS is critical for all actions
232
+ configure_pack_early_hints all: 'preload'
233
+ end
234
+ ```
235
+
236
+ **Why**: Fast JS load is more important than LCP
237
+
238
+ ### Scenario 3: Blog with Varied Content
239
+
240
+ **Problem**: Article pages have images, index doesn't
241
+
242
+ ```ruby
243
+ class ArticlesController < ApplicationController
244
+ # Index: no large images
245
+ configure_pack_early_hints only: [:index], css: 'preload', js: 'preload'
246
+
247
+ # Show: featured images
248
+ configure_pack_early_hints only: [:show], css: 'prefetch', js: 'prefetch'
249
+ end
250
+ ```
251
+
252
+ **Why**: Different pages have different performance needs
253
+
254
+ ### Scenario 4: Mixed Content Types
255
+
256
+ **Problem**: Posts contain videos, images, or interactive content
257
+
258
+ ```ruby
259
+ class PostsController < ApplicationController
260
+ def show
261
+ @post = Post.find(params[:id])
262
+
263
+ case @post.content_type
264
+ when 'video'
265
+ # Video is LCP
266
+ configure_pack_early_hints all: 'none'
267
+ when 'interactive'
268
+ # JS needed immediately
269
+ configure_pack_early_hints css: 'prefetch', js: 'preload'
270
+ when 'image_gallery'
271
+ # Images are LCP
272
+ configure_pack_early_hints all: 'prefetch'
273
+ else
274
+ # Standard text post
275
+ configure_pack_early_hints css: 'preload', js: 'prefetch'
276
+ end
277
+ end
278
+ end
279
+ ```
280
+
281
+ **Why**: Dynamic configuration based on actual content
282
+
283
+ ### Scenario 5: E-commerce Product Pages
284
+
285
+ **Problem**: Product images are critical, but checkout needs JS
286
+
287
+ ```ruby
288
+ class ProductsController < ApplicationController
289
+ # Product page: images are critical
290
+ configure_pack_early_hints only: [:show], css: 'prefetch', js: 'prefetch'
291
+
292
+ # Checkout: form validation needs JS
293
+ configure_pack_early_hints only: [:checkout], css: 'preload', js: 'preload'
294
+ end
295
+ ```
296
+
297
+ **Why**: Shopping vs checkout have different needs
298
+
299
+ ## How It Works
300
+
301
+ Shakapacker automatically sends early hints after your views render:
302
+
303
+ ```text
304
+ 1. Request arrives
305
+ 2. Controller action runs → Database queries, business logic
306
+ 3. Views render → append_javascript_pack_tag('admin')
307
+ 4. Layout renders → javascript_pack_tag, stylesheet_pack_tag
308
+ 5. after_action hook → Reads configuration and queues
309
+ 6. HTTP 103 sent → rel=preload or rel=prefetch based on config
310
+ 7. HTTP 200 sent → Full HTML response
311
+ ```
312
+
313
+ **Important timing note**: HTTP 103 is sent after rendering completes but before the final HTTP 200 response. This means:
314
+
315
+ - ✅ **Benefits**: Browser starts downloading assets while the server transmits the final HTML response
316
+ - ❌ **Limitations**: Does NOT help during database queries or view rendering—only helps with network transfer time
317
+ - 💡 **Best for**: Pages with large HTML responses where asset downloads can happen in parallel with HTML transmission
318
+
319
+ ## Advanced: Manual Control
320
+
321
+ Most apps should use controller configuration. For advanced use cases including:
322
+
323
+ - Sending hints **before** expensive controller work for maximum parallelism
324
+ - Per-pack customization in layouts
325
+ - View-specific logic
326
+
327
+ See the [Manual API Guide](early_hints_manual_api.md) for detailed examples and patterns.
328
+
329
+ ## Requirements
330
+
331
+ - **Rails 5.2+** (for `request.send_early_hints` support)
332
+ - **Web server with HTTP/2 and early hints:**
333
+ - Puma 5+ ✅
334
+ - nginx 1.13+ with ngx_http_v2_module ✅
335
+ - Other HTTP/2 servers with early hints support
336
+ - **Modern browsers:**
337
+ - Chrome/Edge/Firefox 103+ ✅
338
+ - Safari 16.4+ ✅
339
+
340
+ If requirements not met, feature gracefully degrades with no errors.
341
+
342
+ ## Quick Reference
343
+
344
+ ### Priority levels and when to use each:
345
+
346
+ - **`preload`** (Prioritize): Critical assets on text-heavy pages, SPAs, pages without large images
347
+ - **`prefetch`** (De-prioritize): Non-critical assets, pages with large LCP images/videos (downloads when idle)
348
+ - **`none`** (Default behavior): Image/video-heavy pages, API endpoints, SSR pages (no hint sent)
349
+
350
+ ### Testing checklist:
351
+
352
+ 1. Measure LCP with Chrome DevTools Performance tab
353
+ 2. Test on real mobile devices
354
+ 3. A/B test configurations with real user data
355
+ 4. Monitor field data with RUM tools
356
+ 5. Test each page type separately
357
+
358
+ ## Troubleshooting
359
+
360
+ For comprehensive testing instructions including browser DevTools and curl methods, see the [Feature Testing Guide: HTTP 103 Early Hints](feature_testing.md#http-103-early-hints).
361
+
362
+ ### Debug Mode
363
+
364
+ Enable debug mode to see what early hints are being sent (or why they weren't sent):
365
+
366
+ ```yaml
367
+ # config/shakapacker.yml
368
+ production:
369
+ early_hints:
370
+ enabled: true
371
+ debug: true # Outputs debug info as HTML comments
372
+ ```
373
+
374
+ Debug mode adds HTML comments to your page showing:
375
+
376
+ - Whether hints were sent or skipped
377
+ - What pack names were processed
378
+ - What Link headers were sent
379
+ - HTTP/2 support status
380
+
381
+ View page source and look for `<!-- Shakapacker Early Hints Debug -->` comments.
382
+
383
+ **Early hints not appearing:**
384
+
385
+ - **Enable debug mode first** to see what's happening
386
+ - **Check for proxy stripping**: If debug shows hints sent but curl/DevTools don't show `HTTP/2 103`, your reverse proxy or CDN (Control Plane, Cloudflare, AWS ALB/ELB, nginx) is likely stripping 103 responses. This is the **most common cause** of "missing" early hints
387
+ - Check `early_hints: enabled: true` in shakapacker.yml
388
+ - Verify HTTP/2 server (Puma 5+, nginx 1.13+)
389
+ - Check Network tab shows "h2" protocol and 103 status
390
+
391
+ **Reverse proxy stripping 103 responses:**
392
+
393
+ If debug mode shows hints are sent but they're not reaching clients, configure your proxy:
394
+
395
+ - **nginx**: Add `proxy_pass_header Link;` to pass through early hints (nginx 1.13+)
396
+ - **Cloudflare**: Enable "Early Hints" in Speed > Optimization (paid plans only)
397
+ - **AWS ALB/ELB**: Not supported - ALBs strip 103 responses. Workaround: skip ALB or use CloudFront
398
+ - **Control Plane**: Appears to strip 103 - Contact their support if you need early hints
399
+
400
+ See the [Feature Testing Guide](feature_testing.md#troubleshooting-early-hints) for detailed proxy configuration examples.
401
+
402
+ **Performance got worse:**
403
+
404
+ - Page likely has large images/videos as LCP
405
+ - Try `css: 'prefetch', js: 'prefetch'` or `all: 'none'`
406
+ - Measure LCP before and after changes
407
+
408
+ **Wrong hints sent:**
409
+
410
+ - Check configuration precedence (global → controller → manual)
411
+ - Verify values are strings: `'preload'` not `:preload`
412
+ - Check for typos (case-sensitive)
413
+
414
+ ## References
415
+
416
+ ### Shakapacker Documentation
417
+
418
+ - [Feature Testing Guide: HTTP 103 Early Hints](feature_testing.md#http-103-early-hints) - Detailed testing instructions with browser DevTools and curl
419
+
420
+ ### External Resources
421
+
422
+ - [Rails API: send_early_hints](https://api.rubyonrails.org/classes/ActionDispatch/Request.html#method-i-send_early_hints)
423
+ - [RFC 8297: HTTP Early Hints](https://datatracker.ietf.org/doc/html/rfc8297)
424
+ - [MDN: rel=preload vs rel=prefetch](https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/rel)
425
+ - [Web.dev: Optimize LCP](https://web.dev/optimize-lcp/)
426
+ - [HTTP 103 Explained](https://http.dev/103)