[{"data":1,"prerenderedAt":38},["ShallowReactive",2],{"$fBlDRZRqyZMe8xuAqp-ti19wF-gs4XcgzxsXLH_7Oo5Y":3},{"slug":4,"title":5,"description":6,"date":7,"tags":8,"tools":13,"draft":15,"html":16,"toc":17,"minutes":37},"heic-to-jpg-in-the-browser","Converting iPhone HEIC Photos to JPG Inside the Browser","Why HEIC photos get stuck on Windows and upload forms, and how ToolOnTap turns them into JPGs on your device without loading 1.3 MB of decoder on every page.","2026-10-10",[9,10,11,12],"heic","image formats","performance","nuxt",[14],"heic-to-jpg",false,"\u003Cp>Someone sends you twenty photos from their iPhone. You copy them to a Windows laptop, and every file ends in \u003Ccode>.heic\u003C\u002Fcode>. The photo viewer asks you to install a codec, and the visa portal you need them for says “JPG or PNG only”. I built ToolOnTap’s \u003Ca href=\"\u002Fheic-to-jpg\">HEIC to JPG converter\u003C\u002Fa> for exactly this, and the interesting part was making the decoder run in the browser without slowing down the rest of the site.\u003C\u002Fp>\n\u003Ch2 id=\"why-heic-files-get-stuck\">Why HEIC files get stuck\u003C\u002Fh2>\n\u003Cp>iPhones save photos as HEIC by default. HEIC is the HEIF container (ISO\u002FIEC 23008-12) with the picture compressed as HEVC, the same codec used for a lot of modern video. It gives smaller files than JPG at the same visible quality, which is why Apple chose it.\u003C\u002Fp>\n\u003Cp>The problem is support. Windows needs an extra codec to show HEIC, older Android phones and many photo editors can’t open it, and most upload forms only list JPG and PNG. Among the browsers, only Safari decodes HEIC natively. Chrome, Edge and Firefox can’t draw a HEIC file, so an \u003Ccode>&lt;img&gt;\u003C\u002Fcode> tag or a canvas can’t help.\u003C\u002Fp>\n\u003Cp>This affects the rest of the site too. If you drop a HEIC photo on the compressor or the resizer, those tools would fail halfway through decoding. So the shared file check catches HEIC first and sends you to the converter:\u003C\u002Fp>\n\u003Cpre class=\"shiki shiki-themes github-light github-dark\" style=\"--shiki-light:#24292e;--shiki-dark:#e1e4e8;--shiki-light-bg:#fff;--shiki-dark-bg:#24292e\" tabindex=\"0\">\u003Ccode>\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#57606a;--shiki-dark:#8b949e\">\u002F\u002F Only Safari can decode HEIC natively, so send these to the converter instead of failing later\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#cb2431;--shiki-dark:#F97583\">if\u003C\u002Fspan>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#E1E4E8\"> (\u003C\u002Fspan>\u003Cspan style=\"--shiki-light:#6F42C1;--shiki-dark:#B392F0\">isHeic\u003C\u002Fspan>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#E1E4E8\">(file) \u003C\u002Fspan>\u003Cspan style=\"--shiki-light:#cb2431;--shiki-dark:#F97583\">&#x26;&#x26;\u003C\u002Fspan>\u003Cspan style=\"--shiki-light:#cb2431;--shiki-dark:#F97583\"> !\u003C\u002Fspan>\u003Cspan style=\"--shiki-light:#6F42C1;--shiki-dark:#B392F0\">accepts\u003C\u002Fspan>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#E1E4E8\">(accept).\u003C\u002Fspan>\u003Cspan style=\"--shiki-light:#6F42C1;--shiki-dark:#B392F0\">some\u003C\u002Fspan>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#E1E4E8\">(\u003C\u002Fspan>\u003Cspan style=\"--shiki-light:#b35309;--shiki-dark:#FFAB70\">t\u003C\u002Fspan>\u003Cspan style=\"--shiki-light:#cb2431;--shiki-dark:#F97583\"> =>\u003C\u002Fspan>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#E1E4E8\"> t \u003C\u002Fspan>\u003Cspan style=\"--shiki-light:#cb2431;--shiki-dark:#F97583\">===\u003C\u002Fspan>\u003Cspan style=\"--shiki-light:#032F62;--shiki-dark:#9ECBFF\"> '.heic'\u003C\u002Fspan>\u003Cspan style=\"--shiki-light:#cb2431;--shiki-dark:#F97583\"> ||\u003C\u002Fspan>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#E1E4E8\"> t.\u003C\u002Fspan>\u003Cspan style=\"--shiki-light:#6F42C1;--shiki-dark:#B392F0\">startsWith\u003C\u002Fspan>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#E1E4E8\">(\u003C\u002Fspan>\u003Cspan style=\"--shiki-light:#032F62;--shiki-dark:#9ECBFF\">'image\u002Fhei'\u003C\u002Fspan>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#E1E4E8\">))) {\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#cb2431;--shiki-dark:#F97583\">  return\u003C\u002Fspan>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#E1E4E8\"> {\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#E1E4E8\">    message: \u003C\u002Fspan>\u003Cspan style=\"--shiki-light:#032F62;--shiki-dark:#9ECBFF\">`${\u003C\u002Fspan>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#E1E4E8\">file\u003C\u002Fspan>\u003Cspan style=\"--shiki-light:#032F62;--shiki-dark:#9ECBFF\">.\u003C\u002Fspan>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#E1E4E8\">name\u003C\u002Fspan>\u003Cspan style=\"--shiki-light:#032F62;--shiki-dark:#9ECBFF\">} is a HEIC photo, which most browsers can’t open directly.`\u003C\u002Fspan>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#E1E4E8\">,\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#E1E4E8\">    link: { to: \u003C\u002Fspan>\u003Cspan style=\"--shiki-light:#032F62;--shiki-dark:#9ECBFF\">'\u002Fheic-to-jpg'\u003C\u002Fspan>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#E1E4E8\">, label: \u003C\u002Fspan>\u003Cspan style=\"--shiki-light:#032F62;--shiki-dark:#9ECBFF\">'Convert it to JPG first'\u003C\u002Fspan>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#E1E4E8\"> },\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#E1E4E8\">  }\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#E1E4E8\">}\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>\u003Ccode>isHeic\u003C\u002Fcode> looks at both the MIME type and the file name, in case the browser gives the file an empty or generic type.\u003C\u002Fp>\n\u003Ch2 id=\"decoding-heic-without-the-browser-s-help\">Decoding HEIC without the browser’s help\u003C\u002Fh2>\n\u003Cp>Since the browser can’t decode the format, the decoder has to ship with the page. The converter uses heic2any, an open-source library that bundles libheif, the reference HEIF library, compiled to JavaScript. The whole conversion in my code is a few lines:\u003C\u002Fp>\n\u003Cpre class=\"shiki shiki-themes github-light github-dark\" style=\"--shiki-light:#24292e;--shiki-dark:#e1e4e8;--shiki-light-bg:#fff;--shiki-dark-bg:#24292e\" tabindex=\"0\">\u003Ccode>\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#cb2431;--shiki-dark:#F97583\">export\u003C\u002Fspan>\u003Cspan style=\"--shiki-light:#cb2431;--shiki-dark:#F97583\"> function\u003C\u002Fspan>\u003Cspan style=\"--shiki-light:#6F42C1;--shiki-dark:#B392F0\"> useHeic\u003C\u002Fspan>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#E1E4E8\">() {\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#cb2431;--shiki-dark:#F97583\">  async\u003C\u002Fspan>\u003Cspan style=\"--shiki-light:#cb2431;--shiki-dark:#F97583\"> function\u003C\u002Fspan>\u003Cspan style=\"--shiki-light:#6F42C1;--shiki-dark:#B392F0\"> heicToJpg\u003C\u002Fspan>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#E1E4E8\">(\u003C\u002Fspan>\u003Cspan style=\"--shiki-light:#b35309;--shiki-dark:#FFAB70\">file\u003C\u002Fspan>\u003Cspan style=\"--shiki-light:#cb2431;--shiki-dark:#F97583\">:\u003C\u002Fspan>\u003Cspan style=\"--shiki-light:#6F42C1;--shiki-dark:#B392F0\"> File\u003C\u002Fspan>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#E1E4E8\">)\u003C\u002Fspan>\u003Cspan style=\"--shiki-light:#cb2431;--shiki-dark:#F97583\">:\u003C\u002Fspan>\u003Cspan style=\"--shiki-light:#6F42C1;--shiki-dark:#B392F0\"> Promise\u003C\u002Fspan>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#E1E4E8\">&#x3C;\u003C\u002Fspan>\u003Cspan style=\"--shiki-light:#6F42C1;--shiki-dark:#B392F0\">Blob\u003C\u002Fspan>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#E1E4E8\">> {\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#57606a;--shiki-dark:#8b949e\">    \u002F\u002F Dynamic import: heic2any is large and needs window\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#cb2431;--shiki-dark:#F97583\">    const\u003C\u002Fspan>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#E1E4E8\"> { \u003C\u002Fspan>\u003Cspan style=\"--shiki-light:#b35309;--shiki-dark:#FFAB70\">default\u003C\u002Fspan>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#E1E4E8\">: \u003C\u002Fspan>\u003Cspan style=\"--shiki-light:#005CC5;--shiki-dark:#79B8FF\">heic2any\u003C\u002Fspan>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#E1E4E8\"> } \u003C\u002Fspan>\u003Cspan style=\"--shiki-light:#cb2431;--shiki-dark:#F97583\">=\u003C\u002Fspan>\u003Cspan style=\"--shiki-light:#cb2431;--shiki-dark:#F97583\"> await\u003C\u002Fspan>\u003Cspan style=\"--shiki-light:#cb2431;--shiki-dark:#F97583\"> import\u003C\u002Fspan>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#E1E4E8\">(\u003C\u002Fspan>\u003Cspan style=\"--shiki-light:#032F62;--shiki-dark:#9ECBFF\">'heic2any'\u003C\u002Fspan>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#E1E4E8\">)\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#cb2431;--shiki-dark:#F97583\">    const\u003C\u002Fspan>\u003Cspan style=\"--shiki-light:#005CC5;--shiki-dark:#79B8FF\"> out\u003C\u002Fspan>\u003Cspan style=\"--shiki-light:#cb2431;--shiki-dark:#F97583\"> =\u003C\u002Fspan>\u003Cspan style=\"--shiki-light:#cb2431;--shiki-dark:#F97583\"> await\u003C\u002Fspan>\u003Cspan style=\"--shiki-light:#6F42C1;--shiki-dark:#B392F0\"> heic2any\u003C\u002Fspan>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#E1E4E8\">({ blob: file, toType: \u003C\u002Fspan>\u003Cspan style=\"--shiki-light:#032F62;--shiki-dark:#9ECBFF\">'image\u002Fjpeg'\u003C\u002Fspan>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#E1E4E8\">, quality: \u003C\u002Fspan>\u003Cspan style=\"--shiki-light:#005CC5;--shiki-dark:#79B8FF\">0.9\u003C\u002Fspan>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#E1E4E8\"> })\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#cb2431;--shiki-dark:#F97583\">    return\u003C\u002Fspan>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#E1E4E8\"> Array.\u003C\u002Fspan>\u003Cspan style=\"--shiki-light:#6F42C1;--shiki-dark:#B392F0\">isArray\u003C\u002Fspan>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#E1E4E8\">(out) \u003C\u002Fspan>\u003Cspan style=\"--shiki-light:#cb2431;--shiki-dark:#F97583\">?\u003C\u002Fspan>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#E1E4E8\"> out[\u003C\u002Fspan>\u003Cspan style=\"--shiki-light:#005CC5;--shiki-dark:#79B8FF\">0\u003C\u002Fspan>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#E1E4E8\">]\u003C\u002Fspan>\u003Cspan style=\"--shiki-light:#cb2431;--shiki-dark:#F97583\">!\u003C\u002Fspan>\u003Cspan style=\"--shiki-light:#cb2431;--shiki-dark:#F97583\"> :\u003C\u002Fspan>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#E1E4E8\"> out\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#E1E4E8\">  }\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#cb2431;--shiki-dark:#F97583\">  return\u003C\u002Fspan>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#E1E4E8\"> { heicToJpg }\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#E1E4E8\">}\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>Inside, heic2any does three things. It starts a Web Worker that holds the libheif code, so decoding doesn’t freeze the page. The worker turns the HEIC into raw pixels. Back on the main thread, the pixels are drawn onto a canvas, and the browser’s own JPG encoder saves the canvas at the quality I pass in.\u003C\u002Fp>\n\u003Cp>An iPhone photo isn’t one picture inside the file. It’s many small HEVC tiles plus a grid that says how they fit together, which I describe in \u003Ca href=\"\u002Farticles\u002Freading-heic-exif-by-hand\">reading a HEIC file’s EXIF by hand\u003C\u002Fa>. libheif assembles the tiles, so the converter gets the full photo back.\u003C\u002Fp>\n\u003Cp>Safari takes the same route as every other browser here, even though it could decode HEIC by itself, so a photo converts the same way on every device.\u003C\u002Fp>\n\u003Ch2 id=\"keeping-1-3-mb-off-every-other-page\">Keeping 1.3 MB off every other page\u003C\u002Fh2>\n\u003Cp>The heic2any file in \u003Ccode>node_modules\u003C\u002Fcode> is about 1.3 MB before compression. That’s a lot of JavaScript for a page whose job is to load fast, and most visitors to most pages never pick a HEIC file.\u003C\u002Fp>\n\u003Cp>The dynamic \u003Ccode>import('heic2any')\u003C\u002Fcode> above already keeps it out of the main bundle. The library only downloads when the first file reaches \u003Ccode>heicToJpg\u003C\u002Fcode>. The “needs window” in the comment matters too. heic2any creates its worker as soon as the module loads, and the pages are prerendered on a server where there’s no \u003Ccode>window\u003C\u002Fcode>, so a static import would break the build.\u003C\u002Fp>\n\u003Cp>There was a second, less obvious problem. Nuxt adds prefetch hints for code that a page might need later, and the browser downloads those files in the background once the page is idle. The compressor and the resizer share the image tool component with the HEIC converter, so their pages were quietly prefetching the whole decoder before anyone chose a file. A hook in \u003Ccode>nuxt.config.ts\u003C\u002Fcode> turns that off for the heavy libraries:\u003C\u002Fp>\n\u003Cpre class=\"shiki shiki-themes github-light github-dark\" style=\"--shiki-light:#24292e;--shiki-dark:#e1e4e8;--shiki-light-bg:#fff;--shiki-dark-bg:#24292e\" tabindex=\"0\">\u003Ccode>\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#032F62;--shiki-dark:#9ECBFF\">'build:manifest'\u003C\u002Fspan>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#E1E4E8\">(manifest) {\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#cb2431;--shiki-dark:#F97583\">  for\u003C\u002Fspan>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#E1E4E8\"> (\u003C\u002Fspan>\u003Cspan style=\"--shiki-light:#cb2431;--shiki-dark:#F97583\">const\u003C\u002Fspan>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#E1E4E8\"> [\u003C\u002Fspan>\u003Cspan style=\"--shiki-light:#005CC5;--shiki-dark:#79B8FF\">id\u003C\u002Fspan>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#E1E4E8\">, \u003C\u002Fspan>\u003Cspan style=\"--shiki-light:#005CC5;--shiki-dark:#79B8FF\">chunk\u003C\u002Fspan>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#E1E4E8\">] \u003C\u002Fspan>\u003Cspan style=\"--shiki-light:#cb2431;--shiki-dark:#F97583\">of\u003C\u002Fspan>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#E1E4E8\"> Object.\u003C\u002Fspan>\u003Cspan style=\"--shiki-light:#6F42C1;--shiki-dark:#B392F0\">entries\u003C\u002Fspan>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#E1E4E8\">(manifest)) {\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#57606a;--shiki-dark:#8b949e\">    \u002F\u002F By module path, or by chunk name for libraries bundled as shared chunks (_hash.js)\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#cb2431;--shiki-dark:#F97583\">    if\u003C\u002Fspan>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#E1E4E8\"> (\u003C\u002Fspan>\u003Cspan style=\"--shiki-light:#032F62;--shiki-dark:#9ECBFF\">\u002F\u003C\u002Fspan>\u003Cspan style=\"--shiki-light:#032F62;--shiki-dark:#DBEDFF\">heic2any\u003C\u002Fspan>\u003Cspan style=\"--shiki-light:#cb2431;--shiki-dark:#F97583\">|\u003C\u002Fspan>\u003Cspan style=\"--shiki-light:#032F62;--shiki-dark:#DBEDFF\">pdfjs-dist\u003C\u002Fspan>\u003Cspan style=\"--shiki-light:#cb2431;--shiki-dark:#F97583\">|\u003C\u002Fspan>\u003Cspan style=\"--shiki-light:#032F62;--shiki-dark:#DBEDFF\">pdf-lib\u003C\u002Fspan>\u003Cspan style=\"--shiki-light:#cb2431;--shiki-dark:#F97583\">|\u003C\u002Fspan>\u003Cspan style=\"--shiki-light:#032F62;--shiki-dark:#DBEDFF\">jszip\u003C\u002Fspan>\u003Cspan style=\"--shiki-light:#cb2431;--shiki-dark:#F97583\">|\u003C\u002Fspan>\u003Cspan style=\"--shiki-light:#032F62;--shiki-dark:#DBEDFF\">cropperjs\u003C\u002Fspan>\u003Cspan style=\"--shiki-light:#cb2431;--shiki-dark:#F97583\">|\u003C\u002Fspan>\u003Cspan style=\"--shiki-light:#032F62;--shiki-dark:#DBEDFF\">browser-image-compression\u003C\u002Fspan>\u003Cspan style=\"--shiki-light:#032F62;--shiki-dark:#9ECBFF\">\u002F\u003C\u002Fspan>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#E1E4E8\">.\u003C\u002Fspan>\u003Cspan style=\"--shiki-light:#6F42C1;--shiki-dark:#B392F0\">test\u003C\u002Fspan>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#E1E4E8\">(\u003C\u002Fspan>\u003Cspan style=\"--shiki-light:#032F62;--shiki-dark:#9ECBFF\">`${\u003C\u002Fspan>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#E1E4E8\">id\u003C\u002Fspan>\u003Cspan style=\"--shiki-light:#032F62;--shiki-dark:#9ECBFF\">} ${\u003C\u002Fspan>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#E1E4E8\">chunk\u003C\u002Fspan>\u003Cspan style=\"--shiki-light:#032F62;--shiki-dark:#9ECBFF\">.\u003C\u002Fspan>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#E1E4E8\">name\u003C\u002Fspan>\u003Cspan style=\"--shiki-light:#cb2431;--shiki-dark:#F97583\"> ??\u003C\u002Fspan>\u003Cspan style=\"--shiki-light:#032F62;--shiki-dark:#9ECBFF\"> ''}`\u003C\u002Fspan>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#E1E4E8\">)) chunk.prefetch \u003C\u002Fspan>\u003Cspan style=\"--shiki-light:#cb2431;--shiki-dark:#F97583\">=\u003C\u002Fspan>\u003Cspan style=\"--shiki-light:#005CC5;--shiki-dark:#79B8FF\"> false\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#E1E4E8\">  }\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#E1E4E8\">},\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>The check uses both the module path and the chunk name, because Vite sometimes bundles a library into a shared chunk with a hashed file name, and then the path alone doesn’t mention it.\u003C\u002Fp>\n\u003Ch2 id=\"quality-size-and-what-s-left-behind\">Quality, size and what’s left behind\u003C\u002Fh2>\n\u003Cp>The JPG is saved at 90% quality. At that setting the difference from the HEIC is hard to see without zooming in.\u003C\u002Fp>\n\u003Cp>Expect the JPG to be \u003Cstrong>bigger\u003C\u002Fstrong> than the HEIC, often noticeably. That surprises people who think of conversion as compression. HEVC is simply a more efficient codec than JPG, so the same picture needs more bytes as a JPG. If the form also has a size limit, compress the JPG afterwards.\u003C\u002Fp>\n\u003Cp>The JPG is made from the decoded pixels alone, so the photo’s metadata isn’t copied across. The GPS location, the camera model and the date taken are gone from the JPG. For most uploads that’s a bonus. If you need the date, keep the original HEIC.\u003C\u002Fp>\n\u003Ch2 id=\"limits-i-chose-to-keep\">Limits I chose to keep\u003C\u002Fh2>\n\u003Cul>\n\u003Cli>\u003Cstrong>One photo at a time.\u003C\u002Fstrong> The file queue processes files one after another. A decoded 12-megapixel photo takes about 48 MB of memory as raw pixels (4 bytes per pixel), and decoding several at once is how phone tabs crash. A big batch takes longer, but it finishes.\u003C\u002Fli>\n\u003Cli>\u003Cstrong>50 MB per file\u003C\u002Fstrong>, the same limit as the other image tools. Ordinary iPhone photos are far below it.\u003C\u002Fli>\n\u003Cli>\u003Cstrong>The first image only.\u003C\u002Fstrong> A HEIC file can hold more than one image, such as a burst. The converter keeps the first one, which is the photo itself for normal camera shots.\u003C\u002Fli>\n\u003Cli>\u003Cstrong>Speed on older phones.\u003C\u002Fstrong> Decoding HEVC in JavaScript is heavy work, so an older phone can take a few seconds per photo. A laptop is much quicker.\u003C\u002Fli>\n\u003C\u002Ful>\n\u003Cp>If you’d rather avoid HEIC altogether, the iPhone can save JPGs from now on: Settings › Camera › Formats › Most Compatible. Photos you’ve already taken stay HEIC, which is where the converter helps.\u003C\u002Fp>\n\u003Ch2 id=\"try-it\">Try it\u003C\u002Fh2>\n\u003Cp>The \u003Ca href=\"\u002Fheic-to-jpg\">HEIC to JPG converter\u003C\u002Fa> takes one photo or a whole folder and gives you back JPGs, one by one or as a ZIP. The photos are decoded on your device and never uploaded. For more image jobs, see the \u003Ca href=\"\u002Fimage-tools\">image tools\u003C\u002Fa>.\u003C\u002Fp>\n",[18,22,25,28,31,34],{"id":19,"text":20,"level":21},"why-heic-files-get-stuck","Why HEIC files get stuck",2,{"id":23,"text":24,"level":21},"decoding-heic-without-the-browser-s-help","Decoding HEIC without the browser’s help",{"id":26,"text":27,"level":21},"keeping-1-3-mb-off-every-other-page","Keeping 1.3 MB off every other page",{"id":29,"text":30,"level":21},"quality-size-and-what-s-left-behind","Quality, size and what’s left behind",{"id":32,"text":33,"level":21},"limits-i-chose-to-keep","Limits I chose to keep",{"id":35,"text":36,"level":21},"try-it","Try it",4,1792325801025]