Skip to content

Commit 75d35fa

Browse files
committed
Add safe images and placeholder
1 parent 06fc6de commit 75d35fa

File tree

4 files changed

+108
-1
lines changed

4 files changed

+108
-1
lines changed

public/images/placeholder.png

7.76 KB
Loading

src/components/sections/speakers.astro

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
---
22
import { getCollection } from "astro:content";
33
import type { CollectionEntry } from "astro:content";
4-
import { Image } from "astro:assets";
4+
import Image from "@ui/SafeImage.astro";
55
import Section from "@ui/Section.astro"
66
77
const allSpeakers = await getCollection("speakers");

src/components/ui/SafeImage.astro

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
---
2+
import { Image } from 'astro:assets';
3+
4+
interface Props {
5+
src: string;
6+
alt?: string;
7+
width?: number;
8+
height?: number;
9+
placeholder?: string;
10+
[key: string]: any;
11+
}
12+
13+
const {
14+
src,
15+
alt = '',
16+
width,
17+
height,
18+
placeholder = '/images/placeholder.jpg',
19+
...rest
20+
} = Astro.props;
21+
22+
let imageSrc = src;
23+
let isValid = false;
24+
25+
try {
26+
const res = await fetch(src);
27+
if (res.ok) {
28+
isValid = true;
29+
} else {
30+
imageSrc = placeholder;
31+
const fallbackRes = await fetch(placeholder);
32+
isValid = fallbackRes.ok;
33+
}
34+
} catch {
35+
try {
36+
imageSrc = placeholder;
37+
const fallbackRes = await fetch(placeholder);
38+
isValid = fallbackRes.ok;
39+
} catch {
40+
isValid = false;
41+
}
42+
}
43+
---
44+
45+
{isValid ? (
46+
<Image
47+
src={imageSrc}
48+
alt={alt}
49+
width={width}
50+
height={height}
51+
{...rest}
52+
/>
53+
) : null}

src/components/ui/SafePicture.astro

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
---
2+
interface Props {
3+
src: string;
4+
alt?: string;
5+
placeholder?: string;
6+
class?: string;
7+
formats?: ("webp" | "jpeg")[];
8+
widths?: number[];
9+
[key: string]: any;
10+
}
11+
12+
const {
13+
src,
14+
alt = '',
15+
placeholder = '/images/placeholder.jpg',
16+
class: className,
17+
formats,
18+
widths,
19+
...rest
20+
} = Astro.props;
21+
22+
let imageSrc = src;
23+
let isValid = false;
24+
25+
async function validate(url: string): Promise<boolean> {
26+
try {
27+
const res = await fetch(url, { method: 'HEAD' });
28+
return res.ok;
29+
} catch {
30+
return false;
31+
}
32+
}
33+
34+
if (await validate(src)) {
35+
isValid = true;
36+
} else if (await validate(placeholder)) {
37+
imageSrc = placeholder;
38+
isValid = true;
39+
}
40+
---
41+
42+
{isValid ? (
43+
<picture>
44+
{imageSrc.endsWith('.webp') && <source srcset={imageSrc} type="image/webp" />}
45+
<img
46+
src={imageSrc}
47+
alt={alt}
48+
class={className}
49+
loading="lazy"
50+
decoding="async"
51+
{...rest}
52+
/>
53+
</picture>
54+
) : null}

0 commit comments

Comments
 (0)