Skip to content

Commit 49c7771

Browse files
committed
set image size in server component to prevent layout shift, works
1 parent 45c46fd commit 49c7771

File tree

7 files changed

+39
-8
lines changed

7 files changed

+39
-8
lines changed

docs/working-notes/todo4.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,13 @@ add delay to fetch new page, debounce
106106
phone tap on backdrop doesnt close lightbox
107107
responsive page size
108108
usehooks, isVisible or observer hook for infinite scroll, thumbnail, scroll to top
109+
110+
// important for size of all client components
111+
height and width MUST be defined ON SERVER component to prevent layout shift
112+
113+
----
114+
img srcset
115+
copy hooks for width for page size and observer form react-use or usehooks
109116
```
110117

111118

src/components/ImageRandom.astro

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,22 @@
11
---
22
import ImageRandomReact from '@/components/react/ImageRandom';
3+
import { IMAGE_SIZES } from '@/constants/image';
34
import { getImagesProps } from '@/libs/gallery/images';
5+
import { cn } from '@/utils/styles';
46
5-
// forward Astro (MDX) class attr to className React
67
const { class: className, ...props } = Astro.props;
78
89
const galleryImages = await getImagesProps();
10+
11+
// add 'px' suffix or styles will fail
12+
const { width, height } = Object.fromEntries(
13+
Object.entries(IMAGE_SIZES.FIXED.MDX_XL_16_9).map(([key, value]) => [key, `${value}px`])
14+
);
915
---
1016

11-
<ImageRandomReact {...props} {className} {galleryImages} client:load />
17+
{/* height and width MUST be defined ON SERVER component to prevent layout shift */}
18+
{/* set height and width to image size but set real size with max-height and max-width */}
19+
20+
<div class={cn('max-w-full max-h-96 my-8', className)} style={{ width, height }} {...props}>
21+
<ImageRandomReact {galleryImages} client:load />
22+
</div>

src/components/react/ImageBlurPreloader.tsx

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ const ImageBlurPreloader: FC<Props> = ({
2020
onSrcLoaded,
2121
className,
2222
divClassName,
23+
width,
24+
height,
2325
...props
2426
}) => {
2527
const [hasLoaded, setHasLoaded] = useState(false);
@@ -51,9 +53,15 @@ const ImageBlurPreloader: FC<Props> = ({
5153
return (
5254
<>
5355
{imageSrc ? (
54-
<img {...props} src={imageSrc} className={cn('', className)} />
56+
<img
57+
{...props}
58+
src={imageSrc}
59+
width={width}
60+
height={height}
61+
className={cn('object-cover', className)}
62+
/>
5563
) : (
56-
<div className={cn('aspect-video', divClassName)} />
64+
<div className={divClassName} />
5765
)}
5866
</>
5967
);

src/components/react/ImageRandom.tsx

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import type { FC, ImgHTMLAttributes } from 'react';
1010
interface Props extends ImgHTMLAttributes<HTMLImageElement> {
1111
// Important: must pass all allImagesSrc from the server or they wont be included on client
1212
galleryImages: ImageProps[];
13+
divClassName?: string;
1314
}
1415

1516
const imageAttributes: ImageAttributes = {
@@ -25,7 +26,7 @@ const initialImage: ImageProps = {
2526
xl: { ...imageAttributes },
2627
};
2728

28-
const ImageRandomReact: FC<Props> = ({ galleryImages, className, ...props }) => {
29+
const ImageRandomReact: FC<Props> = ({ galleryImages, className, divClassName, ...props }) => {
2930
const randomImage = useMemo(() => getRandomElementFromArray(galleryImages), [galleryImages]);
3031

3132
const [image, setImage] = useState(initialImage);
@@ -49,10 +50,13 @@ const ImageRandomReact: FC<Props> = ({ galleryImages, className, ...props }) =>
4950
{...props}
5051
blurSrc={image.blur.src}
5152
src={image.xl.src}
53+
width={image.xl.width}
54+
height={image.xl.height}
5255
onSrcLoaded={() => setHasLoaded(true)}
5356
alt={imageAlt}
5457
onClick={handleClick}
55-
className={cn('cursor-pointer', className)}
58+
className={cn('cursor-pointer w-full h-full my-0', className)}
59+
divClassName={cn('w-full h-full', divClassName)}
5660
/>
5761
);
5862
};

src/layouts/Centered.astro

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ const { class: className } = Astro.props;
1313
---
1414

1515
{/* flex here to take full height for pagination, flex-row align-stretch */}
16-
{/* h-full dosent work without all parents h-full */}
16+
{/* h-full doesn't work without all parents h-full */}
1717

1818
<Base {...props}>
1919
<main

src/layouts/Page.astro

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ const metadata = { title, description, image };
3232
---
3333

3434
<Centered {metadata} class={cn(className)}>
35+
{/* must not have flex, it will disable margin collapsing */}
3536
<article class="my-prose">
3637
<slot />
3738
</article>

src/pages/index.mdx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import ImageRandom from '../components/ImageRandom.astro';
88

99
# Hello and welcome
1010

11-
<ImageRandom class="w-full sm:max-h-96 object-cover" />
11+
<ImageRandom />
1212

1313
Welcome to my coding blog. Here, I will share my coding experiments and thoughts on React.js, Next.js, Node.js, Astro, Python, DevOps, and more. Feel free to read, explore, and comment. Or, if you are feeling overwhelmed by coding, you can take a break and enjoy some random landscape photos.
1414

0 commit comments

Comments
 (0)