Layout shift is nearly always an element that reserved no space and then took some, and images are the largest single cause.
<!-- reserves nothing until loaded -->
<img src="/hero.jpg" alt="">
<!-- reserves the right box, and stays responsive -->
<img src="/hero.jpg" width="1200" height="675" alt="">
<style>img { max-width: 100%; height: auto; }</style>
<!-- the other three causes:
a web font swapping metrics
an ad or embed injected above content
content inserted after load -->
Browsers compute an aspect ratio from the width and height attributes and reserve the space before the image arrives, which is why the attributes came back into fashion after a decade of being removed as unnecessary. The font case is fixed with font-display: optional or a metrics-matched fallback; the injected-content case is fixed by reserving the space in the markup, which nobody wants to do and which is the only thing that works.