ScreenshotNeo

BlogComparisons

JavaScript Libraries for Image Preview and Editing

Compare Cropper.js and Pintura for browser image previews and editing, with runnable setup, remote-image guidance, integration choices, and troubleshooting.

By the ScreenshotNeo team1 October 20268 min read

Short answer: choose Cropper.js when your requirement is an interactive cropper that you can place inside your own interface. Choose Pintura when you need a broader browser editing component with supplied editor interfaces and framework integrations. Neither is universally best: compare the editing operations, UI ownership, image sources, framework, licensing terms, and amount of integration work your product allows.

1. Cropper.js or Pintura?

Question Cropper.js Pintura
Primary scope Focused JavaScript image cropper Broader browser image-editing component
UI approach You assemble controls and surrounding workflow Append, modal, and overlay editor factories are documented
Frameworks Use the JavaScript package in your framework Installation documentation lists React, Vue, Angular, Svelte, jQuery, and Alpine components
Remote images Must still obey browser loading and canvas security rules URL input is supported, but remote servers need correctly configured CORS headers
License information in the reviewed sources MIT; the project site and npm list version 2.2.0 The reviewed v8 documentation does not establish complete current licensing or commercial terms
Best fit Upload-and-crop flows, avatars, thumbnails, simple custom editors Products that need a supplied editing component and more than cropping

Cropper.js describes itself as a “JavaScript image cropper,” and its project site displays version 2.2.0 under the MIT License. The npm package also lists 2.2.0. Recheck the package and license before publishing because versions and terms can change: official site and npm package.

Pintura v8 documents a JavaScript component for editing images in the browser. Its factories support appending an editor, opening a modal editor, overlaying crop controls, and registering a custom element. The documented overlay layout currently supports cropping only: Pintura v8 documentation and installation and framework components.

2. Decide by the editing job

  • Only crop or set an aspect ratio: start with Cropper.js.
  • Crop plus rotations, adjustments, redaction, or a ready-made editor workflow: evaluate Pintura and confirm the exact plugins and commercial terms you need.
  • Exact custom controls and visual design: Cropper.js gives you a smaller focused surface, but you must build the controls, state handling, validation, and export flow.
  • Several frontend frameworks: Pintura documents framework-specific components; still verify the current package names and versions for your stack.
  • Remote images: confirm CORS before selecting either library. A URL that displays in an <img> element can still fail when pixel data is read from a canvas.

3. A complete Cropper.js preview-and-crop example

This example uses the package from npm, previews a local file, lets the user select a square crop, and exports a JPEG blob. It keeps the original file untouched until the user confirms the crop.

npm install cropperjs
<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>Crop an image</title>
  <link rel="stylesheet" href="/node_modules/cropperjs/dist/cropper.css">
  <style>
    .stage { max-width: 720px; }
    #source { display: block; max-width: 100%; }
    #result { max-width: 240px; margin-top: 1rem; }
  </style>
</head>
<body>
  <label>Choose an image <input id="file" type="file" accept="image/*"></label>
  <div class="stage"><img id="source" alt="Selected image preview" hidden></div>
  <button id="crop" type="button" disabled>Export crop</button>
  <div><img id="result" alt="Cropped result" hidden></div>

  <script type="module">
    import Cropper from 'cropperjs';

    const input = document.querySelector('#file');
    const source = document.querySelector('#source');
    const cropButton = document.querySelector('#crop');
    const result = document.querySelector('#result');
    let cropper;
    let objectUrl;

    input.addEventListener('change', () => {
      const file = input.files?.[0];
      if (!file || !file.type.startsWith('image/')) return;
      if (objectUrl) URL.revokeObjectURL(objectUrl);
      objectUrl = URL.createObjectURL(file);
      source.onload = () => {
        cropper?.destroy();
        cropper = new Cropper(source, {
          aspectRatio: 1,
          viewMode: 1,
          autoCropArea: 0.9,
          responsive: true
        });
        cropButton.disabled = false;
        source.hidden = false;
        result.hidden = true;
      };
      source.src = objectUrl;
    });

    cropButton.addEventListener('click', () => {
      if (!cropper) return;
      cropper.getCroppedCanvas({
        width: 800,
        height: 800,
        imageSmoothingEnabled: true,
        imageSmoothingQuality: 'high'
      }).toBlob(blob => {
        if (!blob) return;
        const resultUrl = URL.createObjectURL(blob);
        result.src = resultUrl;
        result.hidden = false;
        // Upload `blob` with fetch or FormData here.
      }, 'image/jpeg', 0.9);
    });

    window.addEventListener('beforeunload', () => {
      if (objectUrl) URL.revokeObjectURL(objectUrl);
    });
  </script>
</body>
</html>

For a production upload, append the returned blob to FormData, send it to your server, and validate MIME type, dimensions, and maximum bytes on the server as well as in the browser.

Useful Cropper.js configuration decisions

  • aspectRatio locks a ratio such as 1 for square avatars. Leave it unconstrained when users may choose any crop.
  • viewMode controls how the crop box may move relative to the canvas. Test the setting against very wide and very tall photos.
  • autoCropArea controls the initial crop size; it is not a security limit.
  • Use getCroppedCanvas() dimensions to cap output size. Large source photos can otherwise create expensive canvases.
  • Call destroy() before replacing the image to remove event handlers and old state.
  • Revoke every object URL that is no longer needed with URL.revokeObjectURL().

4. Pintura integration patterns

Pintura’s v8 API documents three practical UI patterns:

  1. Append: mount an editor inside an existing element.
  2. Modal: open an editor when the user clicks an upload or edit action.
  3. Overlay: place crop controls over an image preview. The documentation says this layout currently supports cropping only.

Its image property documentation lists files, blobs, data URLs, URLs, ImageData, ImageBitmap, canvases, and image elements as supported sources. Remote URLs must return appropriate CORS headers: Pintura image input documentation.

// The exact package entry points and license depend on your Pintura purchase
// and current v8 installation. Follow the current installation guide first.
import { createEditor } from '@pqina/pintura';
import '@pqina/pintura/pintura.css';

const editor = createEditor({
  src: fileOrBlob,
  imageCropAspectRatio: 1
});

editor.on('process', event => {
  const editedBlob = event.dest;
  // Upload editedBlob with fetch/FormData.
});

// For an inline editor, append the editor element to your container.
container.append(editor.element);

Confirm the current import path, plugins, and commercial terms in the version you install. The reviewed sources establish the v8 factories and input types, but they do not establish Pintura’s complete current license or pricing.

5. Handling local files and remote URLs

Local files

  1. Read the selected File from the file input.
  2. Create an object URL or pass the file/blob directly to the editor.
  3. Release object URLs after replacing or closing the editor.
  4. Validate file type, byte size, pixel dimensions, and decompression limits before processing.

Remote files and CORS

For a remote image, the image host must allow your origin with CORS response headers. If the image is drawn to a canvas without successful CORS handling, canvas export can fail with a security error. Configure the image server, use a same-origin proxy that you control, or download the asset server-side and provide a local upload. Do not assume that adding crossOrigin="anonymous" fixes a server that sends no CORS header.

6. Export, quality, and privacy

  • Format: JPEG is compact for photographs; PNG preserves transparency and sharp edges; WebP can reduce bytes where your delivery targets support it.
  • Quality: JPEG quality is a trade-off, not a guarantee of visual quality. Review representative photos, text-heavy images, and gradients.
  • Dimensions: export only the size your UI needs. A 4,000-pixel avatar wastes bandwidth and memory.
  • Metadata: browser canvas export commonly omits original metadata. If EXIF orientation, GPS, or color profiles matter, define a server-side policy.
  • Privacy: local-only editing keeps source pixels in the browser until you upload them. A remote editor or proxy changes that data path; document it for your users.

7. Troubleshooting

Symptom Likely cause Fix
Preview is blank Image URL was revoked too early, or the file is not an image Set the image source before constructing the cropper, wait for load, and validate the MIME type.
Cropper appears twice The previous instance was not destroyed Call destroy() before creating a new instance.
Canvas export throws a security error Remote image lacks CORS permission Configure CORS on the image host or use a same-origin/server-side proxy.
Output is unexpectedly huge Export uses source dimensions Pass explicit width and height to the canvas export and enforce server limits.
Mobile layout overflows Editor container has a fixed width or height Use responsive CSS, constrain the image to its container, and test portrait devices.
Rotation or adjustment is missing Cropper.js is focused on cropping Build the missing controls yourself or evaluate Pintura’s broader editor and plugins.
Pintura URL input fails Remote server does not return valid CORS headers Fix the remote response headers or provide a same-origin copy.
License review is blocked Package terms are version- or purchase-specific Check the current vendor terms before shipping, especially for Pintura.

8. Performance, reliability, and cost considerations

This research did not establish an apples-to-apples benchmark for speed, bundle size, accessibility, browser coverage, or memory use. Measure your own representative images and devices before making those claims.

  • Decode and resize very large images before opening a full editor when possible.
  • Keep only one editor instance active and release object URLs.
  • Debounce expensive preview operations such as repeated filter or zoom updates.
  • Move heavy transformations to a worker or server when low-end devices struggle.
  • Set upload and processing timeouts, show progress, and allow cancellation for large files.
  • Cache immutable source assets, but avoid caching private uploads in shared browser or proxy caches.

Cropper.js is listed as MIT in the reviewed sources. Pintura’s complete commercial terms were not established in this research pass, so include a license review in procurement and release planning.

9. Or skip the browser setup

If your goal is to capture a website rather than let a user edit an uploaded image, ScreenshotNeo provides a one-request screenshot API. Cookie banners, newsletter popups, and chat widgets are removed before the shot. Bot checks, blank pages, failed loads, and cache hits are not billed, and responses identify the page verdict and billing status. Its MCP server lets Claude, Cursor, and other MCP clients take screenshots, inspect pages, and capture PDFs. The free plan includes 1,000 screenshots per month with no card; paid plans start at $5 for 3,000.

See the ScreenshotNeo API documentation for the current options.

curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
import requests
r = requests.get("https://api.screenshotneo.com/v1/shot", params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"}, timeout=90)
open("shot.webp", "wb").write(r.content)
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);

Create a free ScreenshotNeo account to get 1,000 screenshots each month with no card.

10. FAQ

Can Cropper.js edit pixels beyond cropping?

It is presented as a focused cropper. If you need a broader editing workflow, evaluate Pintura or assemble additional browser APIs and controls.

Does Pintura accept a URL directly?

Yes, its documented image inputs include URLs, but the remote server must be configured for CORS.

Which library is faster?

The reviewed research did not test performance, so there is no evidence here to declare a winner. Benchmark your own images, devices, and export settings.

Do I need a screenshot API for image editing?

No. ScreenshotNeo is for capturing rendered web pages and PDFs. Use an in-browser editor when a user must crop or adjust an uploaded image.

Can I use these libraries with any commercial product?

Review the current license and commercial terms for the exact package and version before shipping. The reviewed sources identify Cropper.js as MIT; they do not establish Pintura’s complete current terms.