Code — how this page is built
Under the hood
The actual source of the components on this page, read straight from the repo. Copy it, read it, or open it on GitHub.
GalleryGameview on GitHub ↗
Fetches a random page from the keyless Art Institute API, picks an artwork with an image_id, and renders it from the IIIF endpoint with its title/artist/date.
'use client';
import * as React from 'react';
import { Win98Button } from '@/components/ui/win-98-button';
interface Art {
title: string;
artist: string;
date: string;
imageId: string;
}
export function GalleryGame() {
const [art, setArt] = React.useState<Art | null>(null);
const [loading, setLoading] = React.useState(true);
const [error, setError] = React.useState(false);
const load = React.useCallback(() => {
setLoading(true);
setError(false);
const page = 1 + Math.floor(Math.random() * 100);
fetch(`https://api.artic.edu/api/v1/artworks?page=${page}&limit=50&fields=id,title,artist_display,date_display,image_id`)
.then((r) => r.json())
.then((d) => {
const withImg = (d.data ?? []).filter((a: { image_id?: string }) => a.image_id);
const pick = withImg[Math.floor(Math.random() * withImg.length)];
if (!pick) throw new Error('none');
setArt({ title: pick.title, artist: pick.artist_display, date: pick.date_display, imageId: pick.image_id });
})
.catch(() => setError(true))
.finally(() => setLoading(false));
}, []);
React.useEffect(() => { load(); }, [load]);
return (
<div className="flex w-full max-w-md flex-col items-center gap-5">
<div className="flex min-h-[18rem] w-full items-center justify-center">
{error ? (
<div className="font-mono text-sm opacity-60">gallery unavailable — try again</div>
) : loading || !art ? (
<div className="font-mono text-sm opacity-50">fetching a work…</div>
) : (
/* eslint-disable-next-line @next/next/no-img-element */
<img
src={`https://www.artic.edu/iiif/2/${art.imageId}/full/600,/0/default.jpg`}
alt={art.title}
className="max-h-[60vh] w-auto rounded-md border border-foreground/20 shadow-[0_8px_24px_rgba(20,17,11,0.2)]"
/>
)}
</div>
{art && !loading && !error ? (
<div className="max-w-[44ch] text-center">
<div className="font-bold leading-snug">{art.title}</div>
<div className="mt-0.5 text-sm opacity-70">{art.artist}</div>
{art.date ? <div className="font-mono text-[11px] opacity-50">{art.date}</div> : null}
</div>
) : null}
<Win98Button onClick={load} disabled={loading} className="h-10 min-w-32 px-7 text-sm">
Next work
</Win98Button>
</div>
);
}