storybook-addon-next icon indicating copy to clipboard operation
storybook-addon-next copied to clipboard

Next12.2 future/image support

Open kodai3 opened this issue 2 years ago • 11 comments

Describe the bug

Using the experimental "allowFutureImage" with storybook, it throw Error.

The "next/future/image" component is experimental and may be subject to breaking changes. To enable this experiment, please include `experimental: { images: { allowFutureImage: true } }` in your next.config.js file.

Your minimal, reproducible example

nextjs example with-storybook-app

Steps to reproduce

  1. npx create-next-app --example with-storybook with-storybook-app
  2. add experimental config
// next.config.js
module.exports = {
  ...
  experimental: {
    images: {
      allowFutureImage: true,
    },
  },
}
  1. replace "next/image" to "next/future/image"
// pages/nextjsImages.js
import Image from 'next/future/image'

Expected behavior

with next.config.js setup, the stories including future/image should load without error.

How often does this bug happen?

Every time

Screenshots or Videos

image

Platform

  • mac OS
  • Chrome (102.0.5005.115)

storybook-addon-next version

1.6.7

Additional context

No response

kodai3 avatar Jun 29 '22 02:06 kodai3

@kodai3 found a quick hack by using this webpack alias:

    return {
      ...config,
      resolve: {
        ...config.resolve,
        alias: {
          ...config.resolve.alias,
          "next/future/image$": "next/image",
          // styles: path.resolve(__dirname, "../src/lib/"),
        },
      },
    };

CesarBenavides777 avatar Jul 02 '22 18:07 CesarBenavides777

This hack works. But, next/future/image render only img tag, while next/image render img tag wrapped with span. Storybook may show different results from production.

kodai3 avatar Jul 03 '22 12:07 kodai3

Yeah I guess the storybook add on will need to be updated. Otherwise we'd have to style for future and non future variants which would be annoying.

CesarBenavides777 avatar Jul 03 '22 13:07 CesarBenavides777

I am looking for a solution as well, the workaround doesn't align with the same container structure so the output will different between Next and Storybook

dohomi avatar Jul 14 '22 11:07 dohomi

Workaround with support of fill property from v12.2.4

// .storybook/preview.js

import * as NextFutureImage from "next/future/image";

Object.defineProperty(NextFutureImage, "default", {
  configurable: true,
  value: (props) => {
    const { fill, ...restProps } = props;

    return (
      <img
        {...restProps}
        style={
          fill
            ? {
                position: "absolute",
                height: "100%",
                width: "100%",
                inset: 0,
                color: "transparent",
              }
            : undefined
        }
      />
    );
  },
});

dawidk92 avatar Aug 23 '22 10:08 dawidk92

The fix for the fill property will be release in the next stable release: https://github.com/vercel/next.js/releases/tag/v12.2.6-canary.3

armandabric avatar Aug 24 '22 08:08 armandabric

Regarding @dawidk92 solution: In my case the src was returned as an object, so in order to make it work I had to destructure it a bit more:

const {
      fill,
      src: { src },
      ...restProps
    } = props

    console.log(restProps)

    return (
      <img
        src={src}
        {...restProps}

mkbctrl avatar Sep 06 '22 20:09 mkbctrl

An addition on @dawidk92 solution: in order to support both string paths and images object imports we can do the following:

Object.defineProperty(NextFutureImage, 'default', {
  configurable: true,
  value: (props) => {
    const { fill, src, ...restProps } = props;
    const fixedSrc = typeof src === 'object' ? src.src : src;

    return (
      <img
        {...restProps}
        src={fixedSrc}
        style={
          fill
            ? {
                position: 'absolute',
                height: '100%',
                width: '100%',
                inset: 0,
                color: 'transparent',
              }
            : undefined
        }
      />
    );
  },
});

JVictorLourenco avatar Sep 12 '22 12:09 JVictorLourenco

I think this is the simplest solution.

import Image from 'next/future/image';

const OriginalImage = Image.default;
Object.defineProperty(Image, 'default', {
  configurable: true,
  value: props => <OriginalImage {...props} unoptimized />,
});

Karibash avatar Sep 14 '22 17:09 Karibash

The best workaround i have made for now:

// /.storybook/preview.tsx
{
  const NextFutureImage = require('next/future/image') as typeof NextFutureImage__Type;

  const OriginalNextFutureImage = NextFutureImage.default;

  Object.defineProperty(NextFutureImage, 'default', {
    configurable: true,
    value: (props: NextFutureImageProps) => {
      const {src, ...restOfProps} = props;
      const fixedSrc: string = '/' + (typeof src === 'object' ? ('default' in src ? src.default.src : src.src) : src);
      const blurDataURL: string | undefined = props.src === 'string' ? props.src : undefined;

      return <OriginalNextFutureImage {...restOfProps} unoptimized src={fixedSrc} blurDataURL={blurDataURL} />;
    },
  });
}

This code is based on the original implementation of next/image in storybook-addon-next properly adjusted to work well with next/future/image instead.

IdkMan2Usertive avatar Sep 27 '22 11:09 IdkMan2Usertive

To fix the warning I have:

// .storybook/main.js

const webpack = require('webpack');
const ImageConfig = require('next/dist/shared/lib/image-config'); // <----------------

module.exports = {
  stories: [
    '../stories/**/*.stories.@(tsx|ts|jsx|js|mts|cts)',
  ],
  addons: [
    'storybook-addon-next',
  ],
  core: {
    builder: 'webpack5',
  },
  webpackFinal(config) {
    config.plugins.push(new webpack.DefinePlugin({ // <----------------
      'process.env.__NEXT_IMAGE_OPTS': JSON.stringify({
        ...ImageConfig.imageConfigDefault,
        experimentalFuture: true,
      }),
    }));

    return config;
  },
};

and

// .storybook/preview.js

import NextFutureImage from 'next/future/image';

const OriginalNextFutureImage = NextFutureImage.default;

Object.defineProperty(NextFutureImage, 'default', {
  configurable: true,
  value: props => <OriginalNextFutureImage {...props} unoptimized />,
});

bertho-zero avatar Oct 20 '22 13:10 bertho-zero

Since next 13 support has been released. I'm closing this issue

RyanClementsHax avatar Nov 28 '22 15:11 RyanClementsHax

@RyanClementsHax What is the fix in Next 13?

ackvf avatar Dec 20 '22 05:12 ackvf

@ackvf in next 13 next/future/image just becomes next/image which this addon supports

RyanClementsHax avatar Dec 20 '22 11:12 RyanClementsHax