How to convert HTML elements to image with javascript?
I want to be able to convert HTML elements to images and download them. I tried using html2canvas
but I have an error caused by the oklch
color function that I'm using
Error: Attempting to parse an unsupported color function "oklch"
Is there any fix to this or any library alternative? I don't want to change the color library as it is the default from Tailwind
Solution
You can use html2canvas-pro, it has the same api as html2canvas
but it supports all the modern color libraries like color
, lab
, lch
, oklab
, oklch
Here is an example of how to download a div as an image with it
const downloadElement = () => {
const element = document.getElementById("element-id");
html2canvas(element, {backgroundColor: null}).then(canvas => {
var myImage = canvas.toDataURL();
downloadURI(myImage, );
const link = document.createElement("a");
link.download = "image-filename.png";
link.href = uri;
document.body.appendChild(link);
link.click();
URL.revokeObjectURL(link.href);
});
}
Alternative #1
I've been using dom-to-image as an alternative to html2canvas for years, and it handles modern CSS color functions much better. It's particularly good with Tailwind's color system.
import domtoimage from 'dom-to-image';
const downloadElement = () => {
const element = document.getElementById("element-id");
domtoimage.toPng(element)
.then(function (dataUrl) {
const link = document.createElement('a');
link.download = 'element.png';
link.href = dataUrl;
link.click();
})
.catch(function (error) {
console.error('Error generating image:', error);
});
};
The library also supports different output formats:
domtoimage.toJpeg()
for JPEGdomtoimage.toSvg()
for SVGdomtoimage.toBlob()
for Blob objects
This approach has worked reliably for me across different browsers and CSS frameworks.
Alternative #2
If you want to stick with the standard html2canvas
library, you can preprocess your CSS to convert modern color functions to RGB values before rendering. This is a bit of a workaround, but it works well for static content.
const convertModernColors = (element) => {
const computedStyle = window.getComputedStyle(element);
const elements = element.querySelectorAll('*');
elements.forEach(el => {
const style = window.getComputedStyle(el);
const backgroundColor = style.backgroundColor;
const color = style.color;
// Convert oklch to rgb if needed
if (backgroundColor.includes('oklch')) {
// Use a color conversion library or CSS custom properties
el.style.backgroundColor = getComputedStyle(document.documentElement)
.getPropertyValue('--fallback-color') || '#000000';
}
});
};
const downloadElement = () => {
const element = document.getElementById("element-id");
convertModernColors(element);
html2canvas(element).then(canvas => {
const link = document.createElement("a");
link.download = "image.png";
link.href = canvas.toDataURL();
link.click();
});
};
This approach requires some setup but allows you to keep using the standard library.
Alternative #3
For a more robust solution, consider using Puppeteer (server-side) or Playwright to render the HTML and capture screenshots. This approach handles all modern CSS features perfectly.
// Server-side with Puppeteer
const puppeteer = require('puppeteer');
const generateImage = async (htmlContent) => {
const browser = await puppeteer.launch();
const page = await browser.newPage();
await page.setContent(htmlContent);
await page.waitForSelector('#element-id');
const element = await page.$('#element-id');
await element.screenshot({
path: 'element.png',
type: 'png'
});
await browser.close();
};
For client-side, you can use Playwright's browser binaries:
import { chromium } from 'playwright';
const generateImage = async (htmlContent) => {
const browser = await chromium.launch();
const page = await browser.newPage();
await page.setContent(htmlContent);
const element = await page.locator('#element-id');
await element.screenshot({ path: 'element.png' });
await browser.close();
};
This approach is more resource-intensive but handles all CSS features correctly.