Skip to content
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

Custom loaders are not recognized by next export #21079

Closed
janhoogeveen opened this issue Jan 13, 2021 · 37 comments · Fixed by #26998
Closed

Custom loaders are not recognized by next export #21079

janhoogeveen opened this issue Jan 13, 2021 · 37 comments · Fixed by #26998
Labels
good first issue Easy to fix issues, good for newcomers
Milestone

Comments

@janhoogeveen
Copy link

janhoogeveen commented Jan 13, 2021

What version of Next.js are you using?

10.0.5

What version of Node.js are you using?

v15.5.0

What browser are you using?

Chrome

What operating system are you using?

macOS

How are you deploying your application?

next export

Describe the Bug

I'm using next/image with a custom loader. The reason for the custom loader is that I've optimised all images on build-time.

When I run next export I will get this message:

Error: Image Optimization using Next.js' default loader is not compatible with next export.
Possible solutions:

  • Use next start, which starts the Image Optimization API.
  • Use Vercel to deploy, which supports Image Optimization.
  • Configure a third-party loader in next.config.js.

Expected Behavior

Next.js understands that I'm using a custom loader, and that a static export would work fine.

Maybe an option is to pass the custom loader as a function to next.config.js?

module.exports = {
  images: {
    loader: (width, src, quality) => `/preprocessed-images/${filename}-${width}.jpg`
  },
}

I would also be fine with an API like this:

module.exports = {
  images: {
    loader: 'static'
  },
}

To Reproduce

  • Use next/image with a custom loader (see body for examples)
  • Run next export

This is all possibly related to #19612, although I didn't get the impression that the issue was recognized as such.

Custom loader
I've got a custom loader defined:
For the example I'm just a default return statement, but normally I'm using some props and helpers here

const myLoader = ({src}) => {
  return `/preprocessed-images/${src}`;
};

Image component

<Image
	loader={myLoader}
    src={`e72657a9019403244cd61aebf1a5752ce1d03b51.jpg`}
    width="640"
    height={480}
/>

Preprocessed images

My folder of preprocessed images looks like this:
image

In my mind, this should work, but I can't tell Next.js that I don't need a Node server.

Let me know if you need a reduced test case or an example (I already have one). I can't publicly post the full example here, yet.

@janhoogeveen janhoogeveen added the bug Issue was opened via the bug report template. label Jan 13, 2021
@Timer Timer added kind: bug and removed bug Issue was opened via the bug report template. labels Jan 13, 2021
@Timer Timer added this to the iteration 16 milestone Jan 13, 2021
@timneutkens timneutkens modified the milestones: iteration 16, 10.x.x Jan 15, 2021
@timneutkens timneutkens added the good first issue Easy to fix issues, good for newcomers label Jan 15, 2021
@MrSaints
Copy link

MrSaints commented Feb 3, 2021

Potentially related https://github.com/vercel/next.js/discussions/19372

FWIW, the check isn't doing anything smart

if (isNextImageImported && loader === 'default' && !hasNextSupport) {
throw new Error(
`Image Optimization using Next.js' default loader is not compatible with \`next export\`.
Possible solutions:
- Use \`next start\`, which starts the Image Optimization API.
- Use Vercel to deploy, which supports Image Optimization.
- Configure a third-party loader in \`next.config.js\`.
Read more: https://err.sh/next.js/export-image-api`
)
}
}

You can set:

images: {
    loader: "imgix",
    path: "https://noop/",
},

And it'll pass.

@andrekutianski
Copy link

same here, no settings from docs works or anything that can un-set the 'default' image loader or possibility to create a custom. I will abandon the use of this framework just because the enforcement of use vercel 'image optimation

@leerob
Copy link
Member

leerob commented Mar 12, 2021

This does appear to be an issue, also reported with #22980. Moving convo here!

I will abandon the use of this framework just because the enforcement of use vercel 'image optimation

It's not enforcing you to use Vercel, this is just a bug 👍

@Simply007
Copy link
Contributor

Hello everybody,

@leerob - do you have any further info about the ETA?

@Simply007
Copy link
Contributor

BTW: to make my custom loader working correctly, I needed
to set a path to an empty string - @MrSaints "https://noop/" was prepending this to the final image URLs" :

https://github.com/Kentico/kontent-starter-corporate-next-js/blob/186d3ca678583cdd01fe845eb8bbe3f5d118ea4b/next.config.js#L6

  images: {
   loader: "imgix",
   path: "",
 }

the custom loader logic (should be irrelevant but for the context)

@ptcampbell
Copy link

The next/image component's default loader is not supported when using next export. However, other loader options will work.

Is this supposed to have been fixed in 10.1? I get the same error whether I use the above workaround or supply my own custom loader.

@superbull
Copy link

Vote for this api:

Maybe an option is to pass the custom loader as a function to next.config.js?

module.exports = {
  images: {
    loader: (width, src, quality) => `/preprocessed-images/${filename}-${width}.jpg`
  },
}

@PatrickMcLennan
Copy link

PatrickMcLennan commented Apr 18, 2021

+1 to receiving this. Custom loader works great with dev and production servers, but running next export gives me the error that there is no custom loader.

@dougaraujos

This comment has been minimized.

@richstokes
Copy link

+1

Adding

images: {
   loader: "imgix",
   path: "",
 }

It will export, but when I open the resultant index.html, none of the images or JS load :(

@robbie-c
Copy link

+1 was investigating using next.js for our company site but decided not to because of this

This would have been fine:

module.exports = {
  images: {
    loader: 'static'
  },
}

@ahmed-abdelfata7
Copy link

+1

Adding

images: {
   loader: "imgix",
   path: "",
 }

It will export, but when I open the resultant index.html, none of the images or JS load :(

Yes it's working with me also thanks 👍

@franklinjavier
Copy link

+1

Adding

images: {
   loader: "imgix",
   path: "",
 }

It will export, but when I open the resultant index.html, none of the images or JS load :(

it worked for me

module.exports = {
  images: {
    loader: 'imgix',
    path: '/',
  },
}

@vitaminac
Copy link

vitaminac commented Jun 26, 2021

I faced the same problem when using next/image with next export command, even in every usage of next/image I've specified the loader option. I still receive the error of

Error: Image Optimization using Next.js' default loader is not compatible with next export.
Possible solutions:

Use next start, which starts the Image Optimization API.
Use Vercel to deploy, which supports Image Optimization.
Configure a third-party loader in next.config.js.

The solution is to add following config inside the next.config.js

module.exports = {
  images: {
    loader: 'imgix',
    path: '',
  },
}

But I don't think it is good to says imgix when we don't actually use it. A better option might be the following, but If I changed to this

module.exports = {
  images: {
    loader: 'static'
  },
}

I will receive error of

Error: Specified images.loader should be one of (default, imgix, cloudinary, akamai), received invalid value (static).
See more info here: https://nextjs.org/docs/messages/invalid-images-config

Checking the code only a subset of value is allowed

if (!VALID_LOADERS.includes(images.loader)) {
and
export const VALID_LOADERS = [
.

Maybe we can just add static to this list of valid loader and update the doc to solve the problem

export const VALID_LOADERS = [

@vercel vercel deleted a comment from MohanSaiTeki Jun 30, 2021
@rohan-deshpande
Copy link

I want to chime in here and say that I currently do not understand how this API is supposed to work as the docs indicate.

I'm building an app with Firebase and my images are stored in their Cloud Storage layer. My app uses Firebase's API to retrieve the the absolute URL for the image from Cloud Storage.

To get things to work I have done configured my next.js.config like so

        images: {
          domains: ['firebasestorage.googleapis.com'],
        },

This works in dev, but when I try to export, I get the same error as everyone else saying I need to configure a custom loader. So I've done that

  const myLoader = ({ src, width, quality }) => {
    return `${src}?w=${width}&q=${quality || 75}`;
  };

However, this still does not let me run the export function. I might be missing something but not sure how to get it to work currently.

@ArekBartnik
Copy link

@rohan-deshpande this bug wasn't fixed. If you use custom loader for Image you can add this to next.config.js:

module.exports = {
  images: {
    domains: ['firebasestorage.googleapis.com'],
    loader: 'imgix' // this is a hack until the bug is fixed
  },
}

and the export command should work.

@rohan-deshpande
Copy link

rohan-deshpande commented Jul 1, 2021

That causes the images to 404 with _next/image appended to their URLs. What's odd is that the custom loader function does not seem to get hit in dev

  const customLoader = ({ src, width, quality }) => {
    console.log('SRC', src); // this never logs
    return `${src}?w=${width}&q=${quality || 75}`;
  };

  return <Image src={firebaseCloudStorageAbsoluteURL} layout="fill" loader={customLoader} />;

@darshkpatel
Copy link
Contributor

Started a discussion for the RFC here: #26850

@le-martre
Copy link

@rohan-deshpande @ArekBartnik You need to specify the path property to avoid having your url prefixed with _next/image, like so :

module.exports = {
  images: {
    domains: ['firebasestorage.googleapis.com'],
    loader: 'imgix', // this is a hack until the bug is fixed
    path: ''
  },
}

@leerob
Copy link
Member

leerob commented Jul 7, 2021

Please follow the PR here! #26847

@kodiakhq kodiakhq bot closed this as completed in #26998 Jul 8, 2021
kodiakhq bot pushed a commit that referenced this issue Jul 8, 2021
… applicable (#26998)

Since we are no longer accepting new built-in loaders, users may wish to use a different cloud provider.

So this PR renames `dangerously-unoptimized` to `custom` to handle this case as well as the intention of `next export`.

If the user doesn't add a `loader` prop, we throw an error.
If the user adds a `loader` prop but it doesn't return the width, we print a warning.

- Follow up to #26847 
- Fixes #21079 
- Fixes #19612 
- Related to #26850
@styfle
Copy link
Member

styfle commented Jul 9, 2021

PR #26998 is available in 11.0.2-canary.8.

You can try it out today with yarn add next@canary, thanks!

@kiranmarshall
Copy link

Potentially related https://github.com/vercel/next.js/discussions/19372

FWIW, the check isn't doing anything smart

if (isNextImageImported && loader === 'default' && !hasNextSupport) {
throw new Error(
`Image Optimization using Next.js' default loader is not compatible with \`next export\`.
Possible solutions:
- Use \`next start\`, which starts the Image Optimization API.
- Use Vercel to deploy, which supports Image Optimization.
- Configure a third-party loader in \`next.config.js\`.
Read more: https://err.sh/next.js/export-image-api`
)
}
}

You can set:

images: {
    loader: "imgix",
    path: "https://noop/",
},

And it'll pass.

Can you make it so we can pass 'custom' as a key (alternatively to passing the loader function) and not have it fail please? It's a small thing but this caught me out on the docs for ages because it infers that custom is an accepted value - I ended up passing cloudinary personally, despite the fact I'm using Contentful, and it works fine.

@leerob
Copy link
Member

leerob commented Aug 8, 2021

@kiranmarshall Did you try this? #21079 (comment)

@jcollum
Copy link

jcollum commented Aug 13, 2021

For a brand new app created with npm init next-app I ran into an error when exporting to static with path: "" but path: "https://noop/", worked

@styfle
Copy link
Member

styfle commented Aug 13, 2021

The path config option doesn't affect that error message, only loader config.

If you loader: "custom" in next.config.js and then set a loader prop on image component to configure your 3rd party image optimization service.

See the Next.js 11.1.0 blog post here: https://nextjs.org/blog/next-11-1#other-image-improvements

@jcollum
Copy link

jcollum commented Aug 13, 2021

This fix worked for me:

next.config.js:

  images: {
    loader: "imgix",
    path: "https://noop/",
  }

@Simply007
Copy link
Contributor

I have tested the custom loader and I am getting this error:

https://github.com/Kentico/kontent-starter-corporate-next-js/pull/51/checks?check_run_id=3338395712#step:6:46

Unhandled error during request: Error: Image with src "https://assets-us-01.kc-usercontent.com:443/***/32212623-9886-4799-a843-df36b46f4c17/pexels-fauxels-3184339.jpg" is missing "loader" prop.
Read more: https://nextjs.org/docs/messages/next-image-missing-loader

I have double-checked that the loader property is passed in my implementation: https://github.com/Kentico/kontent-starter-corporate-next-js/blob/custom-loader/components/Image.js

I have tested Next.js v11.1.0.

My pull request with CI set to static export: https://github.com/Kentico/kontent-starter-corporate-next-js/pull/51

@styfle
Copy link
Member

styfle commented Aug 16, 2021

@Simply007 You need to do two things:

  1. Configure images.loader: "custom" in next.config.js
  2. Add the loader prop to each image component

That error message looks like you did number 1 but not number 2.

If you visit the error message url printed there you'll find more info: https://nextjs.org/docs/messages/next-image-missing-loader

@Simply007
Copy link
Contributor

Simply007 commented Aug 16, 2021

but not number 2.

As I said - I have checked that I am passing loader property in Next Image component -> code here https://github.com/Kentico/kontent-starter-corporate-next-js/blob/custom-loader/components/Image.js#L21

I have debugged the process and the loader was initialized.

-> I will add logging so that it is visible in CI log.

EDIT: @styfle
Logging discovered that sometimes the loader is not set: https://github.com/Kentico/kontent-starter-corporate-next-js/pull/51/checks?check_run_id=3340892798#step:6:47 - I will dig deeper and describe the root cause in that comment - thx.

@Simply007
Copy link
Contributor

but not number 2.

As I said - I have checked that I am passing loader property in Next Image component -> code here https://github.com/Kentico/kontent-starter-corporate-next-js/blob/custom-loader/components/Image.js#L21

I have debugged the process and the loader was initialized.

-> I will add logging so that it is visible in CI log.

EDIT: @styfle
Logging discovered that sometimes the loader is not set: https://github.com/Kentico/kontent-starter-corporate-next-js/pull/51/checks?check_run_id=3340892798#step:6:47 - I will dig deeper and describe the root cause in that comment - thx.

@styfle

Just to share my conclusion. The problem was on my side 🤦‍♂️. I was not passing loader on a couple of places, because I have been using bad react prop as a URL.

flybayer pushed a commit to blitz-js/next.js that referenced this issue Aug 19, 2021
… applicable (vercel#26998)

Since we are no longer accepting new built-in loaders, users may wish to use a different cloud provider.

So this PR renames `dangerously-unoptimized` to `custom` to handle this case as well as the intention of `next export`.

If the user doesn't add a `loader` prop, we throw an error.
If the user adds a `loader` prop but it doesn't return the width, we print a warning.

- Follow up to vercel#26847 
- Fixes vercel#21079 
- Fixes vercel#19612 
- Related to vercel#26850
@omarabid
Copy link

omarabid commented Sep 7, 2021

This is annoying. I wasted a couple of hours for something that could have been disabled with a simple configuration line. Should add something like "image-optimization: false".

@ch-hristov
Copy link

Why is this so hard to do?

@leerob
Copy link
Member

leerob commented Oct 8, 2021

@ch-hristov Did you see this message? #21079 (comment)

@leerob
Copy link
Member

leerob commented Oct 8, 2021

Also, we recently completely revampled the next/image docs. Would love to hear feedback: https://nextjs.org/docs/basic-features/image-optimization

@ch-hristov
Copy link

ch-hristov commented Oct 8, 2021

@leerob yes, we were using img tags and we saw warning that we should use next Image tag etc. and then we took 3 hrs trying to figure out how to put those loaders in. I think it should be easier to put a simple image in.

@pi43r
Copy link

pi43r commented Oct 23, 2021

hmm... I'm still getting the warning has a "loader" property that does not implement width. Please implement it or use the "unoptimized" property instead. even though I am returning width. The srcset correctly set, so I'm not sure if I can just ignore this.
The next.config.js:

images: {
  domains: ['domain.url'],
  deviceSizes: [100, 500,  1200, 1920],
  loader: 'custom',
  path: 'https://domain.url',
},

and my loader (optimized images are in subfolders images/name.jpg, images/100/name.jpg etc...)
my loader:

const myLoader = ({ src, width, quality }) => {
	const baseUrl = process.env.NEXT_PUBLIC_BASE_URL
	let split = ''
	let source = src
	if (width == 100 || width == 500 || width == 1200) {
		split = src.split('/')
		const img = split.pop()
		split = split.join('/') + '/'
		source = '/' + img
	} else {
		width = ''
	}

	return `${baseUrl}${split}${width}${source}`
}

@balazsorban44
Copy link
Member

This issue has been automatically locked due to no recent activity. If you are running into a similar issue, please create a new issue with the steps to reproduce. Thank you.

@vercel vercel locked as resolved and limited conversation to collaborators Jan 27, 2022
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
good first issue Easy to fix issues, good for newcomers
Projects
None yet
Development

Successfully merging a pull request may close this issue.