Image cropper tool
npm install hq-cropper


A lightweight, zero-dependency image cropper for high-quality square crops. Perfect for profile pictures, avatars, and thumbnails.
- Zero dependencies — pure TypeScript
- Mobile-friendly with full touch support
- High-quality output with configurable compression
- Square crop with resizable portal
- Drag and resize from all corners
- File validation (type and size)
- Error handling with callbacks
- Fully customizable styling
- Inherits fonts from your application
- Works with any framework (React, Vue, Angular, vanilla JS)
``bash`
npm install hq-cropper
`bash`
pnpm add hq-cropper
`bash`
yarn add hq-cropper
`typescript
import { HqCropper } from 'hq-cropper'
const cropper = HqCropper((base64, blob, state) => {
console.log('Cropped image:', base64)
console.log('Blob:', blob)
console.log('Original file:', state.fileName)
})
// Open file picker
document.querySelector('#crop-button').addEventListener('click', () => {
cropper.open()
})
`
Creates a new cropper instance.
`typescript`
const cropper = HqCropper(
onSubmit, // Required: callback with result
config, // Optional: configuration options
css, // Optional: custom CSS class names
onError // Optional: error handler
)
#### Parameters
| Parameter | Type | Description |
| ---------- | ----------------------------------------------------------------------- | ----------------------------------------- |
| onSubmit | (base64: string, blob: Blob \| null, state: ApplicationState) => void | Called when user applies the crop |config
| | Partial | Configuration options |css
| | Partial | Custom CSS class names |onError
| | (message: string) => void | Called on validation or processing errors |
#### Returns
`typescript`
interface HqCropperInstance {
open: () => void // Opens file picker dialog
}
| Option | Type | Default | Description |
| ------------------- | ------------------------------ | -------------------------------------------------------- | -------------------------------------------------- |
| portalSize | number | 150 | Initial size of crop portal in pixels |minPortalSize
| | number | 50 | Minimum portal size (prevents too small crops) |portalPosition
| | [number, number] \| 'center' | 'center' | Initial portal position |framePadding
| | number | 3 | Padding around the image frame |outputSize
| | number | 0 | Output size in pixels (0 = use original selection) |compression
| | number | 1 | JPEG compression (0-1, where 1 is best quality) |type
| | 'jpeg' \| 'png' | 'jpeg' | Output image format |maxFileSize
| | number | 0 | Max input file size in bytes (0 = no limit) |allowedTypes
| | string[] | ['image/jpeg', 'image/png', 'image/gif', 'image/webp'] | Allowed MIME types |applyButtonLabel
| | string | 'Apply' | Apply button text |cancelButtonLabel
| | string | 'Cancel' | Cancel button text |
#### Fixed Output Size (512x512 PNG)
`typescript`
const cropper = HqCropper(onSubmit, {
type: 'png',
outputSize: 512,
compression: 1,
})
#### Compressed JPEG for Smaller Files
`typescript`
const cropper = HqCropper(onSubmit, {
type: 'jpeg',
outputSize: 256,
compression: 0.7,
})
#### File Size Restriction
`typescript`
const cropper = HqCropper(
onSubmit,
{
maxFileSize: 5 1024 1024, // 5MB
allowedTypes: ['image/jpeg', 'image/png'],
},
undefined,
(error) => alert(error)
)
#### Custom Button Labels
`typescript`
const cropper = HqCropper(onSubmit, {
applyButtonLabel: 'Сохранить',
cancelButtonLabel: 'Отмена',
})
`tsx
import { useRef, useState } from 'react'
import { HqCropper } from 'hq-cropper'
function AvatarUpload() {
const [avatar, setAvatar] = useState
const cropperRef = useRef(
HqCropper(
(base64) => setAvatar(base64),
{ portalSize: 200 },
undefined,
(error) => console.error(error)
)
)
return (
$3
`vue
![Avatar]()
`$3
`html
`Custom Styling
You can override default CSS classes:
`typescript
const cropper = HqCropper(
onSubmit,
{},
{
root: ['my-cropper'],
portal: ['my-portal'],
applyButton: ['btn', 'btn-primary'],
cancelButton: ['btn', 'btn-secondary'],
}
)
`Available CSS class overrides:
| Class | Element |
| -------------------------- | -------------------------- |
|
root | Root container |
| header | Header with filename |
| body | Main content area |
| footer | Footer with buttons |
| portal | Crop selection area |
| portalArea | Portal container |
| sourceImage | Source image |
| preview | Preview container |
| previewImage | Preview image |
| applyButton | Apply button |
| cancelButton | Cancel button |
| handlerMove | Move handle |
| handlerResizeTopLeft | Top-left resize handle |
| handlerResizeTopRight | Top-right resize handle |
| handlerResizeBottomLeft | Bottom-left resize handle |
| handlerResizeBottomRight | Bottom-right resize handle |TypeScript Support
Full TypeScript support with exported types:
`typescript
import type {
HqCropperInstance,
ConfigurationOptions,
ClassNames,
ApplicationState,
ErrorHandler,
} from 'hq-cropper'
`Fonts
HQ-Cropper does not define any
font-family. The cropper inherits the font from your application's CSS, ensuring seamless integration with your design system.`css
/ Your app's CSS /
body {
font-family:
Inter,
-apple-system,
BlinkMacSystemFont,
'Segoe UI',
sans-serif;
}/ The cropper will automatically inherit this font /
`To override fonts specifically for the cropper:
`typescript
const cropper = HqCropper(
onSubmit,
{},
{
container: ['my-cropper-container'],
}
)
``css
.my-cropper-container {
font-family: 'Custom Font', sans-serif;
}
``Works in all modern browsers that support:
- ES2020+
- Canvas API
- FileReader API
- Touch events (mobile)
MIT © Iakov Salikov