[Feature Request]: Support custom Next.js image loader via next.config
#31758 opened on Jun 12, 2025
Description
Discussed in https://github.com/storybookjs/storybook/discussions/31095
Originally posted by petrch87 April 9, 2025
Summary
I'm using Storybook within a Turborepo monorepo containing a Next.js app (pressweb). The Next.js app uses a custom image loader defined via loader: 'custom' and loaderFile: './lib/imageLoader.ts' in its next.config.ts, which works correctly within the app.
I need to get this same custom loader working for next/image components within Storybook (@storybook/nextjs framework). I've reviewed the official @storybook/nextjs docs (@https://storybook.js.org/docs/get-started/frameworks/nextjs?renderer=react), but couldn't find a clear solution for custom loaders.
Configuring Storybook via the nextConfigPath option in main.ts (pointing to the app's next.config.ts) does not seem to activate the custom loader nad does not throw any errors. The custom loader function does not execute in Storybook, resulting in broken images as the raw src ID is used instead of the loader-generated URL.
Thanks in advance for any help.
Additional information
Environment:
- Storybook Version: 8.6.12
@storybook/nextjsVersion: 8.6.12- Next.js Version: 15.2.3
- Monorepo Tool: Turborepo (using pnpm workspaces)
Relevant Configuration Snippets:
Next.js Config (apps/<app-name>/next.config.ts):
import type { NextConfig } from 'next'
import { getTranspilePackages } from '@cme/config-next-transpile'
const nextConfig: NextConfig = {
reactStrictMode: true,
transpilePackages: getTranspilePackages(['ui-', 'utils-']),
images: {
loader: 'custom',
loaderFile: './lib/imageLoader.ts',
},
}
export default nextConfig
Custom Loader (apps/<app-name>/lib/imageLoader.ts):
export default function imageLoader({ src, width, quality }: {
src: string
width: number
quality?: number
}) {
console.log('[imageLoader] Called with:', { src, width, quality }); // This log does NOT appear in Storybook
const url = `https://your-image-cdn.com/r${width || ''}x/q${quality || 75}/${src}.webp`;
console.log('[imageLoader] Returning URL:', url);
return url;
}
Storybook Main Config (apps/docs/.storybook/main.ts - Attempt with nextConfigPath):
import path from 'path';
import { StorybookConfig } from '@storybook/nextjs'
const config: StorybookConfig = {
framework: {
name: '@storybook/nextjs',
options: {
nextConfigPath: path.resolve(__dirname, '../../<app-name>/next.config.ts'),
}
},
features: {
viewportStoryGlobals: true,
},
stories: [
"../../<app-name>/**/*.stories.@(js|jsx|ts|tsx)",
"../../../packages-ui/common/src/**/*.stories.@(js|jsx|ts|tsx)",
"../../../packages-ui/forms/src/**/*.stories.@(js|jsx|ts|tsx)"
],
addons: [
"@storybook/addon-essentials",
"@storybook/addon-onboarding",
"@storybook/addon-interactions",
"@storybook/blocks"
],
webpackFinal: async (config) => {
config.resolve = config.resolve || {};
config.resolve.alias = {
...config.resolve.alias,
'@<app-name>': path.resolve(__dirname, '../../<app-name>'),
'@<other-app-1>': path.resolve(__dirname, '../../<other-app-1>'),
'@<other-app-2>': path.resolve(__dirname, '../../<other-app-2>'),
}
config.resolve.extensions = config.resolve.extensions || [];
config.resolve.extensions.push('.ts', '.tsx')
return config
}
}
export default config
Storybook Preview Config (apps/docs/.storybook/preview.ts):
import { Preview } from '@storybook/react'
import { INITIAL_VIEWPORTS } from '@storybook/addon-viewport'
const preview: Preview = {
parameters: {
controls: {
matchers: {
color: /(background|color)$/i,
date: /Date$/i,
},
},
viewport: {
options: INITIAL_VIEWPORTS,
},
}
}
export default preview
Issue Summary:
Using nextConfigPath in main.ts to point to the Next.js app's configuration does not result in the custom image loader being used by next/image in Storybook. The console.log within the loader never fires. The component defaults to using the raw src ID, leading to broken images.
Setting unoptimized: true in preview.ts parameters works as expected (shows broken image with raw ID), confirming the issue is specifically with custom loader execution via the intended configuration.
Question: How can we correctly configure @storybook/nextjs in a Turborepo setup to use a custom next/image loader defined in a specific Next.js application package (apps/<app-name>) by referencing its next.config.ts?
Create a reproduction
No response