diff --git a/package-lock.json b/package-lock.json
index 765a63dd4d5276eb0492b2e51de176250778b604..8412ddd6063ca8e00aebecb2c0d34c5b8216d49c 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -930,7 +930,6 @@
       "version": "4.0.1",
       "resolved": "https://registry.npmjs.org/clone-deep/-/clone-deep-4.0.1.tgz",
       "integrity": "sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ==",
-      "dev": true,
       "requires": {
         "is-plain-object": "^2.0.4",
         "kind-of": "^6.0.2",
@@ -2076,7 +2075,6 @@
       "version": "2.0.4",
       "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz",
       "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==",
-      "dev": true,
       "requires": {
         "isobject": "^3.0.1"
       }
@@ -2113,8 +2111,7 @@
     "isobject": {
       "version": "3.0.1",
       "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz",
-      "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=",
-      "dev": true
+      "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8="
     },
     "isstream": {
       "version": "0.1.2",
@@ -2248,8 +2245,7 @@
     "kind-of": {
       "version": "6.0.3",
       "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz",
-      "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==",
-      "dev": true
+      "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw=="
     },
     "klona": {
       "version": "2.0.4",
@@ -6961,7 +6957,6 @@
       "version": "3.0.1",
       "resolved": "https://registry.npmjs.org/shallow-clone/-/shallow-clone-3.0.1.tgz",
       "integrity": "sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA==",
-      "dev": true,
       "requires": {
         "kind-of": "^6.0.2"
       }
@@ -7737,7 +7732,6 @@
       "version": "5.7.3",
       "resolved": "https://registry.npmjs.org/webpack-merge/-/webpack-merge-5.7.3.tgz",
       "integrity": "sha512-6/JUQv0ELQ1igjGDzHkXbVDRxkfA57Zw7PfiupdLFJYrgFqY5ZP8xxbpp2lU3EPwYx89ht5Z/aDkD40hFCm5AA==",
-      "dev": true,
       "requires": {
         "clone-deep": "^4.0.1",
         "wildcard": "^2.0.0"
@@ -7805,8 +7799,7 @@
     "wildcard": {
       "version": "2.0.0",
       "resolved": "https://registry.npmjs.org/wildcard/-/wildcard-2.0.0.tgz",
-      "integrity": "sha512-JcKqAHLPxcdb9KM49dufGXn2x3ssnfjbcaQdLlfZsL9rH9wgDQjUtDxbo8NE0F6SFvydeu1VhZe7hZuHsB2/pw==",
-      "dev": true
+      "integrity": "sha512-JcKqAHLPxcdb9KM49dufGXn2x3ssnfjbcaQdLlfZsL9rH9wgDQjUtDxbo8NE0F6SFvydeu1VhZe7hZuHsB2/pw=="
     },
     "wrap-ansi": {
       "version": "5.1.0",
diff --git a/package.json b/package.json
index 7e16ac6da04c88b548bc3e6b55603f3adb8c1d2b..e081db0e3d21f7af70c91723e3c4afaa79eabaf2 100644
--- a/package.json
+++ b/package.json
@@ -7,11 +7,10 @@
     "doc": "docs"
   },
   "scripts": {
-    "webpack-test": "./node_modules/.bin/webpack --config webpack.test.config.js",
-    "webpack-dev": "./node_modules/.bin/webpack --config webpack.dev.config.js",
-    "webpack-dev-live": "./node_modules/.bin/webpack --config webpack.dev.config.js --watch",
-    "webpack-stag": "./node_modules/.bin/webpack --config webpack.stag.config.js",
-    "webpack-prod": "./node_modules/.bin/webpack --config webpack.prod.config.js"
+    "webpack-dev": "./node_modules/.bin/webpack --config webpack.dev.js",
+    "webpack-dev-live": "./node_modules/.bin/webpack --config webpack.dev.js --watch",
+    "webpack-stag": "./node_modules/.bin/webpack --config webpack.stag.js",
+    "webpack-prod": "./node_modules/.bin/webpack --config webpack.prod.js"
   },
   "private": true,
   "repository": {
@@ -72,7 +71,8 @@
     "tiptap-extensions": "^1.35.2",
     "vue": "^2.6.12",
     "vue-sanitize": "^0.2.1",
-    "vue-select": "^3.11.2"
+    "vue-select": "^3.11.2",
+    "webpack-merge": "^5.7.3"
   },
   "resolutions": {
     "prosemirror-model": "1.8.2"
diff --git a/webpack.dev.config.js b/webpack.common.js
similarity index 96%
rename from webpack.dev.config.js
rename to webpack.common.js
index bebe8ac21d5734f9ee7b9abb547d4cc1436afcb4..b83774c9ada1e72ec2afc34fb14ead51989c0ac0 100644
--- a/webpack.dev.config.js
+++ b/webpack.common.js
@@ -5,9 +5,7 @@ const { VueLoaderPlugin } = require('vue-loader')
 var path_bundles = __dirname + '/static_bundles/bundles';
 
 module.exports = {
-    mode: 'development',
     context: __dirname,
-    devtool: "source-map", // to ensure no eval() (breaking CSP) in development
     entry: {
         base: [
             "./scipost_django/scipost/static/scipost/assets/js/scripts.js",
diff --git a/webpack.dev.js b/webpack.dev.js
new file mode 100644
index 0000000000000000000000000000000000000000..e0a525fb174899d4b9f14f63d8b72d07ad5ebb24
--- /dev/null
+++ b/webpack.dev.js
@@ -0,0 +1,7 @@
+const { merge } = require('webpack-merge');
+const common = require('./webpack.common.js');
+
+module.exports = merge(common, {
+    mode: 'development',
+    devtool: 'inline-source-map',
+});
diff --git a/webpack.prod.config.js b/webpack.prod.config.js
deleted file mode 100644
index 99f066c2124607edccbfaa03eb02d7312df3e691..0000000000000000000000000000000000000000
--- a/webpack.prod.config.js
+++ /dev/null
@@ -1,113 +0,0 @@
-var webpack = require("webpack");
-var BundleTracker = require('webpack-bundle-tracker');
-const { CleanWebpackPlugin } = require('clean-webpack-plugin');
-const VueLoaderPlugin = require('vue-loader/lib/plugin')
-var path_bundles = __dirname + '/static_bundles/bundles';
-
-module.exports = {
-    mode: 'production',
-    context: __dirname,
-    entry: {
-        main: [
-	    "tether",
-            "bootstrap-loader",
-            "./scipost_django/scipost/static/scipost/assets/js/scripts.js",
-        ],
-        homepage: [
-            "./scipost_django/scipost/static/scipost/assets/js/fader.js",
-            "./scipost_django/scipost/static/scipost/assets/js/newsticker.js",
-        ],
-	apimail: [
-            "./scipost_django/apimail/static/apimail/assets/vue/messages_table.js",
-        ],
-	qr: [
-	    "./scipost_django/scipost/static/scipost/assets/js/activate_qr.js",
-	],
-    },
-    output: {
-        path: path_bundles,
-        publicPath: 'https://scipost.org/static/bundles/',
-        filename: "js/[name]-[hash].js",
-    },
-    module: {
-	rules: [
-	    {
-	    	test: require.resolve('jquery'),
-	    	use: [
-		    {
-	    		loader: 'expose-loader',
-	    		options: 'jQuery'
-	    	    },
-		    {
-	    		loader: 'expose-loader',
-	    		options: '$'
-	    	    }
-		]
-	    },
-            {
-                test: /\.css$/,
-	    	use: [
-		    'vue-style-loader',
-		    'style-loader',
-		    'css-loader',
-		    'postcss-loader'
-		],
-            },
-            {
-                test: /\.scss$/,
-	    	use: [
-		    'vue-style-loader',
-		    'style-loader',
-		    'css-loader',
-		    'postcss-loader',
-		    'sass-loader'
-		],
-            },
-	    {
-		test: /\.vue$/,
-		loader: 'vue-loader'
-	    },
-	]
-    },
-    plugins: [
-	new BundleTracker({
-	    filename: './webpack-stats.json'
-	}),
-        new webpack.ProvidePlugin({
-            $: 'jquery',
-            jQuery: 'jquery',
-	    'window.jQuery': 'jquery',
-            Tether: 'tether',
-            'window.Tether': 'tether',
-            Popper: ['popper.js', 'default'],
-	    Alert: "exports-loader?Alert!bootstrap/js/dist/alert",
-	    Button: "exports-loader?Button!bootstrap/js/dist/button",
-	    Carousel: "exports-loader?Carousel!bootstrap/js/dist/carousel",
-	    Collapse: "exports-loader?Collapse!bootstrap/js/dist/collapse",
-	    Dropdown: "exports-loader?Dropdown!bootstrap/js/dist/dropdown",
-	    Modal: "exports-loader?Modal!bootstrap/js/dist/modal",
-	    Popover: "exports-loader?Popover!bootstrap/js/dist/popover",
-	    Scrollspy: "exports-loader?Scrollspy!bootstrap/js/dist/scrollspy",
-	    Tab: "exports-loader?Tab!bootstrap/js/dist/tab",
-            Tooltip: "exports-loader?Tooltip!bootstrap/js/dist/tooltip",
-            Util: 'exports-loader?Util!bootstrap/js/dist/util',
-        }),
-        new CleanWebpackPlugin(),
-        new webpack.optimize.OccurrenceOrderPlugin(),
-        new webpack.optimize.AggressiveMergingPlugin(),
-	new VueLoaderPlugin()
-    ],
-    resolve: {
-	alias: {
-	    // If using the runtime only build
-	    'vue$': 'vue/dist/vue.runtime.esm.js'
-	    // Or if using full build of Vue (runtime + compiler) # NOT GOOD WITH CSP
-	    // 'vue$': 'vue/dist/vue.esm.js'
-	}
-    },
-    optimization: {
-	splitChunks: {
-	    chunks: 'all',
-	},
-    },
-}
diff --git a/webpack.prod.js b/webpack.prod.js
new file mode 100644
index 0000000000000000000000000000000000000000..855baf721d94d7940459a7463b0f558b70cdf23d
--- /dev/null
+++ b/webpack.prod.js
@@ -0,0 +1,6 @@
+const { merge } = require('webpack-merge');
+const common = require('./webpack.common.js');
+
+module.exports = merge(common, {
+    mode: 'production',
+});
diff --git a/webpack.stag.config.js b/webpack.stag.config.js
deleted file mode 100644
index 62d467db9bb8b7c373c6dd71951beed2ad42cf3c..0000000000000000000000000000000000000000
--- a/webpack.stag.config.js
+++ /dev/null
@@ -1,114 +0,0 @@
-var webpack = require("webpack");
-var BundleTracker = require('webpack-bundle-tracker');
-const { CleanWebpackPlugin } = require('clean-webpack-plugin');
-const VueLoaderPlugin = require('vue-loader/lib/plugin')
-var path_bundles = __dirname + '/static_bundles/bundles';
-
-module.exports = {
-    mode: 'production',
-    context: __dirname,
-    // devtool: "source-map", // to ensure no eval() (breaking CSP) in development
-    entry: {
-        main: [
-	    "tether",
-            "bootstrap-loader",
-            "./scipost_django/scipost/static/scipost/assets/js/scripts.js",
-        ],
-        homepage: [
-            "./scipost_django/scipost/static/scipost/assets/js/fader.js",
-            "./scipost_django/scipost/static/scipost/assets/js/newsticker.js",
-	],
-	apimail: [
-            "./scipost_django/apimail/static/apimail/assets/vue/messages_table.js",
-        ],
-	qr: [
-	    "./scipost_django/scipost/static/scipost/assets/js/activate_qr.js",
-	],
-    },
-    output: {
-        path: path_bundles,
-        publicPath: '/static/bundles/',
-        filename: "js/[name]-[hash].js",
-    },
-    module: {
-	rules: [
-	    {
-	    	test: require.resolve('jquery'),
-	    	use: [
-		    {
-	    		loader: 'expose-loader',
-	    		options: 'jQuery'
-	    	    },
-		    {
-	    		loader: 'expose-loader',
-	    		options: '$'
-	    	    }
-		]
-	    },
-            {
-                test: /\.css$/,
-	    	use: [
-		    'vue-style-loader',
-		    'style-loader',
-		    'css-loader',
-		    'postcss-loader'
-		],
-            },
-            {
-                test: /\.scss$/,
-	    	use: [
-		    'vue-style-loader',
-		    'style-loader',
-		    'css-loader',
-		    'postcss-loader',
-		    'sass-loader'
-		],
-            },
-	    {
-		test: /\.vue$/,
-		loader: 'vue-loader'
-	    },
-	]
-    },
-    plugins: [
-	new BundleTracker({
-	    filename: './webpack-stats.json'
-	}),
-        new webpack.ProvidePlugin({
-            $: 'jquery',
-            jQuery: 'jquery',
-	    'window.jQuery': 'jquery',
-            Tether: 'tether',
-            'window.Tether': 'tether',
-            Popper: ['popper.js', 'default'],
-	    Alert: "exports-loader?Alert!bootstrap/js/dist/alert",
-	    Button: "exports-loader?Button!bootstrap/js/dist/button",
-	    Carousel: "exports-loader?Carousel!bootstrap/js/dist/carousel",
-	    Collapse: "exports-loader?Collapse!bootstrap/js/dist/collapse",
-	    Dropdown: "exports-loader?Dropdown!bootstrap/js/dist/dropdown",
-	    Modal: "exports-loader?Modal!bootstrap/js/dist/modal",
-	    Popover: "exports-loader?Popover!bootstrap/js/dist/popover",
-	    Scrollspy: "exports-loader?Scrollspy!bootstrap/js/dist/scrollspy",
-	    Tab: "exports-loader?Tab!bootstrap/js/dist/tab",
-            Tooltip: "exports-loader?Tooltip!bootstrap/js/dist/tooltip",
-            Util: 'exports-loader?Util!bootstrap/js/dist/util',
-        }),
-        new CleanWebpackPlugin(),
-        new webpack.optimize.OccurrenceOrderPlugin(),
-        new webpack.optimize.AggressiveMergingPlugin(),
-	new VueLoaderPlugin()
-    ],
-    resolve: {
-	alias: {
-	    // If using the runtime only build
-	    'vue$': 'vue/dist/vue.runtime.esm.js'
-	    // Or if using full build of Vue (runtime + compiler) # NOT GOOD WITH CSP
-	    // 'vue$': 'vue/dist/vue.esm.js'
-	}
-    },
-    optimization: {
-	splitChunks: {
-	    chunks: 'all',
-	},
-    },
-}
diff --git a/webpack.stag.js b/webpack.stag.js
new file mode 100644
index 0000000000000000000000000000000000000000..855baf721d94d7940459a7463b0f558b70cdf23d
--- /dev/null
+++ b/webpack.stag.js
@@ -0,0 +1,6 @@
+const { merge } = require('webpack-merge');
+const common = require('./webpack.common.js');
+
+module.exports = merge(common, {
+    mode: 'production',
+});