Plugin to resize image and create srcset

I’ve been searching and found some separate plugins - some to resize to multiple sizes and some to create srcset, but didn’t see a single plugin to do both. Are there any? The reason I’m asking, because I wouldn’t want every time I change my mind about sizes, to go to each plugin and adjust them.

Also didn’t see a plugin to use srcset from markdown so that usual image insert would generate HTML with srcset automatically Are there any?

It seems like the main reason to have multiple sizes is to use them in srcset, but none of the plugins I found implements both features :frowning:

@Karmalakas, Not sure if there are any plugins fulfilling your needs, but if you like coding, you could try the following:

Using a fresh install of Grav 1.7.0-rc20:

  • Create config file ‘/user/config/srcset.yaml’ with the following content:
    # Use string for min, max, step or [300, 400, 500] for fixed set of sizes
    derivatives:  300, 1500, 300 
    sizes: '(min-width: 600px) 100vw, 50vw'

For image in Markdown using custom shortcode

  • Install plugin ‘Shortcode Core’: $ bin/gpm install shortcode-core
  • Create file ‘user/config/plugins/shortcode-core.yaml’ and add:
    custom_shortcodes: '/user/custom/shortcodes'
  • Create file ‘user/custom/shortcodes/SrcSetShortcode.php’ with the following content:
    namespace Grav\Plugin\Shortcodes;
    use Thunder\Shortcode\Shortcode\ShortcodeInterface;
    class SrcSetShortcode extends Shortcode
       public function init()
          $this->shortcode->getRawHandlers()->add('srcset', function(ShortcodeInterface $sc) {
             $config = $this->grav['config']->get('srcset');
             $image = $sc->getContent();
             $title = $sc->getParameters()['title'];
             $alt = $sc->getParameters()['alt'];
             if (gettype($config['derivatives']) === 'array') {
                $derivatives = '[' . join($config['derivatives'], ',') . ']';
             } else {
                $derivatives = $config['derivatives'];
             $sizes = $config['sizes'];
             $twig = "{{['$image'].derivatives($derivatives).sizes('$sizes').html('$title', '$alt')|raw }}";
             return $twig;
  • Create page ‘/user/pages/03.srcset/’ with the following content:
    title: SrcSet
        twig: true
    [srcset title=mytitle alt=myalt]image.jpg[/srcset]
  • Add image ‘images.jpg’ to folder ‘/user/pages/03.srcset’
  • Browse to page ‘localhost/srcset’ and you should see an image that resizes at breakpoint 600px using different images;

Using Twig:

  • Update template ‘/user/themes/quark/templates/default.html.twig’ as follows:
    {% extends 'partials/base.html.twig' %}
    {% block content %}
       {% set image =|first %}
       {% if config.srcset.derivatives | of_type('array') %}
          {% set image = image.derivatives(config.srcset.derivatives) %}
       {% else %}
          {% set minmaxstep = config.srcset.derivatives | split(',') %}
          {% set image = image.derivatives(minmaxstep[0], minmaxstep[1], minmaxstep[2]) %}
       {% endif %}
       {% set image = image.sizes(config.srcset.sizes) %}
       {{ image.html('title', 'alt') | raw }}
       {{ page.content|raw }}
    {% endblock %}
  • Browse to page ‘localhost/srcset’ and you should see an image that resizes at breakpoint 600px using different images;

For more info on creating custom shortcodes, see Shortcode Core.


  • I’ll leave it up to you to make the above used hardcoded strings like ‘image.jpg’, ‘alt’, ‘title’ more dynamic.

Wow :slight_smile: Just amazing to see such a response :slight_smile: Thank you!
I have a couple of questions though :slight_smile:

Did you mean 3000 instead of one 300?

derivatives: '300,1600,300'

Where/How does the image real resize to multiple sizes happen? Does {{ image.html() }} do this the first time it is called?


Adapted code to value-type of srcset.derivatives: ‘min’, ‘max’, ‘step’ if string and fixed set of sizes if array.

See ImageMedium::derivatives():

derivatives( int/int[] $min_width , int $max_width=2500 , int $step=200 ) : \Grav\Common\Page\Medium\$this
Generate alternative image widths, using either an array of integers, or a min width, a max width, and a step parameter to fill out the necessary widths. Existing image alternatives won’t be overwritten.

Images are generated at the time ImageMedium::html() is being called.

1 Like

Thank you so much. Will try to implement over the weekend and let you know how it went :slight_smile:

I get some weird behavior.

No matter if I set derivatives to [600, 1200, 1600] or 600, 1800, 600, I get the same result:

    /image600w.jpg 600w, 
    /image.jpg 1150w" 
    sizes="(min-width: 768px) 100vw, 50vw

Also, before setting derivatives and sizes I have this:

    {% set image = page.header.image ?[page.header.image] :|first %}
    {% set image = image.cropZoom(1600, 1000).flip(0, page.header.image_flip) %}

So /image.jpg is cropped and flipped to the dimensions defined with cropZoom, but /image600w.jpg isn’t - it’s the original resized to fit 600px width.


  • Where does 1150w come from?
  • Why does this 1150w have 1600x1000 image instead of 1150px width?
  • Where is 1200w image?
  • How do I make cropped and/or flipped image responsive?


Please remove the cropZoom and flip to test the functioning of derivatives on its own.

  • What size does the original image have according the OS, or image editor?
  • The last and biggest image should be the original image. Maybe your image is only 1150px wide…
  • If so, there will be no 1200px.
  • If original image is 1150px wide, then:
    • [600, 1200, 1600] would result in: 600, 1150
    • 600, 1800, 600 would result in: 600, 1150

Most of my images are 2000px wide and didn’t even think to check. Of course it’s 1150px… Feel so embarrassed now…

What about cropped/flipped? Original is vertical 1150px and I cropZoom it to 1600 (maybe that’s also why didn’t even think to check, because it should be 1600w), but 600 version is resized from original

So it seems @pamtbaau’s solution itself works as described and expected, but the issue with image manipulations still there.

Thanks again for such a detailed response :slight_smile: