Make WordPress Core

Opened 9 months ago

Closed 4 months ago

Last modified 3 weeks ago

#63486 closed enhancement (fixed)

Script modules should support being printed in the footer the same as classic scripts

Reported by: westonruter's profile westonruter Owned by: westonruter's profile westonruter
Milestone: 6.9 Priority: normal
Severity: normal Version: 6.5
Component: Script Loader Keywords: has-patch needs-testing has-unit-tests commit has-dev-note
Focuses: javascript, performance Cc:

Description (last modified by westonruter)

Script modules are always printed at wp_head in block themes. This makes sense in that script modules have the defer behavior by default, so they do not block rendering. By placing them in the head, they can be discovered early which is helpful when any script modules are in the critical rendering path. However, all script modules in WordPress core are for the Interactivity API which has server-side rendering as a top goal/requirement:

It must support server-side rendering. Server-rendered HTML and client-hydrated HTML must be exactly the same. This is important for SEO and the user experience.

Therefore, all script modules for the Interactivity API should be deprioritized to not compete with the loading of any resources needed in the critical rendering path, especially any image resource for the LCP element. One way to reduce the priority of the script modules is to add fetchpriority=low to the script tags (#61734), and the other way is to print the script module in the footer. Implementing both deprioritization techniques yields the highest improvement to LCP in my testing.

I published an in-depth post (Improve LCP by Deprioritizing Script Modules from the Interactivity API) with benchmarking results, showing that moving Interactivity API script modules from wp_head to wp_footer alone can improve LCP by ~7%.

Attachments (1)

63486.diff (4.3 KB) - added by b1ink0 5 months ago.
Patch to add script modules footer placement support

Download all attachments as: .zip

Change History (44)

#1 @westonruter
9 months ago

  • Description modified (diff)

#2 @westonruter
9 months ago

  • Description modified (diff)

This ticket was mentioned in Slack in #core-performance by westonruter. View the logs.


9 months ago

This ticket was mentioned in PR #8995 on WordPress/wordpress-develop by @vipulpatil.


8 months ago
#4

  • Keywords has-patch added; needs-patch removed

## Summary
This PR adds support for printing script modules in the footer.
It also works with premap loaded in the header.

### Changes Made

  • Added a parameter $in_footer to the wp_register_script_module function.
  • The printing of default scripts using wp_default_script_modules remains unchanged to avoid any potential breakage. If needed, we can conditionally set $in_footer to true to print specific scripts in the footer.

### Why

  • This change supports the ongoing effort to print the Interactivity API module in the footer.

#5 @westonruter
8 months ago

  • Milestone changed from Future Release to 6.9

This ticket was mentioned in Slack in #core-performance by westonruter. View the logs.


6 months ago

#7 @westonruter
6 months ago

  • Keywords needs-refresh added

Now that #61734 has been fixed with fetchpriority support added, this ticket can proceed with the additional step of allowing script modules to be printed in the footer. @vipulpatil Would you like to update your PR to resolve the merge conflicts?

#8 @westonruter
6 months ago

  • Type changed from defect (bug) to enhancement

@b1ink0
5 months ago

Patch to add script modules footer placement support

This ticket was mentioned in PR #9867 on WordPress/wordpress-develop by @b1ink0.


5 months ago
#9

  • Keywords needs-refresh removed

Trac ticket: https://core.trac.wordpress.org/ticket/63486

This pull request introduces support for printing script modules in the footer.

This ticket was mentioned in Slack in #core by welcher. View the logs.


5 months ago

#11 @welcher
5 months ago

  • Keywords needs-testing added

#12 @davidbaumwald
5 months ago

Discussed briefly during a bug scrub for 6.9 by @welcher. Updating the keywords to add needs-testing.

#14 @westonruter
5 months ago

  • Keywords needs-unit-tests added

Yes, we'll need unit tests for this.

#15 @westonruter
5 months ago

Something that comes to mind: if fetchpriority is set to low and in_footer is not explicitly set to false, then I think it would make sense to default in_footer to true. Thus would ensure the lowest of priorities.

#16 @westonruter
5 months ago

  • Owner set to b1ink0
  • Status changed from new to assigned

#17 @westonruter
4 months ago

Possible use case for printing script modules in the footer: update the emoji-loader to load from an external file rather than be inlined. See comment on #64076.

@westonruter commented on PR #9867:


4 months ago
#18

@b1ink0 sorry for the delay. I had to commit two prerequisite changes: 9d03e8e15118cf2445fee04f567b09ee750cbaa4 and fe9b235544a45b6b035e6e02f0c373d67ea5c224. Unfortunately these mean there are now some merge conflicts. Could you resolve?

@b1ink0 commented on PR #9867:


4 months ago
#19

@b1ink0 sorry for the delay. I had to commit two prerequisite changes: 9d03e8e15118cf2445fee04f567b09ee750cbaa4 and fe9b235544a45b6b035e6e02f0c373d67ea5c224. Unfortunately these mean there are now some merge conflicts. Could you resolve?

Yes will do it.

@b1ink0 commented on PR #9867:


4 months ago
#20

Merge conflict resolved.
cc: @westonruter

@b1ink0 commented on PR #9867:


4 months ago
#21

With the latest commit, the last use of the get_marked_for_enqueue method has been removed. Since this method is private, it should be safe to remove without any backward compatibility issues right?.

@westonruter commented on PR #9867:


4 months ago
#22

@b1ink0 In the course of reviewing this, I added PHPStan types (temporarily) to attain level 10 compliance, to have additional sanity checks on the changes (as you pointed out an unused method could be removed). In the process, I came to refactor some of the methods (with help from Gemini) to eliminate some of the closures and recursive calls, in particular to get_dependencies(), get_recursive_dependents(), and get_sorted_dependencies():

https://github.com/WordPress/wordpress-develop/pull/9867/files/c8ccf33647a379e81845166b1cdac6177541e287..2438a129cbd022a13b9c60fa2258b7fbf26cb85e

I believe it is more readable now.

The output of the tests you can see is largely the same, except for the order of the preload links which are now reflecting the topological sort.

I'm going to ruminate on this some more, but I welcome your additional revisions and feedback so that we can then commit this on Monday before beta1 on Tuesday.

@westonruter commented on PR #9867:


4 months ago
#23

I'm running benchmarks on the changes and I'm struggling to find an improvement with moving script modules to the footer.

I'm testing with Twenty Twenty-Five with an Image block that has lightbox enabled:

https://github.com/user-attachments/assets/bf055095-608a-472a-8543-07af1f2f523e

I'm also forcing SCRIPT_DEBUG to be off and I'm increasing the CSS inline limit to 75K.

I even went the extra mile and moved the modulepreload link to the footer as well.

Network log:

Before | After

--

https://github.com/user-attachments/assets/f7306d78-c6cd-435b-a75c-02b8d16abe1d | https://github.com/user-attachments/assets/5d6495e1-991d-439b-bb84-c70b74f6c5f7

Diff in the HTML:

  • .html

    old new  
    30083008                                }
    30093009                        }
    30103010                </script>
    3011                 <script
    3012                         type="module"
    3013                         src="http://localhost:8000/wp-includes/js/dist/script-modules/block-library/navigation/view.min.js?ver=61572d447d60c0aa5240"
    3014                         id="@wordpress/block-library/navigation/view-js-module"
    3015                         fetchpriority="low"
    3016                 ></script>
    3017                 <script
    3018                         type="module"
    3019                         src="http://localhost:8000/wp-includes/js/dist/script-modules/block-library/image/view.min.js?ver=e38a2f910342023b9d19"
    3020                         id="@wordpress/block-library/image/view-js-module"
    3021                         fetchpriority="low"
    3022                 ></script>
    3023                 <link
    3024                         rel="modulepreload"
    3025                         href="http://localhost:8000/wp-includes/js/dist/script-modules/interactivity/index.min.js?ver=55aebb6e0a16726baffb"
    3026                         id="@wordpress/interactivity-js-modulepreload"
    3027                         fetchpriority="low"
    3028                 />
    30293011                <style class="wp-fonts-local">
    30303012                        @font-face {
    30313013                                font-family: Manrope;
     
    40654047                                ]
    40664048                        }
    40674049                </script>
     4050                <script
     4051                        type="module"
     4052                        src="http://localhost:8000/wp-includes/js/dist/script-modules/block-library/navigation/view.min.js?ver=61572d447d60c0aa5240"
     4053                        id="@wordpress/block-library/navigation/view-js-module"
     4054                        fetchpriority="low"
     4055                ></script>
     4056                <script
     4057                        type="module"
     4058                        src="http://localhost:8000/wp-includes/js/dist/script-modules/block-library/image/view.min.js?ver=e38a2f910342023b9d19"
     4059                        id="@wordpress/block-library/image/view-js-module"
     4060                        fetchpriority="low"
     4061                ></script>
     4062                <link
     4063                        rel="modulepreload"
     4064                        href="http://localhost:8000/wp-includes/js/dist/script-modules/interactivity/index.min.js?ver=55aebb6e0a16726baffb"
     4065                        id="@wordpress/interactivity-js-modulepreload"
     4066                        fetchpriority="low"
     4067                />
    40684068                <script
    40694069                        type="application/json"
    40704070                        id="wp-script-module-data-@wordpress/interactivity"

I tested 100 requests with and without these changes for the following network conditions:

### Fast 4G

npm run research -- benchmark-web-vitals --url='http://localhost:8000/2025/06/25/lightbox/?active_plugins=increase-styles-inline-size-limit&script_debug=0&script_modules_in_footer=false' --url='http://localhost:8000/2025/06/25/lightbox/?active_plugins=increase-styles-inline-size-limit&script_debug=0&script_modules_in_footer=true' --network-conditions='Fast 4G' --diff --output=md --number=100 | tee fast-4g-script-modules-in-footer.md
Metric Before After Diff (ms) Diff (%)
:--------------------------:-------: --------: -------:
FCP (median) 273.5 273.2 -0.30 -0.1%
LCP (median) 692.4 699.2 +6.80 +1.0%
TTFB (median) 108.2 107.05 -1.15 -1.1%
TTLB (median) 281.6 283.3 +1.70 +0.6%
LCP-TTFB (median) 584.2 591.25 +7.05 +1.2%
TTLB-TTFB (median) 173.55 175.95 +2.40 +1.4%

### Broadband

npm run research -- benchmark-web-vitals --url='http://localhost:8000/2025/06/25/lightbox/?active_plugins=increase-styles-inline-size-limit&script_debug=0&script_modules_in_footer=false' --url='http://localhost:8000/2025/06/25/lightbox/?active_plugins=increase-styles-inline-size-limit&script_debug=0&script_modules_in_footer=true' --network-conditions='broadband' --diff --output=md --number=100 | tee broadband-script-modules-in-footer.md
Metric Before After Diff (ms) Diff (%)
:--------------------------:-------: --------: -------:
FCP (median) 196.5 193.75 -2.75 -1.4%
LCP (median) 441.05 440.85 -0.20 -0.0%
TTFB (median) 108.15 106.9 -1.25 -1.2%
TTLB (median) 198.8 197.3 -1.50 -0.8%
LCP-TTFB (median) 332.35 334.3 +1.95 +0.6%
TTLB-TTFB (median) 90.5 89.85 -0.65 -0.7%

@westonruter commented on PR #9867:


4 months ago
#24

Here's the difference for _just_ adding fetchpriority=low:

  • script-modules-in-head-fetchpriority.

    old new  
    30123012                        type="module"
    30133013                        src="http://localhost:8000/wp-includes/js/dist/script-modules/block-library/navigation/view.min.js?ver=61572d447d60c0aa5240"
    30143014                        id="@wordpress/block-library/navigation/view-js-module"
     3015                        fetchpriority="low"
    30153016                ></script>
    30163017                <script
    30173018                        type="module"
    30183019                        src="http://localhost:8000/wp-includes/js/dist/script-modules/block-library/image/view.min.js?ver=e38a2f910342023b9d19"
    30193020                        id="@wordpress/block-library/image/view-js-module"
     3021                        fetchpriority="low"
    30203022                ></script>
    30213023                <link
    30223024                        rel="modulepreload"
    30233025                        href="http://localhost:8000/wp-includes/js/dist/script-modules/interactivity/index.min.js?ver=55aebb6e0a16726baffb"
    30243026                        id="@wordpress/interactivity-js-modulepreload"
     3027                        fetchpriority="low"
    30253028                />
    30263029                <style class="wp-fonts-local">
    30273030                        @font-face {

<details><summary>Test Script</summary>

number=100;
before_url='http://localhost:8000/2025/06/25/lightbox/?active_plugins=increase-styles-inline-size-limit&script_debug=0&script_modules_in_footer=false&module_fetchpriority=auto';
after_url='http://localhost:8000/2025/06/25/lightbox/?active_plugins=increase-styles-inline-size-limit&script_debug=0&script_modules_in_footer=false&module_fetchpriority=low';
curl "$before_url" > script-modules-in-head-fetchpriority.before.html
prettier --write script-modules-in-head-fetchpriority.before.html
curl "$after_url" > script-modules-in-head-fetchpriority.after.html
prettier --write script-modules-in-head-fetchpriority.after.html
npm run research -- benchmark-web-vitals --url="$before_url" --url="$after_url" --network-conditions='broadband' --diff --output=md --number=$number | tee script-modules-in-head-fetchpriority-broadband.md;
npm run research -- benchmark-web-vitals --url="$before_url" --url="$after_url" --network-conditions='Fast 4G'   --diff --output=md --number=$number | tee script-modules-in-head-fetchpriority-fast-4g.md

</details>

### Broadband

Metric Before After Diff (ms) Diff (%)
:--------------------------:------: --------: -------:
FCP (median) 198.15 195.9 -2.25 -1.1%
LCP (median) 441.65 439.3 -2.35 -0.5%
TTFB (median) 108.6 107.3 -1.30 -1.2%
TTLB (median) 199.7 198.1 -1.60 -0.8%
LCP-TTFB (median) 333.15 332.5 -0.65 -0.2%
TTLB-TTFB (median) 91 90.8 -0.20 -0.2%

### Fast 4G

Metric Before After Diff (ms) Diff (%)
:--------------------------:-------: --------: -------:
FCP (median) 276.2 274.7 -1.50 -0.5%
LCP (median) 697.4 693.4 -4.00 -0.6%
TTFB (median) 107.3 109.35 +2.05 +1.9%
TTLB (median) 284.95 283.25 -1.70 -0.6%
LCP-TTFB (median) 589.9 583.3 -6.60 -1.1%
TTLB-TTFB (median) 177.5 173.55 -3.95 -2.2%

@westonruter commented on PR #9867:


4 months ago
#25

I guess I'm not seeing the same performance impact as in my previous research because my test post doesn't have all the same interactive blocks.

@westonruter commented on PR #9867:


4 months ago
#26

Revisiting the performance impact of moving script modules and modulepreload links to the footer, now that I've added the current full suite of interactive blocks.

HTML diff:

  • script-modules-in-footer.

    old new  
    33133313                                }
    33143314                        }
    33153315                </script>
    3316                 <script
    3317                         type="module"
    3318                         src="http://localhost:8000/wp-includes/js/dist/script-modules/block-library/navigation/view.min.js?ver=61572d447d60c0aa5240"
    3319                         id="@wordpress/block-library/navigation/view-js-module"
    3320                         fetchpriority="low"
    3321                 ></script>
    3322                 <script
    3323                         type="module"
    3324                         src="http://localhost:8000/wp-includes/js/dist/script-modules/block-library/image/view.min.js?ver=e38a2f910342023b9d19"
    3325                         id="@wordpress/block-library/image/view-js-module"
    3326                         fetchpriority="low"
    3327                 ></script>
    3328                 <script
    3329                         type="module"
    3330                         src="http://localhost:8000/wp-includes/js/dist/script-modules/block-library/search/view.min.js?ver=208bf143e4074549fa89"
    3331                         id="@wordpress/block-library/search/view-js-module"
    3332                         fetchpriority="low"
    3333                 ></script>
    3334                 <script
    3335                         type="module"
    3336                         src="http://localhost:8000/wp-includes/js/dist/script-modules/block-library/file/view.min.js?ver=fdc2f6842e015af83140"
    3337                         id="@wordpress/block-library/file/view-js-module"
    3338                         fetchpriority="low"
    3339                 ></script>
    3340                 <script
    3341                         type="module"
    3342                         src="http://localhost:8000/wp-includes/js/dist/script-modules/block-library/query/view.min.js?ver=f55e93a1ad4806e91785"
    3343                         id="@wordpress/block-library/query/view-js-module"
    3344                         fetchpriority="low"
    3345                 ></script>
    3346                 <link
    3347                         rel="modulepreload"
    3348                         href="http://localhost:8000/wp-includes/js/dist/script-modules/interactivity/index.min.js?ver=55aebb6e0a16726baffb"
    3349                         id="@wordpress/interactivity-js-modulepreload"
    3350                         fetchpriority="low"
    3351                 />
    33523316                <style class="wp-fonts-local">
    33533317                        @font-face {
    33543318                                font-family: Manrope;
     
    54725436                                ]
    54735437                        }
    54745438                </script>
     5439                <script
     5440                        type="module"
     5441                        src="http://localhost:8000/wp-includes/js/dist/script-modules/block-library/navigation/view.min.js?ver=61572d447d60c0aa5240"
     5442                        id="@wordpress/block-library/navigation/view-js-module"
     5443                        fetchpriority="low"
     5444                ></script>
     5445                <script
     5446                        type="module"
     5447                        src="http://localhost:8000/wp-includes/js/dist/script-modules/block-library/image/view.min.js?ver=e38a2f910342023b9d19"
     5448                        id="@wordpress/block-library/image/view-js-module"
     5449                        fetchpriority="low"
     5450                ></script>
     5451                <script
     5452                        type="module"
     5453                        src="http://localhost:8000/wp-includes/js/dist/script-modules/block-library/search/view.min.js?ver=208bf143e4074549fa89"
     5454                        id="@wordpress/block-library/search/view-js-module"
     5455                        fetchpriority="low"
     5456                ></script>
     5457                <script
     5458                        type="module"
     5459                        src="http://localhost:8000/wp-includes/js/dist/script-modules/block-library/file/view.min.js?ver=fdc2f6842e015af83140"
     5460                        id="@wordpress/block-library/file/view-js-module"
     5461                        fetchpriority="low"
     5462                ></script>
    54755463                <script
     5464                        type="module"
     5465                        src="http://localhost:8000/wp-includes/js/dist/script-modules/block-library/query/view.min.js?ver=f55e93a1ad4806e91785"
     5466                        id="@wordpress/block-library/query/view-js-module"
     5467                        fetchpriority="low"
     5468                ></script>
     5469                <link
     5470                        rel="modulepreload"
     5471                        href="http://localhost:8000/wp-includes/js/dist/script-modules/interactivity/index.min.js?ver=55aebb6e0a16726baffb"
     5472                        id="@wordpress/interactivity-js-modulepreload"
     5473                        fetchpriority="low"
     5474                />
     5475                <script
    54765476                        type="application/json"
    54775477                        id="wp-script-module-data-@wordpress/interactivity"
    54785478                >

<details><summary>Benchmark Script</summary>

number=100;
before_url='http://localhost:8000/2025/06/25/lightbox/?active_plugins=increase-styles-inline-size-limit&script_debug=0&script_modules_in_footer=false';
after_url='http://localhost:8000/2025/06/25/lightbox/?active_plugins=increase-styles-inline-size-limit&script_debug=0&script_modules_in_footer=true';
curl "$before_url" > script-modules-in-footer.before.html
prettier --write script-modules-in-footer.before.html
curl "$after_url" > script-modules-in-footer.after.html
prettier --write script-modules-in-footer.after.html
npm run research -- benchmark-web-vitals --url="$before_url" --url="$after_url" --network-conditions='broadband' --diff --output=md --number=$number | tee script-modules-in-footer-broadband.md;
npm run research -- benchmark-web-vitals --url="$before_url" --url="$after_url" --network-conditions='Fast 4G'   --diff --output=md --number=$number | tee script-modules-in-footer-fast-4g.md

</details>

### Broadband

Negligible difference in LCP (even a slight degradation, which could be due to noise):

Metric Before After Diff (ms) Diff (%)
:----------------- -------:-------: --------: -------:
FCP (median) 213.3 211.9 -1.40 -0.7%
LCP (median) 466.1 470.35 +4.25 +0.9%
TTFB (median) 116.4 116.4 0.00 0.0%
TTLB (median) 247.5 236.5 -11.00 -4.4%
LCP-TTFB (median) 349.2 353.45 +4.25 +1.2%

### Fast 4G

Metric Before After Diff (ms) Diff (%)
:----------------- -------:-------: --------: -------:
FCP (median) 282.7 282.7 0.00 0.0%
LCP (median) 783.5 714.6 -68.90 -8.8%
TTFB (median) 118.05 117.35 -0.70 -0.6%
TTLB (median) 320.15 320.85 +0.70 +0.2%
LCP-TTFB (median) 663.8 597.55 -66.25 -10.0%

_Finally_, here is some validation. A significant improvement to LCP.

But note how I've also moved the modulepreload link to the footer. I think we need to print all modulepreload links in the footer for script modules being printed in the footer. Otherwise, the modulepreload links will appear in the head and will content for critical network resources.

@westonruter commented on PR #9867:


4 months ago
#27

Well, maybe it's not so urgent to print modulepreload links in the footer, although it would make sense to do so.

I did more benchmarking, this time leaving modulepreload links in HEAD, but printing script modules in the footer.

<details><summary>Benchmark Script</summary>

number=100;
before_url='http://localhost:8000/2025/06/25/lightbox/?active_plugins=increase-styles-inline-size-limit&script_debug=0&script_modules_in_footer=false';
after_url='http://localhost:8000/2025/06/25/lightbox/?active_plugins=increase-styles-inline-size-limit&script_debug=0&script_modules_in_footer=true';
curl "$before_url" > script-modules-in-footer.before.html
prettier --write script-modules-in-footer.before.html
curl "$after_url" > script-modules-in-footer.after.html
prettier --write script-modules-in-footer.after.html
npm run research -- benchmark-web-vitals --url="$before_url" --url="$after_url" --network-conditions='Fast 4G'   --diff --output=md --number=$number | tee script-modules-in-footer-fast-4g.md;
npm run research -- benchmark-web-vitals --url="$before_url" --url="$after_url" --network-conditions='broadband' --diff --output=md --number=$number | tee script-modules-in-footer-broadband.md;

</details>

HTML Diff:

  • script-modules-in-footer.

    old new  
    33133313                                }
    33143314                        }
    33153315                </script>
    3316                 <script
    3317                         type="module"
    3318                         src="http://localhost:8000/wp-includes/js/dist/script-modules/block-library/navigation/view.min.js?ver=61572d447d60c0aa5240"
    3319                         id="@wordpress/block-library/navigation/view-js-module"
    3320                         fetchpriority="low"
    3321                 ></script>
    3322                 <script
    3323                         type="module"
    3324                         src="http://localhost:8000/wp-includes/js/dist/script-modules/block-library/image/view.min.js?ver=e38a2f910342023b9d19"
    3325                         id="@wordpress/block-library/image/view-js-module"
    3326                         fetchpriority="low"
    3327                 ></script>
    3328                 <script
    3329                         type="module"
    3330                         src="http://localhost:8000/wp-includes/js/dist/script-modules/block-library/search/view.min.js?ver=208bf143e4074549fa89"
    3331                         id="@wordpress/block-library/search/view-js-module"
    3332                         fetchpriority="low"
    3333                 ></script>
    3334                 <script
    3335                         type="module"
    3336                         src="http://localhost:8000/wp-includes/js/dist/script-modules/block-library/file/view.min.js?ver=fdc2f6842e015af83140"
    3337                         id="@wordpress/block-library/file/view-js-module"
    3338                         fetchpriority="low"
    3339                 ></script>
    3340                 <script
    3341                         type="module"
    3342                         src="http://localhost:8000/wp-includes/js/dist/script-modules/block-library/query/view.min.js?ver=f55e93a1ad4806e91785"
    3343                         id="@wordpress/block-library/query/view-js-module"
    3344                         fetchpriority="low"
    3345                 ></script>
    33463316                <link
    33473317                        rel="modulepreload"
    33483318                        href="http://localhost:8000/wp-includes/js/dist/script-modules/interactivity/index.min.js?ver=55aebb6e0a16726baffb"
     
    54725442                                ]
    54735443                        }
    54745444                </script>
     5445                <script
     5446                        type="module"
     5447                        src="http://localhost:8000/wp-includes/js/dist/script-modules/block-library/navigation/view.min.js?ver=61572d447d60c0aa5240"
     5448                        id="@wordpress/block-library/navigation/view-js-module"
     5449                        fetchpriority="low"
     5450                ></script>
     5451                <script
     5452                        type="module"
     5453                        src="http://localhost:8000/wp-includes/js/dist/script-modules/block-library/image/view.min.js?ver=e38a2f910342023b9d19"
     5454                        id="@wordpress/block-library/image/view-js-module"
     5455                        fetchpriority="low"
     5456                ></script>
     5457                <script
     5458                        type="module"
     5459                        src="http://localhost:8000/wp-includes/js/dist/script-modules/block-library/search/view.min.js?ver=208bf143e4074549fa89"
     5460                        id="@wordpress/block-library/search/view-js-module"
     5461                        fetchpriority="low"
     5462                ></script>
     5463                <script
     5464                        type="module"
     5465                        src="http://localhost:8000/wp-includes/js/dist/script-modules/block-library/file/view.min.js?ver=fdc2f6842e015af83140"
     5466                        id="@wordpress/block-library/file/view-js-module"
     5467                        fetchpriority="low"
     5468                ></script>
     5469                <script
     5470                        type="module"
     5471                        src="http://localhost:8000/wp-includes/js/dist/script-modules/block-library/query/view.min.js?ver=f55e93a1ad4806e91785"
     5472                        id="@wordpress/block-library/query/view-js-module"
     5473                        fetchpriority="low"
     5474                ></script>
    54755475                <script
    54765476                        type="application/json"
    54775477                        id="wp-script-module-data-@wordpress/interactivity"

### Broadband

Metric Before After Diff (ms) Diff (%)
:--------------------------:-------: --------: -------:
FCP (median) 213.55 213 -0.55 -0.3%
LCP (median) 468.3 463.3 -5.00 -1.1%
TTFB (median) 118.15 116.55 -1.60 -1.4%
TTLB (median) 246.9 241.9 -5.00 -2.0%
LCP-TTFB (median) 349.45 346.8 -2.65 -0.8%

### Fast 4G

Metric Before After Diff (ms) Diff (%)
:--------------------------:-------: --------: -------:
FCP (median) 282.8 284.65 +1.85 +0.7%
LCP (median) 784.55 703.2 -81.35 -10.4%
TTFB (median) 117.4 116.45 -0.95 -0.8%
TTLB (median) 320.8 321.65 +0.85 +0.3%
LCP-TTFB (median) 666.85 587.25 -79.60 -11.9%

If anything, the LCP is even better. I expect it would degrade if there were more than just one modulepreload link.

So perhaps we should hold off on adding the modulepreload link for a subsequent ticket after we've gathered more data.

@westonruter commented on PR #9867:


4 months ago
#28

For reference, these query parameters I was using in the hacked codebase like as follows:

diff --cc src/wp-includes/blocks.php
index 22ef6d086d,22ef6d086d..186397f22f
--- a/src/wp-includes/blocks.php
+++ b/src/wp-includes/blocks.php
@@@ -181,8 -181,8 +181,8 @@@ function register_block_script_module_i
                ( isset( $metadata['supports']['interactivity'] ) && true === $metadata['supports']['interactivity'] ) ||
                ( isset( $metadata['supports']['interactivity']['interactive'] ) && true === $metadata['supports']['interactivity']['interactive'] )
        ) {
--              $args['fetchpriority'] = 'low';
--              $args['in_footer']     = true;
++              $args['fetchpriority'] = isset( $_GET['module_fetchpriority'] ) ? $_GET['module_fetchpriority'] : 'low';
++              $args['in_footer']     = isset( $_GET['script_modules_in_footer'] ) ? rest_sanitize_boolean( $_GET['script_modules_in_footer'] ) : true;
        }
  
        wp_register_script_module(
diff --cc src/wp-includes/class-wp-script-modules.php
index c9c20ffd2b,c9c20ffd2b..b0a3015bd1
--- a/src/wp-includes/class-wp-script-modules.php
+++ b/src/wp-includes/class-wp-script-modules.php
@@@ -345,7 -345,7 +345,12 @@@ class WP_Script_Modules 
                        add_action( 'wp_head', array( $this, 'print_head_enqueued_script_modules' ) );
                }
                add_action( 'wp_footer', array( $this, 'print_enqueued_script_modules' ) );
--              add_action( wp_is_block_theme() ? 'wp_head' : 'wp_footer', array( $this, 'print_script_module_preloads' ) );
++
++//            if ( isset( $_GET['script_modules_in_footer'] ) && rest_sanitize_boolean( $_GET['script_modules_in_footer'] ) ) {
++//                    add_action( 'wp_footer', array( $this, 'print_script_module_preloads' ) );
++//            } else {
++                      add_action( wp_is_block_theme() ? 'wp_head' : 'wp_footer', array( $this, 'print_script_module_preloads' ) );
++//            }
  
                add_action( 'admin_print_footer_scripts', array( $this, 'print_import_map' ) );
                add_action( 'admin_print_footer_scripts', array( $this, 'print_enqueued_script_modules' ) );
diff --cc src/wp-includes/script-modules.php
index 02588f740e,02588f740e..3867b50740
--- a/src/wp-includes/script-modules.php
+++ b/src/wp-includes/script-modules.php
@@@ -198,8 -198,8 +198,8 @@@ function wp_default_script_modules() 
                        str_starts_with( $script_module_id, '@wordpress/block-library' ) ||
                        '@wordpress/a11y' === $script_module_id
                ) {
--                      $args['fetchpriority'] = 'low';
--                      $args['in_footer']     = true;
++                      $args['fetchpriority'] = isset( $_GET['module_fetchpriority'] ) ? $_GET['module_fetchpriority'] : 'low';
++                      $args['in_footer']     = isset( $_GET['script_modules_in_footer'] ) ? rest_sanitize_boolean( $_GET['script_modules_in_footer'] ) : true;
                }
  
                $path = includes_url( "js/dist/script-modules/{$file_name}" );

@westonruter commented on PR #9867:


4 months ago
#29

Re-doing more benchmarking for what is in trunk for the change to add fetchpriority=low to script modules (and modulepreload links), when there are 6 scripts:

HTML diff:

  • script-modules-fetchpriority.

    old new  
    33173317                        type="module"
    33183318                        src="http://localhost:8000/wp-includes/js/dist/script-modules/block-library/navigation/view.min.js?ver=61572d447d60c0aa5240"
    33193319                        id="@wordpress/block-library/navigation/view-js-module"
     3320                        fetchpriority="low"
    33203321                ></script>
    33213322                <script
    33223323                        type="module"
    33233324                        src="http://localhost:8000/wp-includes/js/dist/script-modules/block-library/image/view.min.js?ver=e38a2f910342023b9d19"
    33243325                        id="@wordpress/block-library/image/view-js-module"
     3326                        fetchpriority="low"
    33253327                ></script>
    33263328                <script
    33273329                        type="module"
    33283330                        src="http://localhost:8000/wp-includes/js/dist/script-modules/block-library/search/view.min.js?ver=208bf143e4074549fa89"
    33293331                        id="@wordpress/block-library/search/view-js-module"
     3332                        fetchpriority="low"
    33303333                ></script>
    33313334                <script
    33323335                        type="module"
    33333336                        src="http://localhost:8000/wp-includes/js/dist/script-modules/block-library/file/view.min.js?ver=fdc2f6842e015af83140"
    33343337                        id="@wordpress/block-library/file/view-js-module"
     3338                        fetchpriority="low"
    33353339                ></script>
    33363340                <script
    33373341                        type="module"
    33383342                        src="http://localhost:8000/wp-includes/js/dist/script-modules/block-library/query/view.min.js?ver=f55e93a1ad4806e91785"
    33393343                        id="@wordpress/block-library/query/view-js-module"
     3344                        fetchpriority="low"
    33403345                ></script>
    33413346                <link
    33423347                        rel="modulepreload"
    33433348                        href="http://localhost:8000/wp-includes/js/dist/script-modules/interactivity/index.min.js?ver=55aebb6e0a16726baffb"
    33443349                        id="@wordpress/interactivity-js-modulepreload"
     3350                        fetchpriority="low"
    33453351                />
    33463352                <style class="wp-fonts-local">
    33473353                        @font-face {

<details><summary>Benchmark Script</summary>

number=100
before_url='http://localhost:8000/2025/06/25/lightbox/?active_plugins=increase-styles-inline-size-limit&script_debug=0&script_modules_in_footer=false&module_fetchpriority=auto'
after_url='http://localhost:8000/2025/06/25/lightbox/?active_plugins=increase-styles-inline-size-limit&script_debug=0&script_modules_in_footer=false&module_fetchpriority=low'
slug="script-modules-fetchpriority"
before_html_file="$slug.before.html"
after_html_file="$slug.after.html"
curl "$before_url" > "$before_html_file"
prettier --write "$before_html_file"
curl "$after_url" > "$after_html_file"
prettier --write "$after_html_file"
diff -u "$before_html_file" "$after_html_file" > "$slug.diff"
cat "$slug.diff" | colordiff
npm run research -- benchmark-web-vitals --url="$before_url" --url="$after_url" --network-conditions='Fast 4G'   --diff --output=md --number=$number | tee "$slug-fast-4g.md"
npm run research -- benchmark-web-vitals --url="$before_url" --url="$after_url" --network-conditions='broadband' --diff --output=md --number=$number | tee "$slug-broadband.md"

</details>

### Broadband

Metric Before After Diff (ms) Diff (%)
:--------------------------:-------: --------: -------:
FCP (median) 213.85 213.85 0.00 0.0%
LCP (median) 505.35 468.65 -36.70 -7.3%
TTFB (median) 119.25 118.1 -1.15 -1.0%
TTLB (median) 248.95 254 +5.05 +2.0%
LCP-TTFB (median) 385.1 350.65 -34.45 -8.9%

### Fast 4G

Metric Before After Diff (ms) Diff (%)
:--------------------------:-------: --------: -------:
FCP (median) 283.05 281.5 -1.55 -0.5%
LCP (median) 820.9 783.95 -36.95 -4.5%
TTFB (median) 119.3 119.75 +0.45 +0.4%
TTLB (median) 319.8 322 +2.20 +0.7%
LCP-TTFB (median) 700.8 663.1 -37.70 -5.4%

✅ The marginal LCP improvements I tested before in https://github.com/WordPress/wordpress-develop/pull/9867#issuecomment-3417837672 appear to be due to the fact that there were only three module scripts, whereas in this latest test there were double that (6).

@westonruter commented on PR #9867:


4 months ago
#30

Finally, here's another set of benchmarks for _both_ adding fetchpriority=low _and_ moving the script modules to the footer.

HTML Diff:

  • script-modules-fetchpriority-and-footer.

    old new  
    33133313                                }
    33143314                        }
    33153315                </script>
    3316                 <script
    3317                         type="module"
    3318                         src="http://localhost:8000/wp-includes/js/dist/script-modules/block-library/navigation/view.min.js?ver=61572d447d60c0aa5240"
    3319                         id="@wordpress/block-library/navigation/view-js-module"
    3320                 ></script>
    3321                 <script
    3322                         type="module"
    3323                         src="http://localhost:8000/wp-includes/js/dist/script-modules/block-library/image/view.min.js?ver=e38a2f910342023b9d19"
    3324                         id="@wordpress/block-library/image/view-js-module"
    3325                 ></script>
    3326                 <script
    3327                         type="module"
    3328                         src="http://localhost:8000/wp-includes/js/dist/script-modules/block-library/search/view.min.js?ver=208bf143e4074549fa89"
    3329                         id="@wordpress/block-library/search/view-js-module"
    3330                 ></script>
    3331                 <script
    3332                         type="module"
    3333                         src="http://localhost:8000/wp-includes/js/dist/script-modules/block-library/file/view.min.js?ver=fdc2f6842e015af83140"
    3334                         id="@wordpress/block-library/file/view-js-module"
    3335                 ></script>
    3336                 <script
    3337                         type="module"
    3338                         src="http://localhost:8000/wp-includes/js/dist/script-modules/block-library/query/view.min.js?ver=f55e93a1ad4806e91785"
    3339                         id="@wordpress/block-library/query/view-js-module"
    3340                 ></script>
    33413316                <link
    33423317                        rel="modulepreload"
    33433318                        href="http://localhost:8000/wp-includes/js/dist/script-modules/interactivity/index.min.js?ver=55aebb6e0a16726baffb"
    33443319                        id="@wordpress/interactivity-js-modulepreload"
     3320                        fetchpriority="low"
    33453321                />
    33463322                <style class="wp-fonts-local">
    33473323                        @font-face {
     
    54665442                                ]
    54675443                        }
    54685444                </script>
     5445                <script
     5446                        type="module"
     5447                        src="http://localhost:8000/wp-includes/js/dist/script-modules/block-library/navigation/view.min.js?ver=61572d447d60c0aa5240"
     5448                        id="@wordpress/block-library/navigation/view-js-module"
     5449                        fetchpriority="low"
     5450                ></script>
     5451                <script
     5452                        type="module"
     5453                        src="http://localhost:8000/wp-includes/js/dist/script-modules/block-library/image/view.min.js?ver=e38a2f910342023b9d19"
     5454                        id="@wordpress/block-library/image/view-js-module"
     5455                        fetchpriority="low"
     5456                ></script>
     5457                <script
     5458                        type="module"
     5459                        src="http://localhost:8000/wp-includes/js/dist/script-modules/block-library/search/view.min.js?ver=208bf143e4074549fa89"
     5460                        id="@wordpress/block-library/search/view-js-module"
     5461                        fetchpriority="low"
     5462                ></script>
    54695463                <script
     5464                        type="module"
     5465                        src="http://localhost:8000/wp-includes/js/dist/script-modules/block-library/file/view.min.js?ver=fdc2f6842e015af83140"
     5466                        id="@wordpress/block-library/file/view-js-module"
     5467                        fetchpriority="low"
     5468                ></script>
     5469                <script
     5470                        type="module"
     5471                        src="http://localhost:8000/wp-includes/js/dist/script-modules/block-library/query/view.min.js?ver=f55e93a1ad4806e91785"
     5472                        id="@wordpress/block-library/query/view-js-module"
     5473                        fetchpriority="low"
     5474                ></script>
     5475                <script
    54705476                        type="application/json"
    54715477                        id="wp-script-module-data-@wordpress/interactivity"
    54725478                >

<details><summary>Benchmark Script</summary>

number=100
before_url='http://localhost:8000/2025/06/25/lightbox/?active_plugins=increase-styles-inline-size-limit&script_debug=0&script_modules_in_footer=false&module_fetchpriority=auto'
after_url='http://localhost:8000/2025/06/25/lightbox/?active_plugins=increase-styles-inline-size-limit&script_debug=0&script_modules_in_footer=true&module_fetchpriority=low'
slug="script-modules-fetchpriority-and-footer"
before_html_file="$slug.before.html"
after_html_file="$slug.after.html"
curl "$before_url" > "$before_html_file"
prettier --write "$before_html_file"
curl "$after_url" > "$after_html_file"
prettier --write "$after_html_file"
diff -u "$before_html_file" "$after_html_file" > "$slug.diff"
cat "$slug.diff" | colordiff
npm run research -- benchmark-web-vitals --url="$before_url" --url="$after_url" --network-conditions='Fast 4G'   --diff --output=md --number=$number | tee "$slug-fast-4g.md"
npm run research -- benchmark-web-vitals --url="$before_url" --url="$after_url" --network-conditions='broadband' --diff --output=md --number=$number | tee "$slug-broadband.md"

</details>

### Broadband

URL Before After Diff (ms) Diff (%)
:----------------- -------:-------: --------: -------:
FCP (median) 211.6 207.85 -3.75 -1.8%
LCP (median) 502.9 462.45 -40.45 -8.0%
TTFB (median) 116.95 114.6 -2.35 -2.0%
TTLB (median) 248.2 245.05 -3.15 -1.3%
LCP-TTFB (median) 385 347.6 -37.40 -9.7%

### Fast 4G

URL Before After Diff (ms) Diff (%)
:----------------- -------:-------: --------: -------:
FCP (median) 282.9 285.4 +2.50 +0.9%
LCP (median) 823.25 707.3 -115.95 -14.1%
TTFB (median) 117.95 117.15 -0.80 -0.7%
TTLB (median) 320.25 324.05 +3.80 +1.2%
LCP-TTFB (median) 705.45 590.55 -114.90 -16.3%

✅ As expected, this shows the largest LCP improvement. It is validation that fetchpriority and in_footer are both warranted.

#31 @westonruter
4 months ago

  • Owner changed from b1ink0 to westonruter
  • Status changed from assigned to reviewing

#32 @westonruter
4 months ago

  • Keywords has-unit-tests needs-dev-note added; needs-unit-tests removed

@b1ink0 commented on PR #9867:


4 months ago
#33

@westonruter Sorry for the delay on this.

Only one issue the sort_item_dependencies method is not handling recursive script module dependencies, which causes a memory exhaustion fatal error:

Fatal error: Allowed memory size of 134217728 bytes exhausted (tried to allocate 262144 bytes) in /var/www/src/wp-includes/class-wp-script-modules.php on line 694

Fatal error: Allowed memory size of 134217728 bytes exhausted (tried to allocate 262144 bytes) in Unknown on line 0

This happens with code like:

// Circular Dependency (circular-x -> circular-y -> circular-z -> circular-x)
wp_register_script_module( 'circular-x', 'js/circular-x.js', array( 'circular-y' ), '1.0' );
wp_register_script_module( 'circular-y', 'js/circular-y.js', array( 'circular-z' ), '1.0' );
wp_register_script_module( 'circular-z', 'js/circular-z.js', array( 'circular-x' ), '1.0' );
wp_enqueue_script_module( 'circular-x' );

I checked classic scripts as well, and they also cause the same memory exhaustion error for circular dependencies but in the WP_Dependencies class:

Fatal error: Allowed memory size of 134217728 bytes exhausted (tried to allocate 8192 bytes) in /var/www/src/wp-includes/class-wp-dependencies.php on line 205

Fatal error: Allowed memory size of 134217728 bytes exhausted (tried to allocate 65536 bytes) in /var/www/src/wp-includes/option.php on line 1108

@westonruter commented on PR #9867:


4 months ago
#34

I checked classic scripts as well, and they also cause the same memory exhaustion error for circular dependencies but in the WP_Dependencies class:

@b1ink0 So then it seems we're ok here?

@b1ink0 commented on PR #9867:


4 months ago
#35

I checked classic scripts as well, and they also cause the same memory exhaustion error for circular dependencies but in the WP_Dependencies class:

@b1ink0 So then it seems we're ok here?

Yes I think so!

#36 @peterwilsoncc
4 months ago

  • Keywords commit added

#37 @westonruter
4 months ago

  • Resolution set to fixed
  • Status changed from reviewing to closed

In 60999:

Script Loader: Add support for printing script modules at wp_footer.

This brings API parity with WP_Scripts by implementing opt-in support for printing in the footer via an in_footer argument. This argument can be supplied via the $args array passed to wp_enqueue_script_module() or wp_register_script_module(), alongside the existing fetchpriority key introduced in #61734. It can also be set for previously-registered script modules via WP_Script_Modules::set_in_footer(). This is not applicable to classic themes since modules are enqueued while blocks are rendered after wp_head has completed, so all script modules are printed in the footer anyway; the importmap script must be printed after all script modules have been enqueued.

Script modules used for interactive blocks (with the Interactivity API) are automatically printed in the footer. Such script modules should be deprioritized because they are not in the critical rendering path due to interactive blocks using server-side rendering. Script modules remain printed at wp_head by default, although this default should be revisited since they have deferred execution (and they are printed in the footer for classic themes already, as previously noted). Moving a script module to the footer ensures that its loading does not contend with the loading of critical resources, such as the LCP element's image resource, and LCP is improved as a result.

This also improves specificity of some PHP types, it ensures that script modules can't be registered with an empty ID, and it prevents printing script modules with empty src URLs.

Developed in https://github.com/WordPress/wordpress-develop/pull/9867

Follow-up to [60704].

Props b1ink0, westonruter, jonsurrell, peterwilsoncc, vipulpatil, mindctrl.
See #61734.
Fixes #63486.

This ticket was mentioned in Slack in #core by westonruter. View the logs.


4 months ago

#39 @westonruter
4 months ago

In 61041:

Script Loader: Remove hard-coded versions in test snapshots.

Follow-up to [60999].

Props ellatrix.
See #63486.

#40 @westonruter
4 months ago

In 61073:

Script Loader: Restore original return value for WP_Script_Modules::get_dependencies().

Even though this method is private, there are some usages of it in the ecosystem via the Reflection API. So this reverts the script module IDs string[] return value in favor of the original array<string, array> return value. This re-emphasizes the need for more public accessor methods being tracked in #60597.

Developed in https://github.com/WordPress/wordpress-develop/pull/10403

Follow-up to [60999].

Props pbiron, westonruter, johnbillion.
See #63486, #60597.

#42 @westonruter
3 months ago

In 61323:

Script Loader: Emit notices when enqueueing a script, style, or script module with missing dependencies.

Developed in https://github.com/WordPress/wordpress-develop/pull/10545

Follow-up to [60999].

Props deepakprajapati, westonruter.
See #63486.
Fixes #64229.

#43 @jorbin
3 weeks ago

In 61550:

Script Loader: Emit notices when enqueueing a script, style, or script module with missing dependencies.

First Developed in https://github.com/WordPress/wordpress-develop/pull/10545. Backport developed in https://github.com/WordPress/wordpress-develop/pull/10789.

Follow-up to [60999].

Reviewed by jorbin, wildworks.
Merges [61323], [61357], and [61542].

Props deepakprajapati, westonruter, mukeshpanchal27, jorbin, wildworks.
See #63486.
Fixes #64229.

Note: See TracTickets for help on using tickets.