It is possible to have custom markers in Leaflet. Those markers will be dynamically created by your Angular/Typescript or javascript code.
The marker is created using a canvas object. All the images are drawn in the canvas. That canvas is turned into base64. Why base64? Because it is good to cache the created markers, so you can reuse them. A nice way to cache a marker is turning it into a normal string using base64. That string can be used when creating your Leaflet L.Icon() object. Just put it in the iconUrl attribute. Shadows on your markers also look professional. You can easily configure them when creating a marker with L.icon().
let sources = []; // will contain all properties about the images // load a .png from the webserver. Position and width and height is supplied. sources.push({ src: '/assets/img/icon.png', x: 0, y: 0, dx: 20, dy: 20 }); // or load a svg let svgStr = '<svg> .... </svg>'; let svgDataStr = 'data:image/svg+xml;charset=utf-8,' + encodeURIComponentsvgStr sources.push({ src: svgDataStr, x: 0, y: 0, dx: 20, dy: 20 }); const images = sources.map(source => new Promise((resolve, reject) => { const img = new Image(); img.onerror = () => { reject(new Error('Couldn\'t load image')); }; img.onload = () => { source["image"] = img; resolve(source); }; img.src = source.src; })); let mergedData; await Promise.all(images).then(images => { let canvas = document.createElement('canvas'); canvas.width = 40; canvas.height = 40; const context = canvas.getContext('2d'); images.forEach(image => { context.globalAlpha = 0.65; // a bit transparent context.drawImage(image["image"], (image["x"] || 0) + offsetX, (image["y"] || 0) + offsetY, image["dx"], image["dy"]); }); mergedData = canvas.toDataURL('image/png', 0.92); }); const icon = L.icon({ iconUrl: mergedData, shadowUrl: ... }); leafletMarker.setIcon(icon);