Custom Integration

Getting started

Adding the required JS script

You first need to copy the required script inside the <head> tag of the HTML document:

<script type="text/javascript">
        document.documentElement.className += " optimole_has_js";
        (function(w, d){
            var b = d.getElementsByTagName('head')[0];
            var s = d.createElement("script");
            var v = ("IntersectionObserver" in w) ? "_no_poly" : "";
            s.async = true; // This includes the script as async.
            s.src = "" + v + ".min.js";
            w.optimoleData = {
                key: "<user_key>",
                quality: "85",
                lazyloadOnly: "optimole-lazy-only",
                nativeLazyload : false,
                scalingDisabled: false,
                watchClasses: [],
                backgroundLazySelectors: "",
		backgroundReplaceClasses: [ "my-background-loaded-img" ],
                network_optimizations: false,
                ignoreDpr: true,
        }(window, document));

        document.addEventListener( "DOMContentLoaded", function() {
            document.body.className = document.body.className.replace("optimole-no-script","");
            if ( "loading" in HTMLImageElement.prototype && optimoleData, "nativeLazyload" ) && optimoleData.nativeLazyload === true ) {
                const images = document.querySelectorAll('img[loading="lazy"]');
                images.forEach( function (img) {
                    if ( !img.dataset.optSrc) {
                    img.src = img.dataset.optSrc;
                    delete img.dataset.optSrc;
        } );

If you're using backgroundReplaceClasses or backgroundLazySelectors you also need to add this <style> before the <script> above to lazyload background images:

 html .my-background-loaded-img:not(.optml-bg-lazyloaded),
 html  .my-background-loaded-img2:not(.optml-bg-lazyloaded) { background-image: none !important; }

If a background image is correctly lazy-loaded you should see class optml-bg-lazyloaded being attached to the containers of those images.

In order for the script to work you also need to whitelist the domains from where the images are fetched from. Use the "Whitelist Domain" tab to add a new domain to the list from

You need to replace <user_key> with the key found inside the custom integration section from

The minimum requirements for this to work is to have image tags with the following format inside the document:

<img data-opt-src="" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mP89+9/PQAJeQN8idj/HgAAAABJRU5ErkJggg==" width="800" height="600" />

In this example the domain is whitelisted and we have set as src a transparent pixel, you can leave out the src and it will be automatically filled by the script but it is not HTML compliant so for now we will use a transparent pixel. Take note that the original image source is now under data-opt-src attribute, this will be used by the script to process the URL.

You can generate your own Optimole placeholder but we will present that later.

The resulting image tag after processing will look something like this:

<img data-opt-src="" src="https://<user_key>" width="800" height="600"class="" data-opt-lazy-loaded="true" data-opt-otimized-width="800" data-opt-optimized-height="600">

 This is the minimum required for the service to work.


You can control the quality of the resulting images by changing the quality attribute from optimoleData inside the head script.

You can also control if Optimole should reduce quality based on the network conditions by setting network_optimizations to true.

You can add classes to the backgroundLazySelectors attribute separated by commas eg:

backgroundLazySelectors: ".my-background-loaded-img"

Optimole will lazy-load these elements by disabling the background display until scrolling into view.

You can set ignoreDPR to false to serve retina images if required.

Building your own image tags and URLs

We can go even further and you can custom build your own image tags and generate your own urls to leverage the service.

The basic URL should contain the domain, resize parameters, and source URL, like this:

In this case the domain is where the key is your user_key or custom domain

The resize parameters are width /w:800, height /h:600 and quality /q:85

The source URL is the original URL of the image, here:

Here are the supported URL attributes that you can set:

Width and Height

You can set the /w: and /h: attributes to a specific size or auto.


You can set the /q: attribute to a specific size between 0-100, auto or eco.

You can use eco to generate placeholder images instead of using a transparent base64 placeholder as it was mentioned in our simple example.

Additional attributes that you can use are:

Resize type

Defines how Optimole will resize the source image.

You can set /rt: attribute to fitfill or auto

Default is fit


When Optimole needs to cut some parts of the image, it is guided by the gravity.

You can set /g: to:

  • no: north (top edge);
  • so: south (bottom edge);
  • ea: east (right edge);
  • we: west (left edge);
  • noea: north-east (top-right corner);
  • nowe: north-west (top-left corner);
  • soea: south-east (bottom-right corner);
  • sowe: south-west (bottom-left corner);
  • ce: center.
  • x_offset, y_offset - (optional) specify gravity offset by X and Y axes.

Default is ce:0:0


Defines an area of the image to be processed (crop before resize).

You can set /c: to width:height:gravity

Width and height define the size of the area. When width or height is set to 0, Optimole will use the full width/height of the source image.

Gravity (optional) accepts the same values as gravity option. When gravity is not set, Optimole will use the value of the gravity option.

Eg: /c:200:300:ce where width is 200, height 300 and gravity is center

Using these options you can personalize and generate your own URLs giving you more control over the resulting image. Eg.:

<img data-opt-src="https://<user_key>" src="https://<user_key>" width="500" height="300"/>

For more details you can see our example code here:

Did this answer your question? Thanks for the feedback There was a problem submitting your feedback. Please try again later.