storybook-addon-next
storybook-addon-next copied to clipboard
Next12.2 future/image support
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
-
npx create-next-app --example with-storybook with-storybook-app
- add experimental config
// next.config.js
module.exports = {
...
experimental: {
images: {
allowFutureImage: true,
},
},
}
- 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

Platform
- mac OS
- Chrome (102.0.5005.115)
storybook-addon-next version
1.6.7
Additional context
No response
@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/"),
},
},
};
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.
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.
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
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
}
/>
);
},
});
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
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}
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
}
/>
);
},
});
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 />,
});
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.
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 />,
});
Since next 13 support has been released. I'm closing this issue
@RyanClementsHax What is the fix in Next 13?
@ackvf in next 13 next/future/image just becomes next/image which this addon supports