How to combine several plugins inside next.config.js file?

Viewed 2105

When I add "next-videos" loader to the next.config.js it works perfectly as long as module.exports goes the last one:

module.exports = withVideos()

On the other hand, it breaks another instance of module.exports that located above:

module.exports = {
   images: {
     domains: ['cdn.shopify.com'],
   },
};

Basically, it overwrites every previous module.exports. What are the rules for combining these exports? I guess I need to put them under one module but what are the rules to do it? I'm messing up with syntax and every try to relocate it under one module.exports ends with new errors

Just to sum up the questions:

  1. What are the rules of combining modules inside single export and how I can combine it with all my previous module.exports?

  2. Do I really need to leave "webpack(config)" part that duplicates the identical part above inside next.config? I gathered it from different sources and now trying to figure out how does it really work

webpack(config) { 
  config.module.rules.push(

Contents of the next.config.js:

const withPlugins = require('next-compose-plugins');
const withSass = require('@zeit/next-sass');
const withCSS = require('@zeit/next-css');
const withImages = require('next-images');
const withVideos = require('next-videos');
const withBundleAnalyzer = require('@next/bundle-analyzer')({
    enabled: process.env.ANALYZE === 'true',
});

// next.config.js
module.exports = withPlugins(
    [[withImages], [withSass], [withCSS], [withVideos]],
    {
        plugins: ['postcss-import', 'tailwindcss', 'autoprefixer'],
        serverRuntimeConfig: {
            mySecret: 'secret',
            secondSecret: process.env.SECOND_SECRET, // Pass through env variables
        },
        publicRuntimeConfig: {
            // Will be available on both server and client
            staticFolder: '/public',
        },
        module: {
            loaders: [
                {
                    test: /.jsx?$/,
                    loader: 'babel-loader',
                    exclude: /node_modules/,
                },
                {
                    test: /\.css$/,
                    loader: 'style-loader!css-loader',
                },
                {
                    test: /\.jsx?$/,
                    use: ['babel-loader', 'astroturf/loader'],
                },
                {
                    test: /\.(jpe?g|png|gif|woff|woff2|eot|ttf|svg)(\?[a-z0-9=.]+)?$/,
                    loader: 'url-loader?limit=100000',
                },
                {
                    test: /\.(eot|woff|woff2|otf|ttf|svg|png|jpg|gif|webm)$/,
                    use: {
                        loader: 'url-loader',
                        options: {
                            limit: 100000,
                            name: '[name].[ext]',
                        },
                    },
                },
                {
                    test: /\.mp4$/,
                    use: 'file-loader?name=videos/[name].[ext]',
                },
                {
                    test: /\.style.js$/,
                    use: [
                        'style-loader',
                        {
                            loader: 'css-loader',
                            options: { importLoaders: 1 },
                        },
                        {
                            loader: 'postcss-loader',
                            options: { parser: 'sugarss', exec: true },
                        },
                    ],
                },
            ],
        },
        webpack(config) {
            config.module.rules.push(
                {
                    test: /\.(eot|woff|woff2|otf|ttf|svg|png|jpg|gif)$/,
                    use: {
                        loader: 'url-loader',
                        options: {
                            limit: 100000,
                            name: '[name].[ext]',
                        },
                    },
                },
                {
                    test: /\.style.js$/,
                    use: [
                        'style-loader',
                        'css-loader',
                        {
                            loader: 'css-loader',
                            options: { importLoaders: 1 },
                        },
                        {
                            loader: 'postcss-loader',
                            options: { parser: 'sugarss', exec: true },
                        },
                    ],
                },
                {
                    test: /\.(js|jsx)$/,
                    exclude: /node_modules/,
                    use: ['babel-loader', 'eslint-loader'],
                },
            );
            withBundleAnalyzer({});
            return config;
        },
    },
    {
        images: {
            domains: ['cdn.shopify.com'],
        },
    },
    withVideos(),
);

module.exports = {
    extends: 'airbnb-base',
    rules: {
        'arrow-body-style': 0,
    },
};

module.exports = {
    images: {
        domains: ['cdn.shopify.com'],
    },
};

module.exports = withVideos();
2 Answers

You seem to be mixing several configs incorrectly into your Next.js config file.

With next-compose-plugins

To begin with your next.config.js should only have a single module.exports, and since you're using next-compose-plugins, it should roughly follow this structure:

// next.config.js

// Omitting requires for simplicity
 
module.exports = withPlugins(
    [withImages, withSass, withCSS, withVideos, withBundleAnalyzer], // All next plugins go here
    // Below is the main Next.js config object
    { 
        images: {
            domains: ['cdn.shopify.com']
        },
        serverRuntimeConfig: {
            mySecret: "secret",
            secondSecret: process.env.SECOND_SECRET
        },
        publicRuntimeConfig: {
          staticFolder: "/public"
        },
        // From here everything that's Webpack-related
        webpack(config) {
            // Add your custom Webpack configs
            return config;
        }
    }
);

Without next-compose-plugins

The same can be achieved without using next-compose-plugins, by chaining the plugins instead.

// next.config.js

// Omitting requires for simplicity
 
module.exports = withImages(
    withSass(
        withCSS(
            withVideos(
                withBundleAnalyzer({
                    images: {
                        domains: ['cdn.shopify.com']
                    },
                    serverRuntimeConfig: {
                        mySecret: "secret",
                        secondSecret: process.env.SECOND_SECRET
                    },
                    publicRuntimeConfig: {
                        staticFolder: "/public"
                    },
                    // From here everything that's Webpack-related
                    webpack(config) {
                        // Add your custom Webpack configs
                        return config;
                    }
                })
            )
        )
    )
);

Finally, the following config belongs to your ESlint config file, and not Next.js config.

// .eslintrc.js
module.exports = {
    extends: 'airbnb-base',
    rules: {
        'arrow-body-style': 0,
    },
};

I faced a similar issue when I was trying to access global css fils from node packages into a Next JS Page, after many trials I solved by changing the following:

When there is need of using multiple plugins or a normal JSON config with a plugin, you need to wrap the original config within plugin.

In your case,

module.exports = withVideos(
images: {
     domains: ['cdn.shopify.com'],
   },
)
Related