vercel / remix

Build Better Websites. Create modern, resilient user experiences with web fundamentals.
https://remix.run
MIT License
65 stars 19 forks source link

Add `<Image>` component #65

Open lifeiscontent opened 12 months ago

lifeiscontent commented 12 months ago

Since Vercel supports image optimization out of the box, it would be really great to see a progressively enhanced image component for remix that uses Vercel's image optimization, and falls back to the default source URL in development

here's a rough proof of concept:

app/components/image.tsx

export function Image({
  src,
  width,
  loading = "lazy",
  ...props
}: Omit<React.ComponentPropsWithoutRef<"img">, "src" | "width"> &
  Required<Pick<React.ComponentPropsWithoutRef<"img">, "src" | "width">>) {
  return (
    // eslint-disable-next-line jsx-a11y/alt-text
    <img
      {...props}
      src={imageSrc(src, width)}
      loading={loading}
      width={width}
    />
  );
}

export function imageSrc(src: string, width: number | string, q = 75) {
  return `/resources/image?url=${encodeURIComponent(src)}&w=${width}&q=${q}`;
}

export function imageSrcSet(src: string, width: number, q = 75) {
  return `${imageSrc(src, width, q)}, ${imageSrc(
    src,
    width * 1.5,
    q
  )} 1.5x, ${imageSrc(src, width * 2, q)} 2x`;
}

app/routes/resources.image.tsx

import type { DataFunctionArgs } from "@remix-run/node";

function getAbsoluteURL(request: Request, url: string) {
  if (url.startsWith("http:") || url.startsWith("https:")) {
    return new URL(url).toString();
  }
  let host =
    request.headers.get("X-Forwarded-Host") ??
    request.headers.get("host") ??
    new URL(request.url).host;
  let protocol = host.includes("localhost") ? "http" : "https";
  if (url.startsWith("/")) {
    return new URL(url, `${protocol}://${host}`).toString();
  }
  return new URL(`${protocol}//${url}`).toString();
}

export function loader({ request }: DataFunctionArgs) {
  const url = new URL(request.url);
  const src = url.searchParams.get("url")!;
  const vercelUrl = "/_vercel/image" + url.search;

  const destination = process.env.VERCEL
    ? getAbsoluteURL(request, vercelUrl)
    : getAbsoluteURL(request, src);
  return fetch(destination);
}
anthonypecchillo commented 4 months ago

I would love to see support for this added to Remix. ☝️