Customizing Project Logos and Login Backgrounds with a Webpack Merge Loader
The article explains how to replace manual client‑specific logo and login‑background swaps with a custom Webpack merge‑less loader that injects client assets during build, offering low intrusiveness, strong extensibility, richer styling options, and a smoother developer experience compared to simple copy scripts.
This article describes a practical approach to customize branding assets (logo and login background) for different customers in a Webpack‑based front‑end project.
Background : When delivering a project to multiple clients, each client may require its own logo and background image. Manually replacing files before each build is error‑prone and invasive, and using Git branches is not suitable for this scenario.
First solution – Direct replacement : A simple script copies client‑specific images into the static folder before running the normal build command.
const path = require('path');
const fs = require('fs');
const project = process.argv[2];
const distPath = path.resolve('./src/static/images'); // source directory
const resourcePath = path.resolve('./resources', project); // client resources
function copyDir(src, dist) {
try { fs.accessSync(dist, fs.constants.R_OK | fs.constants.W_OK); }
catch (err) { fs.mkdirSync(dist); }
const copyFile = (src, dist) => { fs.createReadStream(src).pipe(fs.createWriteStream(dist)); };
const dirList = fs.readdirSync(src);
dirList.forEach(item => {
const currentPath = path.resolve(src, item);
const currentDistPath = path.resolve(dist, item);
if (fs.statSync(currentPath).isDirectory()) {
copyDir(currentPath, currentDistPath);
} else {
copyFile(currentPath, currentDistPath);
}
});
}
copyDir(resourcePath, distPath);Running the script:
node ./pre-packaging.js projectnameDrawbacks of this method include high intrusiveness, poor extensibility, limited functionality (only image replacement), and a fragmented build experience.
Second solution – CSS merging via a custom Webpack loader : Instead of replacing files, the approach injects a custom CSS/LESS file that references the client‑specific assets. By placing the loader before less-loader , the custom styles are merged into the compilation pipeline without touching the original source code.
Key advantages:
Low intrusiveness – resources live alongside the project but are not part of the core source.
Strong extensibility – any image format can be used.
Rich functionality – additional custom styles can be added beyond logo and background.
Better developer experience – all changes happen during the build phase.
A minimal merge-less loader implementation:
module.exports = function(source) {
const options = this.getOptions();
const { style } = options;
const string = `\n@import ${style};\n`;
return source + string;
};Further optimization adds validation and handling of absolute paths using loader-utils and schema-utils :
const fs = require('fs');
const path = require('path');
const loaderUtils = require('loader-utils');
const validateOptions = require('schema-utils');
const schema = {
type: 'object',
properties: {
style: { type: 'string' },
target: { type: 'string' }
},
required: ['style', 'target']
};
module.exports = function(source, meta) {
const options = loaderUtils.getOptions(this);
validateOptions(schema, options, 'Loader options');
let { style, target } = options;
style = loaderUtils.stringifyRequest(this, style);
if (meta) {
const { file, sourceRoot } = meta;
if (target === path.join(sourceRoot, file)) {
const string = `\n@import ${style};\n`;
source += string;
}
}
return source;
};The article also shows a typical Webpack configuration where the custom loader is inserted before less-loader :
module.exports = {
entry: [...],
output: {...},
module: {
rules: [
// other rules
{
test: /\.less$/,
use: [
'style-loader',
'css-loader',
'less-loader',
{
loader: path.resolve(__dirname, './loader/merge-less.js'),
options: {
style: path.resolve(root, 'client/statics/projects/it/style.less'),
target: path.resolve(root, 'src/pages/login.vue')
}
}
]
}
]
}
};Finally, the author summarizes the benefits of using a Webpack loader for client‑specific branding: low intrusiveness, strong extensibility, rich functionality, and a smooth build‑time experience.
Didi Tech
Official Didi technology account
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.