withastro / roadmap

Ideas, suggestions, and formal RFC proposals for the Astro project.
290 stars 29 forks source link

First-party support for a Picture component #700

Closed Princesseuh closed 11 months ago

Princesseuh commented 11 months ago

Summary

astro:assets shipped successfully in 3.0. Now that it's available for everyone to enjoy, we'd like to start extending it with new features. One of the most requested feature coming from 3.0 was a Picture component, to allow supporting multiple format and sizes.

Background & Motivation

Images on the web are tricky 😅

When it comes to performance, it's best for pages to load the correct size and image format based on the browser's support and window size. Multiple sizes can be handled by <img srcset="">, but only the <picture> element has support for defining multiple image formats like .jpg, .webp, and .avif for the browser to select from. Ideally, supporting both is the most optimal thing to do, but a Picture component would be a start.

Goals

Out of scope

Example implementations

None of the implementation below are finals, they're merely inspirations to get the discussion going.

@astrojs/image's <Picture>

@astrojs/image included a <Picture> component that may be a decent starting point. It had a few shortcomings (didn't apply width and height as expected, couldn't set attributes on the underlying img etc.) but nothing we wouldn't be able to solve in a more complete version.

---
import { Picture } from "@astrojs/image/components"
---

<Picture widths={[450, 900, 1800]} formats={["avif", "webp", "jpg"]} />

Composition API

Essentially an easier to use normal picture element. Every source element could inherit props from the Picture component (somehow), so you'd be able to set, ex: a src for Picture that would work for every Source that does not have its own src

---
import {Picture, Source} from "astro:assets";
import myImage from "../something.png";
---

<Picture src={myImage}>
    <Source format="avif" width={500} />
    <Source />
<Picture>
louiss0 commented 11 months ago

Can we please get Client Component support?

Princesseuh commented 11 months ago

Can we please get Client Component support?

That would be a separate proposal. Feel free to create a discussion for it.

louiss0 commented 11 months ago

Can we please get Client Component support?

That would be a separate proposal. Feel free to create a discussion for it.

Ok fne then can you explain how this code is supposed to work? What would the output be?

<Picture src={myImage}>
    <Source format="avif" width={500} />
    <Source />
<Picture>
maddsua commented 11 months ago

Can we please get Client Component support?

That would be a separate proposal. Feel free to create a discussion for it.

Is that the moment where I should make another shameless plug of my thing that kinda does just that... it's not really a question https://github.com/maddsua/ssg-assets/blob/main/components/astro/Picture.astro

It generates sources for different image formats and can display different images based on media queries

kanashimia commented 11 months ago

Composition API

I don't think that is possible right now the way it is in the example, but I was able to make this:

<Picture
  src={image}
  alt=""
  property="image"
  quality="mid"
>
  {(props) => (
    <>
      <Source {...props} format="avif" />
      <Source {...props} format="jxl" />
    </>
  )}
</Picture>

This is done using Astro.slots.render

This doesn't look that nice, but I think even such annoying API would be usable, users should make their own image components with their preferred configuration based on this anyways. Typing this function argument would be annoying, it is inferred as any here.

After some thinking I'm not sure that such prop inheritance is even that much needed, users can define variables, and that would be good enough.

I also want to point out the problem with naming of srcset replacement, previous picture component used widths, but in the example for Composition API there is width, width on source elements means something quite different from srcset.

Princesseuh commented 11 months ago

Still working on a formal RFC, but I have a initial PR here https://github.com/withastro/astro/pull/8620

jlarmstrongiv commented 11 months ago

@Princesseuh is there a good/recommended way to try out the PR?

I’ve been toying with implementing a picture component like unpic (with fullWidth, fixed, and constrained layout types), but I’d prefer to use the official component if possible. One thing I like about unpic is that their components work across all frameworks given an image CDN api (not sure how to get something like that working for SSG though)

Princesseuh commented 11 months ago

@Princesseuh is there a good/recommended way to try out the PR?

I’ve been toying with implementing a picture component like unpic (with fullWidth, fixed, and constrained layout types), but I’d prefer to use the official component if possible. One thing I like about unpic is that their components work across all frameworks given an image CDN api (not sure how to get something like that working for SSG though)

I have a preview release that you can install npm install astro@next--picture. It's of course still a work in progress and has no documentation (apart from the changeset), in addition, this is pre-RFC, so not necessarily the final design for it, but you can try it out still!

Princesseuh commented 11 months ago

This Proposal is now a Stage 3 RFC, please leave your comments here: https://github.com/withastro/roadmap/pull/715