Skip to content

feat: add Picture component for art-directed responsive images - #18

Open
intellix wants to merge 1 commit into
QwikDev:mainfrom
intellix:pr/picture-component
Open

feat: add Picture component for art-directed responsive images#18
intellix wants to merge 1 commit into
QwikDev:mainfrom
intellix:pr/picture-component

Conversation

@intellix

@intellix intellix commented Jul 17, 2026

Copy link
Copy Markdown

What

Issue: #17

Adds a Picture component for art-directed responsive images — serving different image files per viewport (e.g. a full-width desktop banner and a smaller mobile banner) via a native <picture> element.

import { Picture } from 'qwik-image';

<Picture
  layout="fullWidth"
  alt="Banner"
  loading="eager"
  fetchpriority="high"
  src={props.image}                 // fallback / default (desktop)
  sources={[
    { src: props.imageMobile, media: '(max-width: 767px)' },
    { src: props.image,       media: '(min-width: 768px)' },
  ]}
/>

Why

A common pattern for different mobile/desktop banners is two <Image> tags toggled with CSS (hidden md:block). But loading="eager" and fetchpriority="high" are honored by the browser even for display:none elements, so both images get fetched on the critical path — an LCP regression Lighthouse flags.

<picture> with <source media> fixes this at the platform level: the browser evaluates the media queries and downloads only the matching source. loading/fetchpriority on the fallback <img> apply to whichever source is selected, so exactly one request is made and correctly prioritized.

How

  • Reuses the existing Image component as the required fallback <img> child, and the existing getSrcSet/getSizes helpers for each source's responsive srcset.
  • Per-source width/height/aspectRatio overrides (fall back to Picture-level values) for tuning each source's generated srcset.
  • resolutions/imageTransformer$ come from the existing ImageContext, so the global useImageProvider config applies with no extra wiring.
  • Image's public API and behavior are unchangedPicture is a new, additive export.

Tests

Adds picture.spec.tsx — 5 tests via @builder.io/qwik/testing's createDOM — covering: fallback-only render, one <source> per entry with correct media/type, loading/fetchpriority on the <img> (verified as the last child), each source's src flowing through imageTransformer$, and per-source width override affecting that source's srcset.

Note: this also repoints the qwik-image:test nx target to run vitest directly, because the @nrwl/vite:test executor (Nx 19.7.4) is incompatible with the repo's pinned vitest@0.30.1/vite@4.3.1. Happy to split that into a separate change if preferred. CI (.github/workflows/test.yml) currently installs deps but doesn't run the test target — worth adding a test step in a follow-up so these tests execute on PRs.

🤖 Generated with Claude Code

Adds a `Picture` component that renders a native `<picture>` element with
art-directed `<source media>` children. The browser downloads only the
source whose media query matches, so serving different desktop/mobile banner
images no longer double-fetches under loading="eager"/fetchpriority="high"
(a common LCP regression when hiding a second <Image> with CSS).

- Reuses the existing Image component as the fallback <img> and the
  getSrcSet/getSizes helpers for per-source responsive srcsets.
- Per-source width/height/aspectRatio overrides (fall back to Picture-level).
- Global useImageProvider config (resolutions, imageTransformer$) applies
  with no extra wiring. Image's public API is unchanged.
- Adds the first component spec (picture.spec.tsx, 5 tests via createDOM) and
  wires the qwik-image test target to run vitest directly (the @nrwl/vite:test
  executor is incompatible with the pinned vitest 0.30/vite 4.3).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@changeset-bot

changeset-bot Bot commented Jul 17, 2026

Copy link
Copy Markdown

⚠️ No Changeset found

Latest commit: aaee8ef

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant