Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Statamic Image Cropper Laravel Package

ibelieve/statamic-image-cropper

A Statamic CMS addon for cropping images directly in the control panel. Editors can define crop areas and focal points, while developers render the resulting crops in templates, keeping image handling simple and consistent across a Laravel-powered site.

View on GitHub
Deep Wiki
Context7

Getting Started

This is a fork of tv2regionerne/statamic-image-cropper, published by @iBelieve. v2.0.0 is the first release of the fork and exists for one reason: Statamic 6 support. The jump from upstream 1.7.0 to 2.0.0 is a major bump for that compatibility work (plus a Vue 3 / Tailwind 4 rewrite of the control-panel UI), not for a new feature set. Treat it as "the same addon, ported forward."

Before you install, decide which side of the line you're on:

  • On Statamic 6 → this fork is currently your only option. Upstream stops at 1.7.0.
  • On Statamic 5 → stay on upstream tv2regionerne/statamic-image-cropper:^1.7. There is no reason to take a v2 major bump for a project that can't consume the Statamic 6 code path.

Minimal steps (Statamic 6):

# If migrating from upstream, remove it first — both packages register
# the same fieldtype handle and CP assets.
composer remove tv2regionerne/statamic-image-cropper

composer require ibelieve/statamic-image-cropper:^2.0
php artisan vendor:publish --tag=statamic-image-cropper-config   # verify tag name
php artisan config:clear && php artisan view:clear
php artisan statamic:stache:clear

Confirm the actual Composer name and publish tags against the fork's composer.json and README rather than trusting any snippet — a fork's namespace is whatever the maintainer set it to, and it may or may not have been renamed away from the upstream vendor.

Where to look first, in this order:

  1. README.md — the fork is only one release old; the README is the spec.
  2. config/ — the published config file defines the crop sizes/presets. This is the file you will actually edit day to day.
  3. src/Fieldtypes/ — the fieldtype class, to see exactly what gets saved into the content (array shape, JSON, asset reference).
  4. resources/js/ — the Vue 3 CP component, if you need to debug editor behaviour or override the UI.
  5. CLAUDE.md — new in this release and genuinely useful: it documents the project's architecture and conventions for agent-assisted work, which doubles as a fast orientation for humans.

First use case to prove it out: a hero image that needs three crops (wide, square, portrait). Add the cropper field to a blueprint, define those three sizes, crop an asset in the CP, then render an <img srcset> in a template. If that loop works — crop saved, Glide URL generated, image correct — the rest of the addon is variations on it.

Implementation Patterns

Config-first crop definitions. Define your crop sizes once in the published config (name + width/height or aspect ratio), then reference them by key from blueprints. This keeps designers' aspect ratios in one place and means changing a size doesn't require touching every blueprint. If the fieldtype also accepts per-field size overrides, prefer config keys for anything used more than once and reserve inline definitions for genuine one-offs.

Blueprint wiring. Add the cropper field to the blueprint where the asset lives, and give it a clear display name — editors see this in the CP. Typical shapes:

image:
  type: image_cropper        # confirm the handle in the fieldtype class
  display: 'Hero image'
  container: assets

Rendering — the crop is applied at Glide time, not at upload time. The stored value is (asset + crop coordinates/focal data); the actual pixels are produced by Glide. So your template work is Glide work:

{{ image }}
  <img
    src="{{ url }}?w=1200&h=600&fit=crop"
    srcset="{{ url }}?w=600&h=300&fit=crop 600w,
            {{ url }}?w=1200&h=600&fit=crop 1200w"
    sizes="100vw"
    alt="{{ alt }}">
{{ /image }}

Wrap that in a Blade component or Antlers partial (resources/views/components/cropped-image.blade.php) that takes the field value plus a preset key and emits src/srcset/alt/width/height. Every implementation of this addon eventually grows that wrapper — build it on day one rather than scattering Glide params across templates.

Programmatic access. Expect the field value to be an array/object containing the asset plus crop metadata. Use $entry->get('image') for raw access and the augmented value ($entry->augmentedValue('image')) when you want the fieldtype's own rendering/URL logic. For JSON APIs and headless use, normalise to a plain array in an API Resource — never leak the raw crop payload to a frontend that doesn't need it:

'hero' => [
    'url'    => $entry->augmentedValue('image')->url(),
    'srcset' => [...],
    'alt'    => $entry->get('image')['alt'] ?? '',
],

Multi-site. Crop coordinates live in the entry's content, so they localise like any other field unless you explicitly mark the field as non-localisable. For a shared brand image across locales, decide deliberately: shared crop (one field, not localised) vs per-locale framing (localised). Mixing the two silently is a common source of "the crop reverted on the French site" bug reports.

Deploy workflow. Crops are URL-parameterised, so:

  • Run php artisan glide:clear (or your Glide cache equivalent) whenever crop presets change.
  • Pre-generate hot crops on deploy if you have a warmup command — first-hit Glide generation on a hero image is visible latency.
  • If you front Glide with a CDN or static cache, key it on the full query string. This is the single most important integration detail (see Gotchas).

Gotchas and Tips

The fork is a one-release-old, single-maintainer project. That's not a reason to avoid it — it's the reason it exists — but pin it (^2.0) and read the diff before each minor. There is no guarantee the fork tracks upstream, and no guarantee upstream accepts the Statamic 6 work back. If upstream ever ships v2 itself, you'll have a package-name conflict to resolve, so isolate the dependency behind your own Blade wrapper component. That wrapper is your escape hatch if you ever have to swap implementations.

Package swap conflicts. Installing the fork alongside upstream, or after a composer remove that left published assets in place, produces confusing "fieldtype not found" or double-registered-CP-asset symptoms. Clean up fully: remove the old package, delete any published config/views from the old vendor, composer dump-autoload, clear config/view/Stache caches, and hard-refresh the CP (the CP caches JS aggressively).

Vue 3 + Tailwind 4 rewrite means published overrides will break. If your project published and customised the addon's views, CSS, or JS from 1.x, those overrides are now stale — Tailwind 4's config model and Vue 3's component API both changed. Re-publish and re-apply customisations rather than copying old files forward. Same applies to any CP CSS that targeted the addon's old class names.

Statamic 6's control panel is not a drop-in. The 2.0.0 changelog attributes the major bump solely to Statamic 6 support. If anything, that undersells the risk: this is a UI rewrite, not a version-constraint bump. Smoke-test the crop editor itself in the CP — drag handles, save, re-open, verify persistence — on newly created entries and on existing entries with 1.x-era crop data. Data-format changes between the Vue 2 and Vue 3 components are exactly the kind of thing a static analyser cannot catch.

CI is a quality signal, not a safety net. This release adds Larastan/PHPStan (multiple PHP versions, deprecation rules), ESLint, and a check that build output is committed. That's a meaningfully better-maintained fork than typical. But notice what's not in the changelog: a test suite. Static analysis proves types, not that cropping behaves. Your manual verification is doing real work here.

Cache-busting on re-crop is the classic production bug. Glide URLs are derived from the source path + transform params. If an editor re-frames a crop and the resulting URL is byte-identical, your CDN/static cache/responsecache will happily serve the old image, sometimes for weeks. Two mitigations: include a version/hash (updated-at timestamp of the asset or entry) as a query parameter that Glide ignores but your cache keys on, or purge the affected paths on save via an event listener. Pick one before launch, not after the first complaint.

Don't trust the dev branch for the build output. The new CI check verifies dist is committed — good practice, but it also means a mid-development commit can contain a stale or partial build. Install from the tagged v2.0.0 release, never from dev-main, unless you're actively debugging the addon itself.

Replicator/Bard sets. The perennially fragile case for any Statamic fieldtype holding structured state: add a set, crop the image, save, reorder the sets, save again. Vue 3 rewrites in particular have a habit of losing state on newly-added sets before the first save. Verify it explicitly.

Glide source constraints apply. Cropping needs a local, Glide-readable asset. Remote/stream-wrapped containers and some S3 setups without a Glide adapter will fail at render time, not at crop time — so a passing CP experience doesn't prove your front end works. Test with the actual production asset container.

Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
uni-method/symfony-lib
tacticmedia/scim-server-bundle
neika-ai/symfony-bundle
mattleads/agent-publishing-bundle
glavweb/framework-bundle
glavweb/extra-bundle
glavweb/api-bundle
glavweb/admin-bundle
adachsoft/api-version-bundle
elsayed85/laravel-api-exceptions
ouhssini/laravel-demo-mode
daworks/ncloud-outbound-mailer-driver
gp247/cms
ignibyte/aic-gates
nexa-merchant/blog
novasa/laravel-language-center
opencage/geocode
adolfoholzer/parameters
livenetworks/ln-starter
cgv/localized-resource