NextJS has a really nice image component. To implement this I need to create a custom serialiser to get the image sizes from Craft . I can define a custom loader, however I'm not sure how feasible this is in Craft.

Here's an example loader function:

const craftImageLoader = ({ src, width, quality }) =>
  `https://example.com/${src}?w=${width}&q=${quality || 75}`;

Does Craft have an image endpoint that accepts these arguments? Perhaps even just src and width? If not then I will probably just write a module to handle this for me.

  • Can you use graphql? I've used next/image with craftcms via graphql with zero issues
    – magicspon
    Aug 16, 2021 at 17:13

1 Answer 1


As far as I know, Craft only provides two controller endpoints that can be used by anonymous users to transform images, both of which won't work well in your case:

  1. AssetsController::actionThumb() – This one takes a width and a height argument, but is geared towards generating thumbnails and doesn't work well for a general purpose image resizer, and it doesn't accept a quality argument.
  2. AssetsController::actionGenerateTransform() – This one generates a transform based on a transform ID or an asset ID & transform handle, but you can only use predefined transforms, it doesn't allow dynamic width/height parameters.

So you're probably stuck building your own. But that's quite doable:

  1. Create a custom controller, either in a plugin or a module.
  2. Set the controller to allow anonymous requests and don't use CSRF validation (see the sample code below).
  3. Add an action-method with arguments for the asset, width, height and any other parameters you want, like the quality. This methods needs to find the asset in question, transform it with the given arguments and return the resulting file as binary data.

One thing that might be a bit tricky is to find the correct asset if you're passing a path instead of an ID. You could either make your frontend handle assets by their IDs instead of their path, which would simplify the backend code. Or deconstruct the path in your controller to determine the folder / volume and query by those.

Here's some sample code to get you started (assuming you're writing your controller as a module, not a plugin):

namespace modules\Assets;

use Craft;
use craft\web\Controller;

class TransformController extends Controller
    protected $allowAnonymous = self::ALLOW_ANONYMOUS_LIVE;

    public $enableCsrfValidation = false;

    public function actionTransformAsset(int $assetId, int $width, int $height) {
        // 1. find asset based on the parameter
        $asset = Asset::find()->id($assetId)->one();
        if (!$asset) {
            throw new BadRequestHttpException('Invalid asset ID');

        // 2. transform the asset with the given width & height parameters
        $asset->setTransform(['width' => $width, 'height' => $height]);

        // 3. return the transformed asset as binary stream
        // Tip: Ensure asset transforms are executed immediately like this:
        Craft::$app->getConfig()->getGeneral()->generateTransformsBeforePageLoad = true;

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.