Problem/Motivation

Olivero ships a preload template with the following:

<link rel="preload" href="{{ olivero_path }}/fonts/metropolis/Metropolis-Regular.woff2" as="font" type="font/woff2" crossorigin>
<link rel="preload" href="{{ olivero_path }}/fonts/metropolis/Metropolis-SemiBold.woff2" as="font" type="font/woff2" crossorigin>
<link rel="preload" href="{{ olivero_path }}/fonts/metropolis/Metropolis-Bold.woff2" as="font" type="font/woff2" crossorigin>
<link rel="preload" href="{{ olivero_path }}/fonts/lora/lora-v14-latin-regular.woff2" as="font" type="font/woff2" crossorigin>

This is to support fonts.css which is loaded as part of the global-styling library.

Steps to reproduce

With Umami, check the network tab in devtools, and see that the fonts are loaded at the same time or before the CSS, instead of only after the CSS is loaded.

Screenshot of chromium devtools showing Umami fonts loading before CSS

Proposed resolution

Add a 'fonts' key to libraries.yml and then process this in HtmlResponseAttachmentsProcessor, with initial support for preload.

Convert Olivero to use it.

e.g.

global-styling:
  version: VERSION
  fonts:
    fonts/metropolis/Metropolis-Regular.woff2:
      preload: true

This will add an html link tag with rel=preload pointing to the font.

In #3570909: Support early hints for fonts we can add support for early hints .

Remaining tasks

Once this is in, we can use the new API to add support for early hints too #3563782: [PP-1] Use early hints for CSS

User interface changes

API changes

Data model changes

Release notes snippet

Issue fork drupal-3366561

Command icon Show commands

Start within a Git clone of the project using the version control instructions.

Or, if you do not have SSH keys set up on git.drupalcode.org:

Comments

catch created an issue. See original summary.

catch’s picture

Issue summary: View changes
vbouchet’s picture

I understand that for now, given the example, it is to add the possibility to specify additional "things" with the library that would generate the link tags. What about the existing external / js that are added in a library?
Should we do something like:

my_library:
  js:
   https://cdn.example.com/js/file.js: { type: external, preconnect: true, dns-prefech: true }

or

my_library:
  js:
   https://cdn.example.com/js/file.js: { type: external }
  links:
    -
      rel: preconnect
      href: //cdn.example.com
    -
      rel: dsn-prefetch
      href: //cdn.example.com
      

The second option allows a lot of flexibility (and cover the example of fonts) but is much more verbose.
Third option is a mix, allowing for shorter version for elements of the library (like example 1) and verbose version for other elements.

I am curious of a direction as we are for now implementing custom on a project but would be keen to write a patch for this to land in core the proper way.

catch’s picture

I kind of like the more verbose option even for the same file, because you could have two files from the same CDN and then defining prefetch inline for one file (or both) doesn't seem ideal. We could always add a shorthand in a follow-up.

rikki_iki’s picture

@catch thanks for the redirect to this issue. As I posted on the other issue, in my mind I think a definition like this would be nice;

font:
  css:
    base:
      https://fonts.example.com/font.css: { type: external }
  preconnect:
    https://fonts.example.com: {}
  preload:
    https://fonts.example.com/font-regular.woff2: { as: font, attributes: { type: font/woff2} }
    https://fonts.example.com/font-bold.woff2: { as: font, attributes: { type: font/woff2 } }

dns-prefetch would also have it's own entry. I think having separate entries instead of one generic 'links' entry reduces the risk of user error (and general misuse - eg putting css and js into the links entry). It's also clearer to read.

catch’s picture

That looks pretty nice to me as an API. We'd have to handle any additions/removals manually but maybe that is fine.

macsim’s picture

Ahem, since the other issue was opened in 2022 and this one in 2023, the duplicate is this one, not the former...
We should have close that issue and improve the original.

rikki_iki’s picture

The other issue has been postponed for a year, and no one seemed to care... I don't mind which issue so long as it stays active.

DNS prefetch is more widely supported and recommended as a fallback for Preconnect (see support), so maybe we can build that in? Or instead of two separate entries it's something like;

font:
  prefetch:
    https://fonts.example.com: { preconnect: true }
    https://other.example.com: { }
  preload:
     etc..

To optionally add a preconnect hint for certain dns-prefetch items, since preconnect usage should be limited to only critical assets;

<link rel="preconnect" href="http://fonts.example.com">
<link rel="dns-prefetch" href="http://fonts.example.com">
<link rel="dns-prefetch" href="http://other.example.com">

Regarding preload, there's also modulepreload to consider. I don't know if we want to have yet another entry just for this separate rel value or if we could so something like;

main:
  js:
    js/main.module.js: { attributes: { type: module } }
    js/main.js: { }
  preload:
    js/main.module.js: { as: script, attributes: { type: module } }
    js/main.js: { as: script }

And if the preload has type:module we can swap the rel value over? Or just as:module?

<link rel="preload" href="main.js" as="script" />
<link rel="modulepreload" href="main.module.js" />

And then how should we handle the crossorigin attribute? Currently it can be added via attributes which I guess is fine.

macsim’s picture

I don't really mind as well it's just about people who spent hours thinking about the problem / trying to fix it / reviewing code / rerolling it and who will never get credit for that.

As long as the subject moves forward it's good, so now that it's been done, let's continue here.

catch’s picture

@macsim we can transfer issue credits over, but I'm not sure which issue you're talking about to be honest, there are at least three somewhat related to this one with different approaches. If you link it, I'll look at transferring relevant credits over.

rajab natshah’s picture

It would be so nice if we could manage preloading from libraries directly.

(Interaction Observer IO API and Preloading API) in Drupal Core

catch’s picture

We can probably add early hint support on top of this too #3563782: [PP-1] Use early hints for CSS.

catch’s picture

Alternative approach for fonts at #3565331: Allow themes to inline font declarations.

catch’s picture

Category: Feature request » Task

Thought about this a bit more.

I agree with @rikki_iki's comment in #5:

I think having separate entries instead of one generic 'links' entry reduces the risk of user error (and general misuse

It's possible to add all of these things with #attached['html_head_link'] and #attached['http_header'] in hook_preprocess_page() so there's already a way for themes to do this in PHP. What we're looking for here is a more convenient/structured way to do font preloading since it's the common use-case.

Given that, for fonts I think we should do something like this:

font:
  https://fonts.example.com/font-regular.woff2:
    preload: TRUE
    early_hint: TRUE
  https://fonts.example.com/font-bold.woff2: {}

As a top level item alongside CSS. We'd then need to add support for this into the asset resolver and collect these in HtmlResponseAttachmentsProcessor

If a font is listed without anything extra set, then nothing happens, but it allows the theme to be explicit about that.

For module preloading maybe we can do something like this - put the preload hint directly on the definition of the file:

js:
    js/main.module.js: { attributes: { type: module } , preload: true }

To make progress here, I'd prefer to open separate issues to look at generic preconnect support for third party domains and also js module preloading, these will need different treatment and a different API.

berdir’s picture

Not caught up, just one note on fonts.

We added something like this (just preload, not early hints) to https://drupal.org/project/bs_base, by adding preload fonts to theme.info.yml. One tedious bit was cache busting, we for example have an icon font that updates pretty frequently and because this isn't the canonical font definition (that's in CSS), it's very easy for the actual font-face in CSS and this preload info to get out of sync, resulting in loading two different files. That doesn't just defeat the whole purpose, it makes it worse than not having it at all.

I'm not sure what a good solution for this is to be honest, in our case we then added support for updating the .info.yml definition into the icon font build toolchain. I think if core respects the library version and adds that to the URL, similar to CSS/JS assets then that's probably OK and we could update our toolchain to maintain the library version for us?

catch’s picture

@berdir I don't think we can do that because if we add the version string in the preload link, that URL will be different to the one in the CSS file unless they're manually kept in sync, so would lead to two http requests.

Cache busting isn't made worse by this though.

berdir’s picture

Yes, that's my point. The two URL's must be the same, the CSS URL would need to use the same version string. I guess if the following definition works then that's also OK?

  https://fonts.example.com/font-regular.woff2?version=5:
    preload: TRUE
    early_hint: TRUE

I'm just saying that this is something that you need to keep in mind, it must be the exact same URL, and if you have it in two different places, it's easy to forget to update them if it's not automated.

catch’s picture

Yes manually adding the version string in a query parameter in both places would work fine.

Version: 11.x-dev » main

Drupal core is now using the main branch as the primary development branch. New developments and disruptive changes should now be targeted to the main branch.

Read more in the announcement.

catch’s picture

Title: Support prefetch/preload/dns-prefetch in the libraries API » Support preloading of fonts (and early hints?) in the libraries API
Issue summary: View changes
Status: Active » Needs review
Issue tags: +front end performance, +Performance

Here's a start. Replaces Olivero's preload template as a proof of concept.

Tried to require the absolute minimum amount of information in libraries.yml so we're not rebuilding the html_head_link API in YAML.

We can add 103 early hints support on top of this without a lot of work. Probably simpler to do that in a follow-up issue to this one.

@todos here:
1. We can add support to Umami and Navigation
2. Explicit test coverage

But would be nice to get a review of the API/implementation in the meantime.

finnsky made their first commit to this issue’s fork.

finnsky’s picture

1. Fixed phpcs
2. Added support for Navigation

---

We need to fix path resolving
http://localhost:8888/node/add/core/modules/navigation/assets/fonts/inte...

on inner pages it is wrong.

catch’s picture

Pushed a fix to use file_url_generator. This required updating some unit tests but pipeline appears to have got past those.

Also implemented the API in Umami for the fonts that are loaded on a front page request. Because Umami doesn't do any preloading at all at the moment this should be visible on https://gander.tag1.io/ (after commit) and possible to before/after in lighthouse locally.

catch’s picture

Lighthouse before and after just to show it working in Umami.

catch’s picture

There's existing NightWatch test coverage for Olivero font preloading.

Added unit test coverage for AssetResolver.

Also added an additional assertion to the performance test in Navigation module.

catch’s picture

Title: Support preloading of fonts (and early hints?) in the libraries API » Support preloading of fonts in the libraries API

Opened #3570909: Support early hints for fonts for early hints. Should be clean to add on top of this one, but better to keep this reviewable.

smustgrave’s picture

Status: Needs review » Needs work

This one needs a rebase, but based on lauriii comment should a CR be added? Or IS updated?

catch’s picture

Issue summary: View changes

Expanded the proposed resolution in the issue summary.

Yes we should add a change record here but I'd prefer to do that once the API has had more review.

catch’s picture

Status: Needs work » Needs review
needs-review-queue-bot’s picture

Status: Needs review » Needs work
StatusFileSize
new98 bytes

The Needs Review Queue Bot tested this issue. The merge request has merge conflicts and cannot be merged. Therefore, this issue status is now "Needs work".

This does not mean that the patch necessarily needs to be re-rolled or the MR rebased. Read the Issue Summary, the issue tags and the latest discussion here to determine what needs to be done.

Consult the Drupal Contributor Guide to find step-by-step guides for working with issues.

catch’s picture

Issue summary: View changes
Status: Needs work » Needs review
Issue tags: +Needs change record

Rebased.

needs-review-queue-bot’s picture

Status: Needs review » Needs work
StatusFileSize
new91 bytes

The Needs Review Queue Bot tested this issue. It no longer applies to Drupal core. Therefore, this issue status is now "Needs work".

This does not mean that the patch necessarily needs to be re-rolled or the MR rebased. Read the Issue Summary, the issue tags and the latest discussion here to determine what needs to be done.

Consult the Drupal Contributor Guide to find step-by-step guides for working with issues.

catch’s picture

Status: Needs work » Needs review

Rebased.

grimreaper’s picture

Status: Needs review » Needs work

Hello everyone,

Thanks all for the discussions and work on this issue!

This looks promising!

I put some review comments on the MR.

catch’s picture

Status: Needs work » Needs review

Fixed or replied to all the review points I think (including one test change I initially didn't want to do but have now done and it's better).

catch’s picture

Latest changes broke performance tests, but in a good way - because we filter libraries by asset type,and the libraries with fonts are more stable across pages than the libraries with CSS (because there's less of them), the cache hit rate on the asset resolver cache entry is a lot higher.

grimreaper’s picture

Status: Needs review » Needs work

Thanks @catch for pushing this.

I change to needs work as last pipeline failed.

Otherwise RTBC for me.

catch’s picture

Status: Needs work » Needs review
Issue tags: -Performance, -Needs change record +Page loading performance

Updated that test, missed it in the previous round of changes. Also added a change record now there's a bit more consensus on the API.

#3570909: Support early hints for fonts should be doable as soon as this lands.

grimreaper’s picture

Status: Needs review » Reviewed & tested by the community

Thanks.

I gave a last quick review.

catch’s picture

Issue summary: View changes
StatusFileSize
new141.63 KB

Uploading a screenshot of chromium devtools network tab showing the fonts loading just before the CSS in Umami, whereas in HEAD they would load only after the CSS is loaded and parsed. Olivero there will be no change because it already implements preloading, it's just able to switch to the new API.

I'll open an issue for default_admin to use the new API too.

needs-review-queue-bot’s picture

Status: Reviewed & tested by the community » Needs work
StatusFileSize
new91 bytes

The Needs Review Queue Bot tested this issue. It no longer applies to Drupal core. Therefore, this issue status is now "Needs work".

This does not mean that the patch necessarily needs to be re-rolled or the MR rebased. Read the Issue Summary, the issue tags and the latest discussion here to determine what needs to be done.

Consult the Drupal Contributor Guide to find step-by-step guides for working with issues.

catch’s picture

Status: Needs work » Reviewed & tested by the community

Rebased - just performance tests to update.

grimreaper’s picture

Assigned: Unassigned » grimreaper
Status: Reviewed & tested by the community » Needs review

I thought about something.

I need to check about the library declaration in SDC, if there is an impact and if possible to have a component declared library to provide fonts too.

I will try to do that today.

grimreaper’s picture

Issue tags: +DevDaysAthens2026
grimreaper’s picture

Assigned: grimreaper » Unassigned

Like in comment #3565258-67: Support library-specific aggregates and next comments, should we handle fonts from component libraries in a follow-up issue?

catch’s picture

Maybe one follow-up issue to respect additional library definition keys in components.yml? It's possible that it also works by accident but even if it does would be good to add test coverage and documentation.

grimreaper’s picture

Status: Needs review » Reviewed & tested by the community

  • alexpott committed ac409b9d on 11.x
    feat: #3366561 Support preloading of fonts in the libraries API
    
    By:...

  • alexpott committed 1c65c7ad on main
    feat: #3366561 Support preloading of fonts in the libraries API
    
    By:...
alexpott’s picture

Version: main » 11.x-dev
Status: Reviewed & tested by the community » Fixed

Committed 1c65c7a and pushed to main. Thanks!
Committed ac409b9 and pushed to 11.x. Thanks!

Now that this issue is closed, review the contribution record.

As a contributor, attribute any organization that helped you, or if you volunteered your own time.

Maintainers, credit people who helped resolve this issue.

  • alexpott committed 497e962f on 11.x
    feat: #3366561 follow-up Support preloading of fonts in the libraries...

  • alexpott committed 0ef23f47 on 11.x
    feat: #3366561 follow-up Support preloading of fonts in the libraries...

  • alexpott committed dbe5bca8 on 11.x
    Revert: "feat: #3366561 Support preloading of fonts in the libraries API...
alexpott’s picture

Status: Fixed » Patch (to be ported)

Bah - this was wrong to try to resolve merge conflicts on the fly... ho hum. reverted from 11.x... will open an MR against 11.x

alexpott’s picture

Status: Patch (to be ported) » Fixed

Committed 26b9607 and pushed to 11.x. Thanks!

Now that this issue is closed, review the contribution record.

As a contributor, attribute any organization that helped you, or if you volunteered your own time.

Maintainers, credit people who helped resolve this issue.

  • alexpott committed 26b96079 on 11.x
    feat: #3366561 Support preloading of fonts in the libraries API
    
    By:...

grimreaper’s picture

Follow up issue #3586470: Support preloading of fonts declared from components to add this feature in SDC ready for review.

Status: Fixed » Closed (fixed)

Automatically closed - issue fixed for 2 weeks with no activity.