Skip to content

Instantly share code, notes, and snippets.

@davidteather
Created August 24, 2025 21:53
Show Gist options
  • Select an option

  • Save davidteather/91c0e02120897d4b2b8376a351420474 to your computer and use it in GitHub Desktop.

Select an option

Save davidteather/91c0e02120897d4b2b8376a351420474 to your computer and use it in GitHub Desktop.
rehype-inline-svg
function rehypeInlineSvg() {
return (tree) => {
visit(tree, 'element', (node) => {
if (node.tagName === 'code' && node.properties.className && node.properties.className.length > 0 && node.properties.className[0] === 'language-mermaid') {
// We need to look up any elements in the value that are like <img src='/icons/worm.svg' width='25' height='25' />
// and then directly replace the img with the svg content inline
var mermaidCode = node.children[0].value;
// regex to find all the img tags
const imgRegex = /<img[^>]*\/>/g;
const imgMatches = mermaidCode.match(imgRegex);
if (imgMatches) {
for (const imgMatch of imgMatches) {
const imgNode = unified().use(rehypeParse).parse(imgMatch);
const imgElement = imgNode.children[0].children[1].children[0];
const src = imgElement.properties.src;
// Check if the file is an SVG if so inline time
if (src.endsWith('.svg')) {
let svgPath;
if (src.startsWith('/')) {
const publicFolder = process.env.PUBLIC_FOLDER_PATH || 'public';
svgPath = resolve(publicFolder, `.${src}`);
} else {
svgPath = join(process.cwd(), src);
}
try {
var svgContent = readFileSync(svgPath, 'utf-8');
// cut off anything before the first <svg tag
svgContent = svgContent.substring(svgContent.indexOf('<svg'));
const inlineSvgNode = unified()
.use(rehypeParse, { fragment: true })
.parse(svgContent);
const svgElement = inlineSvgNode.children[0];
// Modify each <path> element inside the <svg>
// You might want to modify this to be conditional
visit(svgElement, 'element', (svgNode) => {
if (svgNode.tagName === 'path') {
svgNode.properties.style = svgNode.properties.style || '';
var fill = svgNode.properties.fill;
svgNode.properties.style += `fill: ${fill || 'inherit'} !important;`;
svgNode.properties.style += `stroke-width: 0 !important;`;
delete svgNode.properties.fill;
}
});
// Copy relevant properties from the <img> to the <svg> tag
svgElement.properties = svgElement.properties || {};
if (imgElement.properties.width) {
svgElement.properties.width = imgElement.properties.width;
}
if (imgElement.properties.height) {
svgElement.properties.height = imgElement.properties.height;
}
if (imgElement.properties.style) {
svgElement.properties.style = imgElement.properties.style;
}
if (imgElement.properties.className) {
svgElement.properties.className = imgElement.properties.className;
}
const inlineSvgHtml = unified().use(rehypeStringify).stringify(inlineSvgNode).replaceAll("\n", "").replaceAll("\t", "");
// Replace with the inline SVG code
mermaidCode = mermaidCode.replace(imgMatch, inlineSvgHtml);
} catch (error) {
console.error(`Failed to inline SVG at ${svgPath}:`, error);
}
}
}
node.children[0].value = mermaidCode;
}
}
});
};
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment