Faster, better AI-powered code reviews. Start your free trial!  
Faster, better AI-powered code reviews.
Start your free trial!

Get high quality AI code reviews

Javascript to Save Image – A How-To Guide

Table of Contents

Saving images with Javascript can provide powerful functionality to websites and web applications. With the ability to upload, optimize, and cache images, developers can create more dynamic and interactive experiences for users. This comprehensive guide will walk you through everything you need to know to save images locally using Javascript.In the early days of the web, images were primarily handled on the server-side. But with the rise of complex web applications, developers often need to enable users to upload images and save them directly in the browser. Whether it’s for photo editing tools, social platforms, ecommerce sites, or any app that deals with images, Javascript provides simple yet robust ways to handle image saving and manipulation on the client-side.

There are many advantages to saving images locally with Javascript:

  • Improved performance – Images load faster when saved locally versus retrieving from a server each time
  • Offline availability – Users can access locally saved images even without an internet connection
  • Smoother UX – No lag waiting for network requests means a more responsive experience
  • Less server load – By handling images client-side, your servers have less work to do

In this comprehensive guide, we’ll dig into the key Javascript APIs and methods that allow you to:

  • Upload and read image files from the user’s device
  • Securely save image data locally using client-side storage
  • Optimize images for performance using compression and caching
  • Follow best practices for security when handling user images

By the end, you’ll have a solid understanding and reusable code snippets to build robust image handling into your web applications using plain Javascript. Let’s get started!

Uploading and Reading Image Files

The first step in saving images locally is accessing the user’s selected image files and reading them into memory using Javascript. This involves getting a reference to the File object representing the image, then using the FileReader API to asynchronously read the file contents.

Accessing Image Files from Input

When a user selects an image file in a <input type="file"> element, the file can be accessed through the change event:

const input = document.querySelector('input[type="file"]');

input.addEventListener('change', e => {
  const imageFile =[0];

  // Do something with the image file

The files property on the contains a FileList of all selected files. We grab the first file at index 0.

You can also directly access the selected files from the input element:

const input = document.querySelector('input[type="file"]');

input.addEventListener('change', e => {
const imageFile = input.files[0];

// Do something with imageFile…


This gives you a reference to the first File object representing the image selected by the user.

Reading Image File Contents with FileReader

Once you have a reference to the File object, you can use the FileReader API to asynchronously read the contents of the file.

Create a new FileReader instance:

const reader = new FileReader();

Next, handle the load event which will be triggered once the file is read:

reader.addEventListener('load', () => {
// File contents loaded here


Finally, call readAsDataURL() to start reading the file contents into memory as a base64 encoded string:


Put together, it looks like:

const reader = new FileReader();
reader.addEventListener('load', () => {
  // File contents loaded here

The result property of the FileReader will contain the file’s data URI once loading is complete.Using the FileReader API, you can asynchronously read image files selected by the user in preparation for saving.

Saving Images with Local Storage

Now that you can load image files into memory as data URIs, the next step is saving them locally. The Local Storage API available in all modern browsers provides simple and secure client-side storage.

Storing a Single Image

To store a single image using Local Storage:

Get a reference to the image file and create a FileReader as shown earlier.

In the load callback, save the data URI to Local Storage:

const img = document.getElementById('my-img');
img.src = localStorage.getItem('myImage');

Set the src attribute of an <img> tag to the data URI and it will render the image.

With just a few lines of code, you can upload and save an image entirely client-side!

Storing Multiple Images

If your <input type="file"> allows multiple selections using the multiple attribute, you can store multiple images.

When multiple files are selected, the files property contains a FileList array-like object. We can convert this to a standard array using Array.from():

const images = Array.from(input.files);

Then loop through each image file:

images.forEach(image => {

// Create FileReader
// Read image
// Save to localStorage


To save each image with a unique key, you can use the index of the loop:

images.forEach((image, index) => {
const reader = new FileReader();
reader.addEventListener('load', () => {
localStorage.setItem('image-' + index, reader.result);

This will save the images with keys like image-0, image-1, etc.

Later when you need to display the images, query the DOM for image elements:

const storedImages = document.querySelectorAll('img');
storedImages.forEach((img, index) => {
img.src = localStorage.getItem('image-' + index);

And set the src of each to the corresponding localStorage key!

With these powerful yet simple APIs, you can build complete image upload and management functionality in the browser.

Optimizing Image Size and Performance

Saving images locally provides a major performance boost over retrieving them from a server every time. However, large image files will still slow down page load speeds.

Next we’ll explore client-side image optimization techniques to further improve performance.

Compressing Images Before Upload

Before saving the user’s uploaded images, it’s good practice to compress and resize them to more reasonable dimensions. This reduces bandwidth usage and improves page load times.

The HTML5 Canvas API makes compressing images easy with just a few lines of Javascript:

// Load image on canvas
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
const img = new Image();
img.src = originalImage;
canvas.width = img.width;
canvas.height = img.height;
ctx.drawImage(img, 0, 0);
// Compress image
const compressedImage = canvas.toDataURL('image/jpeg', 0.5); // 0.5 is compression ratio

This loads the original image, draws it on a canvas element, then converts the canvas contents to a JPEG data URI compressed to 50% quality.

The compressed image data can then be saved to Local Storage for optimized performance.

Caching Images with Service Workers

For frequently used images that need to load instantly, consider caching them locally using Service Workers.

Service Workers allow intercepting network requests and serving cached responses instead.

Register a service worker in your page:

if ('serviceWorker' in navigator) {

Then in sw.js, cache any images on install:

self.addEventListener('install', e => {
e.waitUntil('images').then(cache => {
return cache.addAll([
// etc

The Cache API opens a named cache and adds image files to it.

Later, intercept requests and try to serve from cache first:

self.addEventListener('fetch', e => {
caches.match(e.request).then(response => {
return response || fetch(e.request);

This provides an instant, cached response for images without any network requests!

With compression and caching, you can optimize images to improve your app’s speed and responsiveness.

Security Considerations

When dealing with user-provided files in the browser, security should always be a top priority. Let’s discuss some best practices to ensure safely handling images.

Access Control Allow Origin

Browser security prevents a web page from making requests to a different origin than itself.

So if your images are hosted on a different domain, you need to enable CORS (Cross Origin Resource Sharing) to allow cross-origin requests:


Otherwise, you’ll get errors trying to load images from a different origin.

To avoid such issues, consider handling images on the same origin as your app, or enabling CORS headers programmatically on the server for image requests.

Sanitizing File Names

When saving user-uploaded images, don’t directly use the file name they provide without sanitizing it first.

A malicious user could upload an image called <script>dangerousCode()</script>.jpg which could execute unwanted Javascript if directly added to your page.

To avoid this, sanitize the file name before saving:

function sanitizeFilename(name) {
return name.replace(/[^a-z0-9]/gi, '_').substr(0, 64);

const fileName = sanitizeFilename(userProvidedFilename);

This removes any non-alphanumeric characters and limits the length to a reasonable size.Always sanitize user inputs to avoid injection attacks!


Saving images locally using Javascript opens up many possibilities for building responsive image-centric web applications. Here are some key takeaways:

  • The FileReader API allows asynchronously reading image files selected in file inputs
  • Local Storage provides secure and persistent client-side storage for images
  • Optimization with Canvas compression and Service Worker caching improves performance
  • Follow security best practices like sanitizing inputs and enabling CORS

With the techniques covered in this guide including reading files, leveraging browser storage APIs, optimizing delivery, and keeping security top of mind, you can add powerful image handling functionality to your web projects using plain Javascript.The ability to save, manipulate, and display images on the client-side enables you to build the next generation of dynamic web applications with excellent user experiences. I hope you found this comprehensive overview helpful! Let me know if you have any other questions

Sarang Sharma

Sarang Sharma

Sarang Sharma is Software Engineer at Bito with a robust background in distributed systems, chatbots, large language models (LLMs), and SaaS technologies. With over six years of experience, Sarang has demonstrated expertise as a lead software engineer and backend engineer, primarily focusing on software infrastructure and design. Before joining Bito, he significantly contributed to Engati, where he played a pivotal role in enhancing and developing advanced software solutions. His career began with foundational experiences as an intern, including a notable project at the Indian Institute of Technology, Delhi, to develop an assistive website for the visually challenged.

Written by developers for developers

This article was handcrafted with by the Bito team.

Latest posts

Mastering Python’s writelines() Function for Efficient File Writing | A Comprehensive Guide

Understanding the Difference Between == and === in JavaScript – A Comprehensive Guide

Compare Two Strings in JavaScript: A Detailed Guide for Efficient String Comparison

Exploring the Distinctions: == vs equals() in Java Programming

Understanding Matplotlib Inline in Python: A Comprehensive Guide for Visualizations

Top posts

Mastering Python’s writelines() Function for Efficient File Writing | A Comprehensive Guide

Understanding the Difference Between == and === in JavaScript – A Comprehensive Guide

Compare Two Strings in JavaScript: A Detailed Guide for Efficient String Comparison

Exploring the Distinctions: == vs equals() in Java Programming

Understanding Matplotlib Inline in Python: A Comprehensive Guide for Visualizations

Get Bito for IDE of your choice