Vanilla HTML & JavaScript

Use <crop-image> with zero framework dependencies.

Installation

CDN / Script Tag

Drop a single script tag and the element is ready to use. No build step required.

<script src="https://unpkg.com/@imazen/crop-image/dist/crop-image.iife.js"></script>

<crop-image src="/photo.jpg"></crop-image>

npm + ES Modules

npm install @imazen/crop-image
<script type="module">
  import '@imazen/crop-image';
</script>

<crop-image src="/photo.jpg"></crop-image>

Attributes

All configuration happens through HTML attributes. Changes are reflected immediately.

AttributeTypeDefaultDescription
srcstringImage URL to crop
modecrop | crop-padcropCrop-only or crop with letterbox padding
aspect-ratiostringfreeLock ratio: 16/9, 1:1, or 1.5
shaperect | circlerectFrame shape. Circle forces 1:1 aspect ratio.
max-zoomnumberautoMaximum zoom level (default: image size / 50px)
snap-thresholdnumber0.03Snap-to-AR sensitivity during free resize (0 = off, 0.03 = 3%)
aspect-ratiosJSONMenu of choices: [{"w":16,"h":9,"label":"Wide"}]
min-widthnumberMinimum crop width in source pixels
min-heightnumberMinimum crop height in source pixels
max-widthnumberMaximum crop width in source pixels
max-heightnumberMaximum crop height in source pixels
valueJSONSet selection as CropSelection JSON
namestringForm field name (enables form participation)
adapterstringgenericRIAPI adapter: generic, imageflow, imageresizer
disabledbooleanfalseDisable all interaction

Events

The element fires two custom events. Both carry the same detail shape.

EventWhen
crop-changeContinuously during pan, zoom, or frame resize
crop-commitOnce when a gesture ends (pointer up, scroll stop, slider change)

Both events bubble and are composed (cross Shadow DOM). The detail object contains:

{
  selection: {
    crop: { x1, y1, x2, y2 },  // 0..1 fractions of source image
    pad: { top, right, bottom, left }  // 0..1 fractions (crop-pad mode)
  },
  riapi: {
    params: { crop: "...", cropxunits: "1", ... },
    querystring: "?crop=0.1,0.1,0.9,0.9&cropxunits=1&cropyunits=1"
  }
}

Listening to Events

const cropper = document.querySelector('crop-image');

cropper.addEventListener('crop-change', (e) => {
  // Live updates during drag
  console.log('Selection:', e.detail.selection);
  console.log('RIAPI:', e.detail.riapi.querystring);
});

cropper.addEventListener('crop-commit', (e) => {
  // Final value when user releases
  const qs = e.detail.riapi.querystring;
  document.getElementById('output').textContent = qs;
});

Setting the Crop Programmatically

Use the selection property to set the crop from JavaScript:

const cropper = document.querySelector('crop-image');

// Set a centered 50% crop
cropper.selection = {
  crop: { x1: 0.25, y1: 0.25, x2: 0.75, y2: 0.75 },
  pad: { top: 0, right: 0, bottom: 0, left: 0 }
};

Or use the value attribute with JSON:

<crop-image
  src="/photo.jpg"
  value='{"crop":{"x1":0.1,"y1":0.1,"x2":0.9,"y2":0.9},"pad":{"top":0,"right":0,"bottom":0,"left":0}}'
></crop-image>

Reading Properties

const cropper = document.querySelector('crop-image');

// Current selection (CropSelection object)
const sel = cropper.selection;

// RIAPI querystring for the current crop
const qs = cropper.riapiQuerystring;

// RIAPI params as an object
const params = cropper.riapiParams;

// Current config (read-only snapshot)
const config = cropper.config;

Changing Attributes Dynamically

const cropper = document.querySelector('crop-image');

// Lock to 16:9
cropper.setAttribute('aspect-ratio', '16/9');

// Circle crop (forces 1:1)
cropper.setAttribute('shape', 'circle');

// Limit zoom to 4x
cropper.setAttribute('max-zoom', '4');

// Switch to crop-pad mode
cropper.setAttribute('mode', 'crop-pad');

// Change image
cropper.setAttribute('src', '/another-photo.jpg');

// Use Imageflow adapter
cropper.setAttribute('adapter', 'imageflow');

CSS Custom Properties

Style the crop UI through CSS custom properties on the element:

crop-image {
  --crop-overlay-color: rgba(0, 0, 0, 0.65);
  --crop-border-color: rgba(255, 255, 255, 0.85);
  --crop-border-width: 2px;
  --crop-pad-color: rgba(80, 140, 220, 0.25);
  --crop-slider-track: rgba(255, 255, 255, 0.3);
  --crop-slider-thumb: #fff;
}

Keyboard Support

Click or tab to focus the crop element, then use:

Complete Example

<!DOCTYPE html>
<html>
<head>
  <script src="https://unpkg.com/@imazen/crop-image/dist/crop-image.iife.js"></script>
  <style>
    crop-image { max-width: 600px; }
  </style>
</head>
<body>
  <crop-image
    src="/photo.jpg"
    aspect-ratio="16/9"
    mode="crop"
    max-zoom="8"
  ></crop-image>

  <p>Querystring: <code id="output"></code></p>

  <script>
    document.querySelector('crop-image')
      .addEventListener('crop-commit', (e) => {
        document.getElementById('output').textContent =
          e.detail.riapi.querystring;
      });
  </script>
</body>
</html>