Extension API

Last updated: August 19th 2026

Extend Super Images from your own Craft plugin without forking core.

Runnable starters: examples/ — storage adapter, optimizer, operation, and a sample init() wiring.

How registration works

On plugin boot, Super Images registers built-in drivers, encoders, optimizers, operations, and storage types. Each registry then fires an event you can listen to:
RegistryEvent constantAdd via
DriversDriverManager::EVENT_REGISTER_DRIVERS$event->drivers[]
EncodersEncoderManager::EVENT_REGISTER_ENCODERS$event->encoders[]
OptimizersOptimizerManager::EVENT_REGISTER_OPTIMIZERS$event->optimizers[]
OperationsOperationRegistry::EVENT_REGISTER_OPERATIONS$event->operations['handle'] = Class::class
StorageOperationRegistry::EVENT_REGISTER_OPERATIONS$event->types['type'] = factory or $event->adapters['handle'] = instance

Register in your plugin init() before generation runs (normal Craft plugin init order is fine).

See examples/ExtensionPlugin.php for a complete wiring template.

Custom storage adapter

Implement StorageAdapterInterface. Super Images builds paths; you read/write/delete and return public URLs.


Recommended: config type factory (deployable via config/super-images.php):
use amici\SuperImages\events\RegisterStorageAdaptersEvent;
use amici\SuperImages\registries\StorageManager;
use yii\base\Event;

Event::on(StorageManager::class, StorageManager::EVENT_REGISTER_STORAGE_ADAPTERS,
    static function (RegisterStorageAdaptersEvent $event): void {
        $event->types['acme'] = static fn(string $name, array $config) => new \myagency\AcmeStorageAdapter($name, $config);
    },
);
// config/super-images.php
'storage' => [
    'adapters' => [
        'acme' => ['type' => 'acme', 'baseUrl' => 'https://cdn.example.com', /* … */],
    ],
],

Copy-paste base: examples/storage/ExampleStorageAdapter.php

For S3-compatible APIs, study src/storage/S3CompatibleStorageAdapter.php instead of starting from scratch.

Custom optimizer

Implement OptimizerInterface. Your name() must match the tool string in config:
'optimizers' => [
    'jpeg' => 'example-jpeg',  // matches ExampleOptimizer::name()
],

Copy-paste base: examples/optimizers/ExampleOptimizer.php

Use ProcessRunner for CLI tools — never shell out manually. If no registered optimizer matches, Super Images falls back to the built-in binary optimizer when the CLI exists.

Custom operation

Implement OperationInterface or extend AbstractOperation:
$event->operations['tint'] = \myagency\ExampleTintOperation::class;
Twig:
{{ craft.superImages.img(asset, {
  operations: [{ type: 'tint', color: '#0066cc', opacity: 0.15 }],
  format: 'jpg',
}) }}

Built-in operations call driver methods via duck typing (invokeDriver). Custom drivers work if they expose the same method names (resize, grayscale, …).

Copy-paste base: examples/operations/ExampleTintOperation.php

Custom driver

Implement ImageDriverInterface. Register with RegisterDriversEvent. Set 'driver' => 'my-driver' in config, or rely on auto fallback after libvips → imagick → gd.

Drivers manipulate pixels only — no storage URLs, no encoding to final formats unless via encodeNative().

Custom encoder

Implement EncoderInterface and register with RegisterEncodersEvent. Encoders are selected by output format — registering a WebP encoder replaces the built-in native encoder for webp while your plugin is loaded.

Most projects should keep the native encoder and set optimizers.webp = 'cwebp'. Custom encoders are for specialized pipelines (proprietary SDK, GPU farm, etc.).

Event::on(EncoderManager::class, EncoderManager::EVENT_REGISTER_ENCODERS,
    static function (RegisterEncodersEvent $event): void {
        $event->encoders[] = new \myagency\ExampleWebpEncoder();
    },
);

Copy-paste base: examples/encoders/ExampleWebpEncoder.php — PNG intermediate → cwebp → WebP, with native fallback.


Generation lifecycle events

GenerationService emits hooks for analytics or side effects:
EventWhen
EVENT_BEFORE_GENERATEBefore processing (including cache-hit checks)
EVENT_AFTER_GENERATEAfter success or skip
EVENT_BEFORE_ENCODEAfter operations, before encode
EVENT_AFTER_ENCODEAfter encode

Payload: GenerationEvent with request, definition, identity, and result when available.

Changing identity-affecting options in listeners can alter cache keys — treat identity as part of your public contract.

Contracts (quick reference)

InterfaceResponsibility
ImageDriverInterfaceLoad, transform, native encode
EncoderInterfaceHandle → format bytes
OptimizerInterfacePost-encode shrink
StorageAdapterInterfacePersist + public URL
OperationInterfaceOne named transform step

Rules: drivers don't store files; encoders don't build URLs; external binaries only via ProcessRunner; never log credentials or secrets into markers/identity.