# Defer javascript and show interim content on initial page load

**URL:** https://meta.discourse.org/t/defer-javascript-and-show-interim-content-on-initial-page-load/216458
**Category:** Development
**Created:** [January 30, 2022, 2:02am UTC](https://meta.discourse.org/t/defer-javascript-and-show-interim-content-on-initial-page-load/216458 "2022-01-30T02:02:16Z")
**Posts on this page:** 20
**Page:** 1

<div class="post-metadata">

### Author: ![rrit](https://avatars.discourse-cdn.com/v4/letter/r/b5ac83/32.png) [@rrit](https://meta.discourse.org/u/rrit)
#### Post date: [January 30, 2022, 2:02am UTC](https://meta.discourse.org/t/defer-javascript-and-show-interim-content-on-initial-page-load/216458/1 "2022-01-30T02:02:16Z")

</div>

## How about: Deferring Discourse’ javascripts

Add defer-attributes to all javascripts if possible. Deferring javascript-loading and execution makes the browser start HTML-parsing, rendering and painting.

So some static interim content may be shown pretty early in (or even before) the discourse booting process. This should do for a faster user-perceived page loading speed on first page load.

 ![FCP static content](https://global.discourse-cdn.com/meta/original/3X/a/a/aabc80111306d900788962849f54786d234bb019.png)

Ideas for static interim content:

- splash screen with logo and loading spinner
- topic view with posts from backend

* * *

## POC and PR

For latest proof of concept and PR - [please look into this post](https://meta.discourse.org/t/defer-javascript-and-show-interim-content-on-initial-page-load/216458/14).

* * *

The vendor-javascript and all preceding javascripts are not deferred right now.  
@see: [https://github.com/rr-it/discourse/commit/328efd5c055f5f2a4d93b5e52268cfe92913faf7](https://github.com/rr-it/discourse/commit/328efd5c055f5f2a4d93b5e52268cfe92913faf7)

Ideas on how to solve this are very welcome.

* * *

## JavaScript async vs. defer vs. none

More about javascript loading options - including `defer`: [Redirecting to: /lesson/more-html/](https://flaviocopes.com/javascript-async-defer/)  
(This is not about speeding up the real discourse boot.)

* * *

## Fastboot/rehydration

> **[State-of-the-art LCP performance for Ember.JS](https://discuss.emberjs.com/t/state-of-the-art-lcp-performance-for-ember-js/18792)**
>
> Background Google have recently announced it will be rolling out an update to search ranking based on “Core Web Vitals” starting May 2021. It will use 3 signals for this new ranking bump: LCP: Largest Contentful Paint This metric is used to...

I read this article:  
The conclusion over there appears to be an implementation of Fastboot/rehydration.  
Is there a timeline for this?

---

<div class="post-metadata">

### Author: ![Falco](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/falco/32/179432_2.png) [@Falco](https://meta.discourse.org/u/Falco)
#### Post date: [January 30, 2022, 2:13am UTC](https://meta.discourse.org/t/defer-javascript-and-show-interim-content-on-initial-page-load/216458/2 "2022-01-30T02:13:11Z")

</div>

> [@rrit](#):
>
> So some static interim content may be shown pretty early in (or even before) the discourse booting process.

That would cause the LCP to be still after the EmberJS boots and re-renders, not addressing the main problem regarding new Google rankings.

> [@rrit](#):
>
> The conclusion over there appears to be an implementation of Fastboot/rehydration.

That’s our current midterm plan to address LCP in Discourse.

---

<div class="post-metadata">

### Author: ![rrit](https://avatars.discourse-cdn.com/v4/letter/r/b5ac83/32.png) [@rrit](https://meta.discourse.org/u/rrit)
#### Post date: [February 3, 2022, 11:06pm UTC](https://meta.discourse.org/t/defer-javascript-and-show-interim-content-on-initial-page-load/216458/3 "2022-02-03T23:06:14Z")

</div>

> [@Falco](#):
>
> That would cause the LCP to be still after the EmberJS boots and re-renders

As of Chrome 88 this is luckily not true anymore! 🚀  
Didn’t know about this either until now. :))

> **[Chrome Speed - Largest Contentful Paint Definition Change in M88](https://chromium.googlesource.com/chromium/src/+/refs/heads/main/docs/speed/metrics_changelog/2020_11_lcp_2.md)**

"Before this change, an element being removed caused it to no longer be considered a valid LCP candidate. […] After this change, an element being removed is still considered a valid LCP candidate. "

“The change to include content which is later removed from the DOM as possible largest contentful paints will improve Largest Contentful Paint times on sites which have ~~images~~ [_for Discourse:_ **text elements**] **of the same size** inserted multiple times. This is a common pattern for carousels, as well as some JavaScript frameworks which do **server-side rendering**.”

* * *

### LCP Changelog

There might be more good changes in the future:

> **[Chrome Speed - Largest Contentful Paint Changelog](https://chromium.googlesource.com/chromium/src/+/master/docs/speed/metrics_changelog/lcp.md)**

---

<div class="post-metadata">

### Author: ![rrit](https://avatars.discourse-cdn.com/v4/letter/r/b5ac83/32.png) [@rrit](https://meta.discourse.org/u/rrit)
#### Post date: [February 4, 2022, 12:37am UTC](https://meta.discourse.org/t/defer-javascript-and-show-interim-content-on-initial-page-load/216458/4 "2022-02-04T00:37:40Z")

</div>

Here are some _simulated_ statistics for a topic page with the POC implemented.

Lighthouse: _“Values are estimated and may vary.”_

### WebPageTest

[webpagetest.org](http://webpagetest.org)

#### Moto4G simulation

 ![grafik](https://global.discourse-cdn.com/meta/original/3X/a/4/a4578ecbb0deeb423fbb03e2bb567adcd5fc53b3.png)  
Note: we are the black arrow on top.

 ![grafik](https://global.discourse-cdn.com/meta/original/3X/7/1/710f390da76fe1ce8013050d30a2b944aa86ecfd.png)  
Notes:

- 0s-2s - blank screen:  
WebPageTest ignores `defer` for JavaScript and downloads all the JavaScripts before doing a first paint - this works correctly on a real device.
- 2.5s - LCP: static content from server-rendering
- 3.5s - Visual Change: logo loaded
- 6.5s - Visual Change: content from EmberJs rendering
- 7s - Visual complete

### PageSpeed Insights

> **[PageSpeed Insights](https://pagespeed.web.dev)**

#### Largest Contentful Paint element

PageSpeed correctly identifies the static text-node from server-rendering as ~~FCP~~ LCP element:  
`div.row > div.topic-body > div.post > p`

EmberJs rendered text-node:  
`div.row > div.topic-body > div.regular.contents > div.cooked > p`

 ![grafik](https://global.discourse-cdn.com/meta/original/3X/3/3/3315ea726d2dc376755c2ececa05de4acefec726.png)

But it looks like PageSpeed does not use the correctly identified static text-node for its _simulated_ result: _simulated_ FCP and LCP are to huge.

 ![grafik](https://global.discourse-cdn.com/meta/original/3X/1/1/112288aaf2a09d9572c56cb5febcdc8d7b50739d.png)

#### Real-world user-data

Let’s wait another 14-28 days to get “real” data from [Chrome UX Report](https://developers.google.com/web/tools/chrome-user-experience-report) with the POC implemented.

Statistics without POC implemented for the tested topic page:  
(The data is of this single topic url – and not of the entire origin.)

 ![grafik](https://global.discourse-cdn.com/meta/original/3X/0/f/0f3da5c281798a22c7eeb5bd0497a3d081bce4b6.png)

---

<div class="post-metadata">

### Author: ![Falco](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/falco/32/179432_2.png) [@Falco](https://meta.discourse.org/u/Falco)
#### Post date: [February 4, 2022, 1:49am UTC](https://meta.discourse.org/t/defer-javascript-and-show-interim-content-on-initial-page-load/216458/5 "2022-02-04T01:49:28Z")

</div>

Oh that’s a very interesting find! Great work!

> [@rrit](#):
>
> But it looks like PageSpeed does not use the correctly identified static text-node for its _simulated_ result: _simulated_ FCP and LCP are to huge.

What does the you get on this extension [https://chrome.google.com/webstore/detail/web-vitals/ahfhijdlegdabablpippeagghigmibma](https://chrome.google.com/webstore/detail/web-vitals/ahfhijdlegdabablpippeagghigmibma)?

---

<div class="post-metadata">

### Author: ![rrit](https://avatars.discourse-cdn.com/v4/letter/r/b5ac83/32.png) [@rrit](https://meta.discourse.org/u/rrit)
#### Post date: [February 4, 2022, 10:40am UTC](https://meta.discourse.org/t/defer-javascript-and-show-interim-content-on-initial-page-load/216458/6 "2022-02-04T10:40:23Z")

</div>

Via Web Vitals Chrome Extension

- on desktop
- Chromium Version 90.0.4430.212
- first load on new incognito window

 ![grafik](https://global.discourse-cdn.com/meta/original/3X/5/2/5266c2da2e05bd56d133cd95d288de86acd22ba0.png)  
Note on First Input Delay: I waited until fully loaded page and then clicked on background - so after EmberJs rendering finished.

 ![grafik](https://global.discourse-cdn.com/meta/original/3X/6/c/6c941a2d49f43b3d4651f6fe35cca53787934483.png)  
Note on First Input Delay: Here I clicked on background immediately when the static content was first visible. Add my reaction time 🦥 on top of this FID.

Extra note on _percentiles_ below the bars in these graphs:  
The percentiles are not that relevant as they only compare the meassured values with the origin values. The origin is a TYPO3 webpage with a subfolder install of Discourse.

---

<div class="post-metadata">

### Author: ![Faizan\_Zahid](https://avatars.discourse-cdn.com/v4/letter/f/b9bd4f/32.png) [@Faizan\_Zahid](https://meta.discourse.org/u/Faizan_Zahid)
#### Post date: [February 7, 2022, 7:30pm UTC](https://meta.discourse.org/t/defer-javascript-and-show-interim-content-on-initial-page-load/216458/10 "2022-02-07T19:30:31Z")

</div>

Awesome idea! @rrit

I totally agree Discourse is very slow JS Heavy web-app, if we can defer CSS/JS FILES, it will hugely help speed up LCP, FCP, FID,CLS.

Would really help to see this getting live, we and many other people are facing this issue. All discourse sites are failing at Core Web Vitals. If we server a fast STATIC HTML page to users 1st time and or defer all JS/CSS logic in 1st initial load, and this way we can speed up all pages and pass CWV scores! excited to see this live on core discourse update.

All discourse sites Google rankings are declining due to sites not passing Core Web Vitals.

---

<div class="post-metadata">

### Author: ![david](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/david/32/157490_2.png) [@david](https://meta.discourse.org/u/david)
#### Post date: [February 7, 2022, 7:37pm UTC](https://meta.discourse.org/t/defer-javascript-and-show-interim-content-on-initial-page-load/216458/11 "2022-02-07T19:37:15Z")

</div>

We’re open to experimenting with this in core. The ‘flash’ of differently-styled content can be a little disconcerting, so we’d like to start with it behind a default-disabled “experimental” site setting to start with. That way, Site admins can choose to enable it if they want to.

Are you able to try adding a site setting in your PR @rrit? It would also be good to add some RSpec tests to verify the behaviour with the setting enabled / disabled.

---

<div class="post-metadata">

### Author: ![Falco](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/falco/32/179432_2.png) [@Falco](https://meta.discourse.org/u/Falco)
#### Post date: [February 7, 2022, 8:03pm UTC](https://meta.discourse.org/t/defer-javascript-and-show-interim-content-on-initial-page-load/216458/12 "2022-02-07T20:03:41Z")

</div>

> [@rrit](#):
>
> After this change, an element being removed is still considered a valid LCP candidate. "

Doesn’t that means that we can simply put a fullscreen spinner (that has 100% width and 100% height) on the page rendered by the server, and replace it by the Ember app when it finally boots to get an extremely low LCP?

> [@david](#):
>
> We’re open to experimenting with this in core. The ‘flash’ of differently-styled content can be a little disconcerting, so we’d like to start with it behind a default-disabled “experimental” site setting to start with. That way, Site admins can choose to enable it if they want to.

We could make this spinner a SVG that mimics the Discourse UI so the transition is smoother and less FOUC-like.

---

<div class="post-metadata">

### Author: ![david](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/david/32/157490_2.png) [@david](https://meta.discourse.org/u/david)
#### Post date: [February 7, 2022, 8:04pm UTC](https://meta.discourse.org/t/defer-javascript-and-show-interim-content-on-initial-page-load/216458/13 "2022-02-07T20:04:51Z")

</div>

> [@Falco](#):
>
> > [@rrit](#):
> >
> > After this change, an element being removed is still considered a valid LCP candidate. "
> 
> Doesn’t that means that we can simply put a fullscreen spinner

I think the key part is LCP “ **candidate** ”

It will only be considered the largest contentful paint if it really is the largest (or at least, the same size) as what is eventually rendered?

So using the crawler view works quite well because the content (i.e. the text) is largely the same?

(I am mostly guessing here, based on the screenshots above - I haven’t tried a fullscreen spinner)

---

<div class="post-metadata">

### Author: ![rrit](https://avatars.discourse-cdn.com/v4/letter/r/b5ac83/32.png) [@rrit](https://meta.discourse.org/u/rrit)
#### Post date: [February 7, 2022, 8:33pm UTC](https://meta.discourse.org/t/defer-javascript-and-show-interim-content-on-initial-page-load/216458/14 "2022-02-07T20:33:55Z")

</div>

> [@david](#):
>
> Are you able to try adding a site setting in your PR @rrit?

Feature flag is implemented.

> [@david](#):
>
> RSpec tests to verify the behaviour with the setting enabled / disabled.

I’m not at all a ruby developer - on this I definitely need some help.

~~Maybe push my POC into a new branch in the discourse/discourse repo, before doing a PR on `main`?~~

This is my PR on this feature:  
[https://github.com/discourse/discourse/pull/15858](https://github.com/discourse/discourse/pull/15858)

@david Can you lend me your head for some help on developing Rspec tests for these changes:

[app/helpers/application\_helper.rb](https://github.com/discourse/discourse/pull/15858/files#diff-5f3fc5e3977d242572aa1d08551f5eb557de0ccaff30370838ee9df5386ea0da): [spec/helpers/application\_helper\_spec.rb](https://github.com/discourse/discourse/pull/15858/commits/da1ad9bcda6fe013eb4edde0b88cf86923c76436?diff=split&w=1#diff-caa3def9981603cfcc31966f2bbe59aba2777727934cb2f06a375510582584c1)

I don’t see feasible unit tests here. It looks testable by integration tests only.  
 ~~[app/models/theme.rb](https://github.com/discourse/discourse/pull/15858/files#diff-415da1b21008116467704855fa96412f74bec76fed4013f1f53653523adffa54)~~  
 ~~[app/models/theme\_field.rb](https://github.com/discourse/discourse/pull/15858/files#diff-4d55893c87d5416eb58dfddfdc147764c722c431356f3003956f317d20497560)~~

I had to disable defer tag for QUnit Test Runner: [app/views/qunit/index.html.erb](https://github.com/discourse/discourse/pull/15858/commits/727426aadaea3c6de4f09306db81d0ca38b38f90#diff-fbee51a96326b48b47f2bcfeffb2ae5dcc21c65cd4761f47fec5a3263be17bf2)  
Before QUnit Tests did still run with the feature flag `"javascript defer" = false`. And now the tests run also with `"javascript defer" = true`.

---

<div class="post-metadata">

### Author: ![rrit](https://avatars.discourse-cdn.com/v4/letter/r/b5ac83/32.png) [@rrit](https://meta.discourse.org/u/rrit)
#### Post date: [February 7, 2022, 8:54pm UTC](https://meta.discourse.org/t/defer-javascript-and-show-interim-content-on-initial-page-load/216458/15 "2022-02-07T20:54:04Z")

</div>

> [@Falco](#):
>
> Doesn’t that means that we can simply put a fullscreen spinner (that has 100% width and 100% height) […] to get an extremely low LCP?

This is probably already blocked by [https://chromium.googlesource.com/chromium/src/+/master/docs/speed/metrics\_changelog/2020\_11\_lcp.md:](https://chromium.googlesource.com/chromium/src/+/master/docs/speed/metrics_changelog/2020_11_lcp.md:)

> Full viewport images, which are visually equivalent to background images, are no longer considered as the largest contentful paint

* * *

> [@david](#):
>
> I think the key part is LCP “ **candidate** ” It will only be considered the largest contentful paint if it really is the largest

Good point: see [Largest Contentful Paint (LCP) &nbsp;|&nbsp; Articles &nbsp;|&nbsp; web.dev](https://web.dev/lcp/#how-is-an-element's-size-determined)

> For text elements, only the size of their text nodes is considered (the smallest rectangle that encompasses all text nodes).
> 
> For all elements, any margin, padding, or border applied via CSS is not considered.

- That’s why the static text-node must be rendered exactly the same size as the EmberJs text-node.
- Or even slightly bigger by increasing the `line-height`.  
E.g. if the width of the text-nodes doesn’t match, there are a lot of geometric cases introduced by different linebreaks where the static text-node becomes smaller than the EmberJs-one.

See: [LCP examples](https://web.dev/lcp/#examples)

* * *

> [@david](#):
>
> So using the crawler view works quite well because the content (i.e. the text) is largely the same?

I actually used the `noscript`-rendering of the posts inside a topic page. The CSS-classes do slightly match the real ones - so the look is equal.

See: [Changes to app/views/layouts/application.html.erb](https://github.com/rr-it/discourse/pull/1/commits/ac6831116ad6afc84f49e396afc7115ce07e26b7#diff-f43fe075643e681b2c01c2f853bb0c4299d135b47fcbd4da96890d521c49e3eb)

Edit: My fault, this is actually the crawler view: [app/views/topics/show.html.erb](https://github.com/rr-it/discourse/pull/1/commits/ac6831116ad6afc84f49e396afc7115ce07e26b7#diff-05a0a5c48fba0953efdf6d070f2410904e8cfe47044d66945a51d40b97cef63e)

---

<div class="post-metadata">

### Author: ![rrit](https://avatars.discourse-cdn.com/v4/letter/r/b5ac83/32.png) [@rrit](https://meta.discourse.org/u/rrit)
#### Post date: [February 7, 2022, 9:08pm UTC](https://meta.discourse.org/t/defer-javascript-and-show-interim-content-on-initial-page-load/216458/16 "2022-02-07T21:08:40Z")

</div>

> [@david](#):
>
> We’re open to experimenting with this in core. The ‘flash’ of differently-styled content can be a little disconcerting, so we’d like to start with it behind a default-disabled “experimental” site setting to start with.

In the POC there are two features combined - shall we split them into two experimental feature flags?

- JavaScript with defer-tag (feature flag in settings dashbord)  
~~(hidden feature flag as a container rebuild or theme cache flush is needed for this)~~ ← [Fix: hot-switching with cache](https://github.com/discourse/discourse/pull/15858/commits/c82b3cb2810c76d2e5641bf0258f79f1e7d87db6)
- Showing static content in topic view (feature flag in settings dashbord)

Here we go: [feature flags](https://github.com/discourse/discourse/pull/15858/commits/7e1fc782e69f6f1874da538679cb84554e17028b)

* * *

Of course the full impact on LCP is only accomplished by using both: `FCP: static content`

There might be Discourse instances where plugins or theme components fail on the JS defering. By splitting these feature, they can have a small gain on the static content without defering JS: `FCP: static content without JS defer`

[![](https://global.discourse-cdn.com/meta/optimized/3X/a/a/aabc80111306d900788962849f54786d234bb019_2_1035x420.png) ](https://global.discourse-cdn.com/meta/optimized/3X/a/a/aabc80111306d900788962849f54786d234bb019_2_1035x420.png)

---

<div class="post-metadata">

### Author: ![rrit](https://avatars.discourse-cdn.com/v4/letter/r/b5ac83/32.png) [@rrit](https://meta.discourse.org/u/rrit)
#### Post date: [February 11, 2022, 6:32pm UTC](https://meta.discourse.org/t/defer-javascript-and-show-interim-content-on-initial-page-load/216458/17 "2022-02-11T18:32:17Z")

</div>

First impression from Google Search Console with the POC applied since 2022-01-30:

### Desktop

Desktop took some time for results to come in:

 ![grafik](https://global.discourse-cdn.com/meta/original/3X/3/2/323211a16d409ab31a41362cd4c71984e393c727.png)  
Note: the old green baseline represents non-Discourse webpages on the same domain.

 ![grafik](https://global.discourse-cdn.com/meta/original/3X/7/4/74d702210c09f0e7a4ad8991bf56e4abf850b7d0.png)

### Mobile

 ![grafik](https://global.discourse-cdn.com/meta/original/3X/9/a/9aef9c4c395f688f037f1a65d54686394ffb8320.png)  
Note: the old green baseline represents non-Discourse webpages on the same domain.

 ![grafik](https://global.discourse-cdn.com/meta/original/3X/3/2/32352583d1c5a6daa322dd6b7997755c5ce623b8.png)

Let’s wait another 7-14 days to hopefully see more improvements for mobile pages as the values are averaged over the last 28 days - only 12 days counting with the POC applied right now.

---

<div class="post-metadata">

### Author: ![rrit](https://avatars.discourse-cdn.com/v4/letter/r/b5ac83/32.png) [@rrit](https://meta.discourse.org/u/rrit)
#### Post date: [March 3, 2022, 3:22pm UTC](https://meta.discourse.org/t/defer-javascript-and-show-interim-content-on-initial-page-load/216458/18 "2022-03-03T15:22:39Z")

</div>

## LCP summary on Proof of Concept

The POC is applied since 2022-01-30 and it took +4 weeks to affect all pages in Google Search Console “Core Web Vitals” report - based on CrUX data.

All topic pages are in the LCP green zone (measured by CrUX):

- Desktop: LCP 1.7 sec
- Mobile: LCP 2.0 sec

* * *

## LCP data: Google Search Console/CrUX

Impression from Google Search Console with the POC applied since 2022-01-30:

### Desktop

 ![grafik](https://global.discourse-cdn.com/meta/original/3X/9/0/901cfc54fa37aa9ef8c67898cad89c8701dc57bf.png)  
Note: the old green baseline represents non-Discourse webpages on the same domain.

 ![grafik](https://global.discourse-cdn.com/meta/original/3X/3/9/391491a0deb5512d51de8a93c38a145c66343086.png)

#### Good URLs

 ![grafik](https://global.discourse-cdn.com/meta/original/3X/c/a/ca429814b4b2d62ae38a238cda915d4ccf9aef63.png)

### Mobile

 ![grafik](https://global.discourse-cdn.com/meta/original/3X/5/9/591748ce59be98e79eb4585729c3f38f77832ce0.png)  
Note: the old green baseline represents non-Discourse webpages on the same domain.

 ![grafik](https://global.discourse-cdn.com/meta/original/3X/8/e/8e9df35414641b591cbfd0d57c3f2640d04d65b9.png)

#### Good URLs

 ![grafik](https://global.discourse-cdn.com/meta/original/3X/f/7/f7206c12a2daf1c78f9bf6de93f02bce258bb0ca.png)

#### LCP issue: longer than 2.5s (mobile)

 ![grafik](https://global.discourse-cdn.com/meta/original/3X/f/f/ff4ff28bfd804ca712e456bc36d20d661c706764.png)  
Note: Only topic pages show static content before EmberJS-content

* * *

## Approval of PR with feature flags needed

[https://github.com/discourse/discourse/pull/15858](https://github.com/discourse/discourse/pull/15858)

@sam May you delegate this PR to someone to take a look for approval, please.

---

<div class="post-metadata">

### Author: ![sam](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/sam/32/102149_2.png) [@sam](https://meta.discourse.org/u/sam)
#### Post date: [March 8, 2022, 3:09am UTC](https://meta.discourse.org/t/defer-javascript-and-show-interim-content-on-initial-page-load/216458/19 "2022-03-08T03:09:27Z")

</div>

> [@rrit](#):
>
> May you delegate this PR to someone to take a look for approval, please.

We will certainly carefully review it, it is a very big change may take us a bit to get to it.

---

<div class="post-metadata">

### Author: ![david](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/david/32/157490_2.png) [@david](https://meta.discourse.org/u/david)
#### Post date: [March 9, 2022, 12:48pm UTC](https://meta.discourse.org/t/defer-javascript-and-show-interim-content-on-initial-page-load/216458/29 "2022-03-09T12:48:49Z")

</div>

@rrit thanks for sharing the data from your site! We’ve been discussing this internally, and I’m afraid we won’t be adding this functionality to Discourse core at the moment.

While the Web Vital metrics you shared are very impressive, the flash of ‘crawler view’ content doesn’t make for a great user experience. The styling changes you’ve made certainly help, but they will need to be tweaked for every Discourse site that has custom styling.

Our long-term aim is to implement true server-side-rendering using something like [Ember FastBoot](https://ember-fastboot.com/). Theoretically, that would provide the same statistical improvements that you’ve measured, while also providing a seamless user experience. We would prefer to focus our efforts towards that goal.

* * *

All that said, Discourse is super extensible, so I think it should be totally possible to implement your idea in a Discourse plugin and then share it here in #Customization > Plugin.

The biggest change you’ve made in the core PR is to add the `defer` attribute to script tags. Overriding all those places from a plugin would be very difficult. However, I think the same result could be achieved with a middleware-based approach. I found this blog post which describes a similar problem:

> **[Rack/Rails middleware that will add rel="nofollow" to all your links - Closer...](https://mensfeld.pl/2014/12/rackrails-middleware-that-will-ensure-relnofollow-for-all-your-links/)**
>
> Table of Contents1 Nokogiri is the answer2 Small corner cases that we need to cover3 Hooking it up to Rack middleware4 Usage example5 Performance6 TL;DR - Whole middleware Few years ago I wrote a post about adding rel=”nofollow” to all the links in...

Using that technique, you could write a middleware which checks for `text/html` responses, parses them, and then adds `defer` attributes where necessary.

Adding middleware from a plugin can be done something like this:

```ruby
# name: my-plugin
# about: My plugin description
# version: 1.0
# url: https://example.org

require_relative "lib/script_defer_middleware"

on(:after_initializers) do
  Rails.configuration.middleware.use(ScriptDeferMiddleware)
end

```

If you run into any roadblocks with a plugin-based approach, feel free to post here and we’ll be happy to try and point you in the right direction.

---

<div class="post-metadata">

### Author: ![rrit](https://avatars.discourse-cdn.com/v4/letter/r/b5ac83/32.png) [@rrit](https://meta.discourse.org/u/rrit)
#### Post date: [March 9, 2022, 3:58pm UTC](https://meta.discourse.org/t/defer-javascript-and-show-interim-content-on-initial-page-load/216458/30 "2022-03-09T15:58:39Z")

</div>

> [@david](#):
>
> All that said, Discourse is super extensible, so I think it should be totally possible to implement your idea in a Discourse plugin and then share it here in #Customization > Plugin.
> 
> The biggest change you’ve made in the core PR is to add the `defer` attribute to script tags. Overriding all those places from a plugin would be very difficult. However, I think the same result could be achieved with a middleware-based approach.

If I find time for this, I will probably implement a plugin.

But for now I try to get along with a patching approach in `web_only.yml`:

```yaml
# not tested pseudo-code!
hooks:
  after_code:
    - exec:
        cd: $home
        cmd:
          - curl https://patch-diff.githubusercontent.com/raw/discourse/discourse/pull/15858.diff | git apply

```

---

<div class="post-metadata">

### Author: ![rrit](https://avatars.discourse-cdn.com/v4/letter/r/b5ac83/32.png) [@rrit](https://meta.discourse.org/u/rrit)
#### Post date: [March 9, 2022, 4:09pm UTC](https://meta.discourse.org/t/defer-javascript-and-show-interim-content-on-initial-page-load/216458/31 "2022-03-09T16:09:38Z")

</div>

> [@david](#):
>
> Our long-term aim is to implement true server-side-rendering using something like [Ember FastBoot](https://ember-fastboot.com/). Theoretically, that would provide the same statistical improvements that you’ve measured, while also providing a seamless user experience. We would prefer to focus our efforts towards that goal.

Ember FastBoot looks like a perfect long-term approach. Meanwhile the LCP topic stays hot:

> [@](#):
>
> **Update on February 22, 2022** : The page experience update is now slowly rolling out for desktop. It will be complete by the end of March 2022.

> **[Timeline for bringing page experience ranking to desktop  |  Google Search...](https://developers.google.com/search/blog/2021/11/bringing-page-experience-to-desktop?hl=en)**

---

<div class="post-metadata">

### Author: ![Johani](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/johani/32/176920_2.png) [@Johani](https://meta.discourse.org/u/Johani)
#### Post date: [July 5, 2022, 5:45pm UTC](https://meta.discourse.org/t/defer-javascript-and-show-interim-content-on-initial-page-load/216458/38 "2022-07-05T17:45:37Z")

</div>

Thank you for working on this @rrit 👍

I have good news, we’ve implemented a new feature in Discourse, which should help with this quite a bit

> [@Introducing Discourse Splash - A visual preloader displayed while site assets load](https://meta.discourse.org/t/introducing-discourse-splash-a-visual-preloader-displayed-while-site-assets-load/232003):
>
> Discourse is a single-page Javascript application. This means that navigation within the app is blazingly fast. The compromise with modern web applications is that they must be loaded and parsed on the initial page view. While we’ve been doing a lot of work over the years to optimize how we serve site assets, this can sometimes be a little bit slow based on device/network conditions. Over the last few weeks, we’ve been actively working and testing potential improvements for the initial wait th…

[Next page](https://meta.discourse.org/t/defer-javascript-and-show-interim-content-on-initial-page-load/216458.md?page=2)
