Many answers online are based on the assumption that lighthouse is detecting the FCP correctly, and so give instructions on how to resolve actual issues with your site, eg: slow load times, incorrect code, javascript errors, etc; or with your browser, eg: cookies, plugins, caching; This is indeed usually the cause.
However, Lighthouse is not infallible, and sometimes has reasons for actually mis-detecting a perfectly functioning page.
In my case, the problem was that Lighthouse appears to sometimes not consider any element which begins life at opacity: 0
, even if that beginning-of life is via an animation. For me, this issue was being triggered by a "fade-in" animation which was used to lessen FOUT. I suspect that this might be caused by an over-zealous fix to this issue from 2020, wherein previously any elements with opacity: 0
would be treated as immediately visible.
eg:
/* THIS IS MIS-DETECTED BY LIGHTHOUSE AS NEVER BEING VISIBLE */
body {
/* default opacity of 1, if animation is not supported */
opacity: 1;
animation-name: fadeIn;
animation-iteration-count: 1;
animation-timing-function: ease-in;
animation-duration: 0.5s;
}
@keyframes fadeIn {
0% {
opacity: 0;
}
50% {
opacity: 0;
}
100% {
opacity: 1;
}
}
Lighthouse will return the NO_FCP
error due to the first frame starting at an opacity of 0, even though the page does soon afterwards become opaque.
To trick Lighthouse into doing the right thing, ensure the first frame begins with some value other than zero.
eg:
/* THIS IS DETECTED BY LIGHTHOUSE AS EVENTUALLY BEING VISIBLE */
body {
/* default opacity of 1, if animation is not supported */
opacity: 1;
animation-name: fadeIn;
animation-iteration-count: 1;
animation-timing-function: ease-in;
animation-duration: 0.5s;
}
@keyframes fadeIn {
0% {
opacity: 0.01;
}
1% {
opacity: 0;
}
50% {
opacity: 0;
}
100% {
opacity: 1;
}
}