Skip to content

fix: double loading image when placeholder is present #1686

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion src/runtime/components/NuxtImg.vue
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,7 @@ const nuxtApp = useNuxtApp()
const initialLoad = nuxtApp.isHydrating

onMounted(() => {
if (placeholder.value || props.custom) {
if (props.custom) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

there are things in this block we want to apply for placeholders, I think

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

at this point, if SSR nor SSR the placeholder it is already loaded, if the
if (placeholder.value ... will be kept a weird dance will happend the existing/loaded placeholder will get replaced by the mainSrc but the computed will repace it with the placeholder and then with the mainSrc once again ... so from here the double loading

Copy link

@pascalvaccaro pascalvaccaro Jun 9, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Following up on this, as we encountered the same issue using the placeholder prop.

@george-moongroup Daniel is right, there are some usecases where the placeholder is needed in the onMounted callback, especially in non-SSR mode (the placeholderLoaded ref would never be updated, leaving the image src as the URL of the placeholder, not the main image)

@danielroe even with a valid fix (I'm not asking to merge, the PR is for testing purposes only so I add to tweak the code to enable & run e2e testing), I'm still left wondering about the preload attribute in SSR-enabled environment when placeholder holds a "truthy" value:

  • should the link tag preload the placeholder URL? => it seems counter-intuitive as the goal of the placeholder is to "wait until" the main image is loaded. However the page will load faster this way as the browser won't have to wait for the main image.
  • should the link tag preload the actual URL? => it seems a bit useless to have a placeholder if the main image is preloaded. However, the page will load slower because it'll have to wait for the main image to be loaded.

Another point could be made about the loading prop when set to "lazy": should the placeholder feature still run? The browser won't load the main image until it estimates that it will be needed imminently: I feel like whenever loading="lazy", fetching both the placeholder AND the main image is a bit much, as the browser could only load the main image lazily.

Please advise πŸ™ Thank you for your work! ❀️

const img = new Image()

if (mainSrc.value) {
Expand Down Expand Up @@ -185,11 +185,14 @@ onMounted(() => {
emit('error', new Event('error'))
}
else {
placeholderLoaded.value = true
emit('load', new Event('load'))
}
return
}

imgEl.value.onload = (event) => {
placeholderLoaded.value = true
emit('load', event)
}

Expand Down
Loading