Optimize image loading

Hi everyone I'm trying to create an image component to be optimized the loading, etc. I see this repository on a video: https://github.com/w3cj/solid-vs-react/blob/main/solid-image-search/src/components/ImageLoader.tsx But I'm trying to use it and facing an error: IntersectionObserver is not defined I need to do someting on SolidStart?
GitHub
solid-vs-react/solid-image-search/src/components/ImageLoader.tsx at...
This repo has a few apps built with react and solid for comparison. - w3cj/solid-vs-react
4 Replies
Tommypop
Tommypop4mo ago
In SolidStart, components run twice: once on the server, and once on the client. IntersectionObserver doesn't exist on the server, so, when the component runs on the server-side it throws an error. To fix this, you could wrap your IntersectionObserver in onMount, which runs when the component is mounted to the DOM
let observer;
onMount(() => {
observer = new IntersectionObserver((entries) => {
entries.forEach((entry) => {
if (entry.isIntersecting) {
setImgSrc(photo.src.small)
loadImage();
observer.unobserve(entry.target);
}
});
});
})

onCleanup(() => {
observer.disconnect();
});
let observer;
onMount(() => {
observer = new IntersectionObserver((entries) => {
entries.forEach((entry) => {
if (entry.isIntersecting) {
setImgSrc(photo.src.small)
loadImage();
observer.unobserve(entry.target);
}
});
});
})

onCleanup(() => {
observer.disconnect();
});
Daniel Sousa @TutoDS
Seems not working the loadImage :/ Do you suggest any way else to optimize images? I try to add some console.logs on onMount, like before the loadImage() but don't appear on the console and the image doens't load
lxsmnsyc
lxsmnsyc4mo ago
The same rule applies in NextJS: you are constructing an IntersectionObserver during the component's setup/render, which isn't available in SSR. You can basically copy something like this: https://github.com/lxsmnsyc/solid-tiny-router/blob/bf795238c341cb10b6bc333c739c784e4bbfdbbc/packages/solid-tiny-router/src/components/Link.tsx#L66-L85
Daniel Sousa @TutoDS
import { type JSX, createSignal, onCleanup, onMount, createEffect } from 'solid-js';

type Props = Omit<JSX.ImgHTMLAttributes<HTMLImageElement>, 'src'> & {
src: NonNullable<JSX.ImgHTMLAttributes<HTMLImageElement>['src']>;

/**
* Image to show while the image is loading.
*/
loadingImgSrc?: string;
};

export default function ImageLoader(props: Props) {
const [imgSrc, setImgSrc] = createSignal('');
const [imageRef, setImageRef] = createSignal<Element>();

const loadImage = () => {
const img = new Image();
img.onload = () => {
setImgSrc(props.src);
};

img.src = props.src;
};

createEffect(() => {
const observer = new IntersectionObserver((entries) => {
entries.forEach((entry) => {
if (entry.isIntersecting) {
if (props.loadingImgSrc) {
setImgSrc(props.loadingImgSrc);
}

loadImage();
observer.unobserve(entry.target);
}
});

if (imageRef()) {
observer.observe(imageRef());
}

onCleanup(() => {
observer.disconnect();
});
});
});

return (
<img
{...props}
ref={setImageRef}
class="loading-image"
// style={{
// 'aspect-ratio': props.width / props.height,
// }}
src={imgSrc()}
alt={props.alt}
/>
);
}
import { type JSX, createSignal, onCleanup, onMount, createEffect } from 'solid-js';

type Props = Omit<JSX.ImgHTMLAttributes<HTMLImageElement>, 'src'> & {
src: NonNullable<JSX.ImgHTMLAttributes<HTMLImageElement>['src']>;

/**
* Image to show while the image is loading.
*/
loadingImgSrc?: string;
};

export default function ImageLoader(props: Props) {
const [imgSrc, setImgSrc] = createSignal('');
const [imageRef, setImageRef] = createSignal<Element>();

const loadImage = () => {
const img = new Image();
img.onload = () => {
setImgSrc(props.src);
};

img.src = props.src;
};

createEffect(() => {
const observer = new IntersectionObserver((entries) => {
entries.forEach((entry) => {
if (entry.isIntersecting) {
if (props.loadingImgSrc) {
setImgSrc(props.loadingImgSrc);
}

loadImage();
observer.unobserve(entry.target);
}
});

if (imageRef()) {
observer.observe(imageRef());
}

onCleanup(() => {
observer.disconnect();
});
});
});

return (
<img
{...props}
ref={setImageRef}
class="loading-image"
// style={{
// 'aspect-ratio': props.width / props.height,
// }}
src={imgSrc()}
alt={props.alt}
/>
);
}
Like this? If yes, it's something wrong... Only way to try to show a lower image while is loading is this:
import { type JSX, createMemo, createSignal } from 'solid-js';
import { cn } from '~/shared/utils';

type Props = Omit<JSX.ImgHTMLAttributes<HTMLImageElement>, 'src' | 'onLoad'> & {
src: NonNullable<JSX.ImgHTMLAttributes<HTMLImageElement>['src']>;

/**
* Image to show while the image is loading.
*/
loadingImgSrc?: string;
};

export default function ImageLoader(props: Props) {
const { loadingImgSrc, ...restOfProps } = props;
const [imgSrc, setImgSrc] = createSignal(props.loadingImgSrc ?? '');

const loadImage = createMemo(() => {
setImgSrc(props.src);
});

return (
<img
{...restOfProps}
class={cn(['bg-gray-500', props.class ?? ''])}
onLoad={loadImage}
src={imgSrc()}
alt={props.alt}
/>
);
}
import { type JSX, createMemo, createSignal } from 'solid-js';
import { cn } from '~/shared/utils';

type Props = Omit<JSX.ImgHTMLAttributes<HTMLImageElement>, 'src' | 'onLoad'> & {
src: NonNullable<JSX.ImgHTMLAttributes<HTMLImageElement>['src']>;

/**
* Image to show while the image is loading.
*/
loadingImgSrc?: string;
};

export default function ImageLoader(props: Props) {
const { loadingImgSrc, ...restOfProps } = props;
const [imgSrc, setImgSrc] = createSignal(props.loadingImgSrc ?? '');

const loadImage = createMemo(() => {
setImgSrc(props.src);
});

return (
<img
{...restOfProps}
class={cn(['bg-gray-500', props.class ?? ''])}
onLoad={loadImage}
src={imgSrc()}
alt={props.alt}
/>
);
}
But I prefer the part where the load only starts whe the user intersect the image itself but isn't working
Want results from more Discord servers?
Add your server