/** @type {import('next').NextConfig} */
const path = require('path');
const nextConfig = {
// Transpile framer-motion
transpilePackages: ['framer-motion', 'motion-dom', 'motion-utils'],
// Enable source maps in production for better debugging
productionBrowserSourceMaps: true,
// Additional security headers
headers: async () => {
return [
{
source: '/(.*)',
headers: [
{
key: 'X-Content-Type-Options',
value: 'nosniff',
},
{
key: 'X-XSS-Protection',
value: '1; mode=block',
},
// Add HSTS header with preload
{
key: 'Strict-Transport-Security',
value: 'max-age=31536000; includeSubDomains; preload',
},
// Add header to help with bfcache
{
key: 'Cache-Control',
value: 'public, max-age=3600, s-maxage=86400, stale-while-revalidate=31536000',
},
// Enable back/forward cache
{
key: 'Service-Worker-Allowed',
value: '/',
},
],
},
// Improved cache control for static assets with longer TTL
{
source: '/(.*)\\.(jpg|jpeg|gif|png|svg|webp|avif|woff|woff2)$',
headers: [
{
key: 'Cache-Control',
value: 'public, max-age=31536000, immutable',
},
// Add content type for WebP images to ensure proper handling
{
key: 'Vary',
value: 'Accept',
},
],
},
// Add cache control for JS and CSS with appropriate max-age
{
source: '/(.*)\\.(js|css|map)$',
headers: [
{
key: 'Cache-Control',
value: 'public, max-age=31536000, immutable',
},
// Add content encoding header for compression
{
key: 'Content-Encoding',
value: 'br, gzip',
},
// Ensure proper MIME type for source maps
{
key: 'Content-Type',
value: 'application/json',
},
],
},
// Add cache control for HTML and data requests
{
source: '/$',
headers: [
{
key: 'Cache-Control',
value: 'public, max-age=3600, stale-while-revalidate=86400',
},
],
},
];
},
// Ensure output is static where possible
output: 'standalone',
// Add optimizeFonts for better font performance
optimizeFonts: true,
// Improved image optimization settings
images: {
formats: ['image/webp', 'image/avif'],
deviceSizes: [640, 750, 828, 1080, 1200, 1920, 2048],
imageSizes: [16, 32, 48, 64, 96, 128, 256, 384],
minimumCacheTTL: 60 * 60 * 24 * 30, // 30 days
dangerouslyAllowSVG: true,
contentDispositionType: 'attachment',
contentSecurityPolicy: "default-src 'self'; script-src 'none'; sandbox;",
// Limit image sizes to reduce payload
remotePatterns: [],
domains: [],
},
// Enable compression middleware - ensure this is enabled
compress: true,
// Performance optimizations with stable experimental features
experimental: {
// Enable React Server Components optimizations
serverActions: {
bodySizeLimit: '2mb',
},
// External packages that should be compiled
serverComponentsExternalPackages: ['@supabase/supabase-js'],
// Enable optimizeCss to remove unused CSS
optimizeCss: true,
// Improved memory usage
memoryBasedWorkersCount: true,
// Reduce unused JavaScript via partial imports
optimizePackageImports: ['react-icons', 'framer-motion', 'lucide-react'],
},
// Webpack configuration for performance optimization
webpack: (config, { dev, isServer }) => {
// Path aliases already in tsconfig.json should be respected by Next.js
// but let's add them here explicitly as well
config.resolve.alias = {
...config.resolve.alias,
'@': path.resolve(__dirname),
};
// Only run in production build on client
if (!dev && !isServer) {
// Split chunks more aggressively for better caching
config.optimization.splitChunks = {
chunks: 'all',
maxInitialRequests: 25,
minSize: 20000,
maxSize: 244000, // Limit chunk size to reduce main thread parse time
cacheGroups: {
default: false,
vendors: false,
framework: {
name: 'framework',
test: /[\\/]node_modules[\\/](react|react-dom|scheduler|next)[\\/]/,
priority: 40,
enforce: true,
},
// Create a separate chunk for large libraries
framerMotion: {
test: /[\\/]node_modules[\\/](framer-motion)[\\/]/,
name: 'framer-motion',
priority: 35,
reuseExistingChunk: true,
},
// Group all react icons separately
reactIcons: {
test: /[\\/]node_modules[\\/](react-icons)[\\/]/,
name: 'react-icons',
priority: 35,
reuseExistingChunk: true,
},
lib: {
test: /[\\/]node_modules[\\/]/,
name(module) {
const match = module.context ? module.context.match(
/[\\/]node_modules[\\/](.*?)([\\/]|$)/
) : null;
const packageName = match ? match[1] : 'unknown';
return `npm.${packageName.replace('@', '')}`;
},
priority: 30,
minChunks: 1,
reuseExistingChunk: true,
},
commons: {
name: 'commons',
minChunks: 2,
priority: 20,
},
},
};
// Enable terser minification with better options
if (config.optimization.minimizer) {
config.optimization.minimizer.forEach(minimizer => {
if (minimizer.constructor.name === 'TerserPlugin') {
minimizer.options.terserOptions = {
...minimizer.options.terserOptions,
compress: {
...minimizer.options.terserOptions.compress,
drop_console: true, // Remove console logs
pure_funcs: ['console.log', 'console.info', 'console.debug'],
passes: 3, // More aggressive optimization
ecma: 2020, // Use modern optimizations
toplevel: true, // Optimize top-level functions
unsafe_math: true, // Enable faster math optimizations
unsafe_methods: true, // Enable unsafe method optimizations
keep_infinity: true, // Preserve Infinity literals
module: true, // Enable module compression options
reduce_funcs: true, // Inline single-use functions when possible
},
mangle: {
...minimizer.options.terserOptions.mangle,
safari10: true,
toplevel: true, // Mangle top-level functions
module: true, // Enable module mangling
},
format: {
comments: false, // Remove all comments
ecma: 2020, // Use modern formatting
},
module: true, // Enable module processing
toplevel: true, // Enable top-level optimizations
keep_classnames: false, // Allow class name mangling
keep_fnames: false, // Allow function name mangling
};
}
// Add CSSO Plugin for CSS minification if available
if (minimizer.constructor.name === 'CssMinimizerPlugin') {
minimizer.options.minimizerOptions = {
preset: ['advanced', {
discardComments: { removeAll: true },
mergeIdents: true,
reduceIdents: true,
zindex: false,
}],
};
}
});
}
// Disable source maps in production
config.devtool = false;
// Add image optimization - ensure it's available
try {
// Use responsive-loader if available
const { resolve } = require('path');
const responsiveLoader = resolve(process.cwd(), 'node_modules/responsive-loader');
if (require('fs').existsSync(responsiveLoader)) {
config.module.rules.push({
test: /\.(jpe?g|png|gif)$/i,
use: [
{
loader: 'responsive-loader',
options: {
adapter: require('responsive-loader/sharp'),
quality: 80,
placeholder: true,
placeholderSize: 40,
format: 'webp',
progressive: true,
},
},
],
});
console.log('✅ Responsive image loader enabled');
}
} catch (e) {
console.warn('⚠️ Could not enable responsive-loader', e.message);
}
}
return config;
},
// Reduce power usage on browsers
poweredByHeader: false,
// Configure Next.js to automatically set gzip/brotli compression
async rewrites() {
return [];
},
// Configure React optimization options
reactStrictMode: true,
};
module.exports = nextConfig;