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

[NEXT-699] next/image is unusable in Server Components with loader function #41924

Open
1 task done
raed667 opened this issue Oct 27, 2022 · 5 comments
Open
1 task done
Labels
area: next/image Image optimization (next/image, next/legacy/image) linear: next template: bug A user has filled out the bug report template. Issue needs triaging

Comments

@raed667
Copy link

raed667 commented Oct 27, 2022

Verify canary release

  • I verified that the issue exists in the latest Next.js canary release

Provide environment information

Operating System:
  Platform: darwin
  Arch: x64
  Version: Darwin Kernel Version 21.6.0: Mon Aug 22 20:17:10 PDT 2022; root:xnu-8020.140.49~2/RELEASE_X86_64
Binaries:
  Node: 16.13.1
  npm: 8.1.2
  Yarn: 1.22.19
  pnpm: 7.1.7
Relevant packages:
  next: 13.0.0
  eslint-config-next: 13.0.0
  react: 18.2.0
  react-dom: 18.2.0

What browser are you using? (if relevant)

No response

How are you deploying your application? (if relevant)

No response

Describe the Bug

When using next/image with a custom loader in a Server Component we get the following error:

Uncaught Error: Functions cannot be passed directly to Client Components because they're not serializable.
  <... loader={function} src=... alt=...>
              ^^^^^^^^^^

Expected Behavior

I expect a custom loader to be usable with server components.

Link to reproduction

https://codesandbox.io/s/cocky-bohr-g3tqkt?file=/app/post/ImageWrapper.tsx:275-287

To Reproduce

Go to /post in the provided codesandbox.

NEXT-699

@raed667 raed667 added the template: bug A user has filled out the bug report template. Issue needs triaging label Oct 27, 2022
@ijjk ijjk added the area: next/image Image optimization (next/image, next/legacy/image) label Oct 27, 2022
@iankduffy
Copy link

Getting the same thing, to get the image working it has to be a client component which is not what I want.

@jee-r
Copy link

jee-r commented Feb 5, 2023

Not optimal but as a workaround it seems to work if you set the loader in next.config.js file

images: {
    loader: 'custom',
    loaderFile: './loader.tsx',
...
}

https://nextjs.org/docs/api-reference/next/image#loader-configuration

@LuudJanssen
Copy link
Contributor

next/image has to be a client component. It uses JS to detect when the image has loaded. Although I agree that it would be nice to be able to create a shell around next/image using loader which isn't a client component (perhaps by allowing a loader template string $src?q=$q), it won't save you much. The only thing it saves is that the loader function doens't end up in your JS bundle.

Keep in mind that client components are still server-side rendered, so requiring next/image to be a client component doesn't make it less usable.

@timneutkens timneutkens changed the title next/image is unusable in Server Components with loader function [NEXT-699] next/image is unusable in Server Components with loader function Feb 28, 2023
@jee-r
Copy link

jee-r commented Apr 6, 2023

@timneutkens
Copy link
Member

timneutkens commented Jul 17, 2023

next/image is a Client Component, which is why you can't serialize a function from a server component to the client component. Creating your own client component around it is entirely fine, there's no performance difference in doing so. As said next/image is already a Client Component, you just don't specifically see it is a Client Component. Also to clarify further Client Components are the same as components in pages, they are pre-rendered to HTML, so the <img /> tags will be in the initial HTML.

TLDR: Client Components are not bad, they are the components that you already knew. They are pre-rendered as part of the initial HTML.

With this context, it’s entirely fine to create your own client component around it:

'use client'
 
import Image from 'next/image'
 
const imageLoader = ({ src, width, quality }) => {
  return `https://example.com/${src}?w=${width}&q=${quality || 75}`
}
 
export default function Page() {
  return (
    <Image
      loader={imageLoader}
      src="me.png"
      alt="Picture of the author"
      width={500}
      height={500}
    />
  )
}

You can learn more in the documentation here: https://nextjs.org/docs/app/api-reference/components/image#loader. You can also set "loaderFile" globally: https://nextjs.org/docs/app/api-reference/components/image#loaderfile, however there is no performance win in doing so, it's purely convenience.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area: next/image Image optimization (next/image, next/legacy/image) linear: next template: bug A user has filled out the bug report template. Issue needs triaging
Projects
None yet
Development

No branches or pull requests

6 participants