Skip to content

Repository files navigation

js-crop

npm version Documentation Maintenance License: MIT

A lightweight, dependency-free image cropper with a responsive built-in UI for mouse, touch, and keyboard input.

Features

  • Works with existing <img> elements and <input type="file"> controls
  • Responsive editor for desktop, mobile, tablets, and touchscreens
  • Resizable crop selection with undo, redo, and reset controls
  • JPEG and PNG output with configurable quality
  • Built-in download button and custom callback buttons
  • Customizable overlay, toolbar, and button colors
  • No framework or runtime dependency

Install

npm install js-crop

Browser script

Load the minified file from a CDN. The constructor is exposed as window.jsCrop.

<script src="https://cdn.jsdelivr.net/npm/js-crop@3.1.2/js-crop.min.js"></script>

To follow the latest published version instead of pinning a release:

<script src="https://cdn.jsdelivr.net/npm/js-crop/js-crop.min.js"></script>

You can also serve node_modules/js-crop/js-crop.min.js from your own application.

CommonJS

const jsCrop = require('js-crop');

js-crop runs in a browser and requires DOM, canvas, Image, and FileReader APIs. If you bundle it on the server, create an instance only in the browser.

Quick start

Crop an uploaded image

<input id="avatar" type="file" accept="image/png,image/jpeg" />

<script src="https://cdn.jsdelivr.net/npm/js-crop@3.1.2/js-crop.min.js"></script>
<script>
  new jsCrop('#avatar', {
    imageType: 'jpeg',
    imageQuality: 0.9,
    extButton: {
      buttonText: 'Use image',
      buttonTitle: 'Use the cropped image',
      callBack: function (dataUrl) {
        document.querySelector('#preview').src = dataUrl;
      }
    }
  });
</script>

Selecting a file opens the crop editor automatically.

Crop an existing image

<img class="crop-image" src="/images/photo.jpg" alt="Select an area to crop" />

<script>
  new jsCrop('.crop-image');
</script>

Clicking any matching image opens it in the editor. A selector may match one or many elements.

API

new jsCrop(selector, options?, customButtons?);

Creating an instance attaches listeners to every element that matches selector. The constructor returns the cropper instance; normal use does not require storing it.

selector

Type: string (required)

Any selector accepted by document.querySelectorAll(). Matching elements can be:

  • <input type="file">: opens the first selected file when the input changes.
  • <img>: opens the image when it is clicked.
  • Another element with a src property: opens that source when the element is clicked.
new jsCrop('#profile-photo');
new jsCrop('.croppable');
new jsCrop('input[type="file"][data-crop]');

options

Type: object (optional)

Option Type Default Description
imageType 'png' or 'jpeg' 'png' Output format used by downloads and callbacks. Values other than 'jpeg' fall back to PNG.
imageQuality number 1 Canvas export quality from 0 to 1. It mainly affects JPEG output.
saveButton boolean true Set to false to hide the built-in Download image button.
customColor object See below Overrides colors in the editor UI.
extButton object None Adds one callback button after the built-in controls.

customColor

All color fields accept any valid CSS color value and are optional.

new jsCrop('.crop-image', {
  customColor: {
    overlayBgColor: 'rgba(10, 10, 12, 0.98)',
    toolbarBgColor: 'rgba(18, 18, 20, 0.84)',
    buttonBgColor: 'rgba(255, 255, 255, 0.10)',
    buttonFontColor: '#ffffff'
  }
});

extButton

extButton is useful when you want the cropped result in application code instead of, or in addition to, the built-in download.

Field Type Default Description
callBack function Required Called with the cropped image data URL when the button is clicked. The button is only added when this is a function.
buttonText string 'ext' Button content. The value is inserted as HTML, so only use trusted content.
buttonTitle string 'Extension' Tooltip and accessible label.
buttonCSS string '' Inline CSS declarations appended to the button style, for example 'background:#2563eb;'. This is not a class name.
new jsCrop('#avatar', {
  saveButton: false,
  extButton: {
    buttonText: 'Save avatar',
    buttonTitle: 'Save the cropped avatar',
    buttonCSS: 'background:#2563eb;color:#fff;',
    callBack: function (dataUrl) {
      // dataUrl looks like: data:image/png;base64,...
      document.querySelector('#preview').src = dataUrl;
    }
  }
});

Despite the historical callback examples calling this value blob, the library currently returns a base64-encoded data URL string from canvas.toDataURL().

customButtons

Type: Array<object> (optional)

The third constructor argument adds any number of application-specific toolbar buttons.

Field Type Default Description
callBack function None Receives (dataUrl, relParam) when buttonEvent fires.
buttonText string 'ext' Button content, inserted as HTML.
buttonTitle string Button N Tooltip and accessible label.
buttonEvent string 'click' DOM event name that triggers the callback.
buttonCSS string '' Inline CSS declarations appended to the button style.
relParam any undefined Application-defined value passed unchanged as the callback's second argument.
new jsCrop(
  '.crop-image',
  {
    imageType: 'png',
    saveButton: false
  },
  [
    {
      buttonText: 'Preview',
      buttonTitle: 'Preview this crop',
      relParam: { destination: 'avatar-preview' },
      callBack: function (dataUrl, metadata) {
        document.getElementById(metadata.destination).src = dataUrl;
      }
    },
    {
      buttonText: 'Upload',
      buttonTitle: 'Upload this crop',
      buttonCSS: 'background:#15803d;',
      callBack: async function (dataUrl) {
        await fetch('/api/avatar', {
          method: 'POST',
          headers: { 'Content-Type': 'application/json' },
          body: JSON.stringify({ image: dataUrl })
        });
      }
    }
  ]
);

Converting the callback result to a Blob

If an upload API expects a Blob or File, convert the returned data URL first:

async function dataUrlToBlob(dataUrl) {
  return fetch(dataUrl).then(function (response) {
    return response.blob();
  });
}

new jsCrop('#avatar', {
  extButton: {
    buttonText: 'Upload',
    callBack: async function (dataUrl) {
      const blob = await dataUrlToBlob(dataUrl);
      const formData = new FormData();
      formData.append('image', blob, 'crop.png');

      await fetch('/api/avatar', {
        method: 'POST',
        body: formData
      });
    }
  }
});

Using the editor

  1. Open an image by clicking it or choosing a file.
  2. Select the crop tool, then drag across the image to create a crop area.
  3. Move or resize the selection, then select the crop tool again to apply it.
  4. Use Reset, Undo, or Redo as needed.
  5. Download the result or use one of your callback buttons.

Press Escape to close the editor. While a crop selection is visible, use the arrow keys to move it by one pixel or Shift + an arrow key to move it by ten pixels.

Cross-origin images

The browser must allow the image to be drawn to a canvas. Use same-origin images or images selected through a file input. A remote image that is not canvas-safe will let the editor open but the browser may block download and callback export with a canvas security error.

Contributing

Contributions, issues, and feature requests are welcome. See the issues page and contributing guide.

Author

ujw0l

Support

If this project helped you, please star the repository or buy me a coffee.

License

Copyright © 2019 ujw0l.

Licensed under the MIT License.

About

Library to enable image cropping

Topics

Resources

Code of conduct

Contributing

Security policy

Stars

1 star

Watchers

0 watching

Forks

Releases

Sponsor this project

Packages

Used by

Contributors

Languages