Getting StartedTutorialsDeveloper GuidesGenerating Social Images with imgix

Generating Social Images with imgix

In this guide, we’ll explore how to leverage the builder pattern to create imgix image URLs. Using the builder pattern to generate imgix image URLs provides a structured and scalable approach to image manipulation.

This method allows for flexible and efficient URL generation by chaining method calls to create complex image compositions with ease. By chaining method calls, you can add parameters incrementally, creating a more maintainable and readable codebase.

This setup creates a proxy object that handles method chaining and appends query parameters to the URL. The proxyHandler intercepts method calls to add parameters, and appendQueryParam ensures each parameter is correctly formatted and added to the URL. This function also uses paramName and paramValue to append the right elements to the URL query parameter to build the image.

Prerequisites

Before we begin, ensure you have the following:

Implementation

Step 1: Create a buildURL

First, we need a function that initiates the URL building process. This function will be our “Director,” responsible for constructing the URL by appending query parameters.

This setup creates a proxy object that handles method chaining and appends query parameters to the URL. The proxyHandler intercepts method calls to add parameters, and appendQueryParam ensures each parameter is correctly formatted and added to the URL. This function also uses paramName and paramValue to append the right elements to the URL query parameter to build the image.

  export function buildURL({ protocol = "https://", domain }) {
let baseURL;
// ...
const proxyHandler = {
  get(target, prop) {
    return function (paramValue) {
      // ...
      appendQueryParam(prop, paramValue);
      return new Proxy(target, proxyHandler); // allow method chaining
    };
  }
},
 
// Expose the proxy to the parent scope object
const parentScope = new Proxy({}, proxyHandler);
 
return parentScope;
  }

Step 2: Chaining Method Calls

Using the proxy object, we can chain method calls to build our URL with the desired imgix parameters. This approach allows for a clean and readable way to specify multiple parameters. Each method call appends a parameter to the URL.

const image = buildURL({ domain })
  .path("/blog-og-image.png")
  .width(400)
  .height(400)
  .auto("format,compress")
  .fm("jpeg")
  .fit("crop")
  .finalize()
 
console.log(image)
// https://assets.imgix.net/blog-og-image.png?w=400&h=400&fm=auto&fit=crop

Step 3: Handling Special Parameters

For more complex parameters such as mark64 and blend64, which involve base64 encoding, we need specialized methods.

function mark64({ text, ...rest }) {
  // start building the `~text` URL
  const markURl = new URL(`${protocol + domain}/~text`)
 
  // append params to the ~txt URL that we'll encode to base64
  Object.entries(text).forEach(([key, value]) => {
    if (!(key === "txt" || key === "w")) {
      markURl.searchParams.set(`txt-${key}`, value)
    } else {
      markURl.searchParams.set(key, value)
    }
  })
  const mark64 = markURl.toString()
 
  // clear the search params
  baseURL.searchParams.delete("mark64")
  Object.entries(rest).forEach(([key, _value]) => {
    baseURL.searchParams.delete(`mark-${key}`)
  })
 
  // set the new search params
  baseURL.searchParams.set("mark64", Buffer.from(mark64).toString("base64"))
  Object.entries(rest).forEach(([key, value]) => {
    // console.log(`key = ${key}, value = ${value}`);
    baseURL.searchParams.set(`mark-${key}`, value)
  })
 
  return this
}
 
function blend64({ url, ...rest }) {
  // clear the search params
  baseURL.searchParams.delete("blend64")
  Object.entries(rest).forEach(([key, _value]) => {
    baseURL.searchParams.delete(`blend-${key}`)
  })
 
  // set the new search params
  baseURL.searchParams.set("blend64", Buffer.from(url).toString("base64"))
  Object.entries(rest).forEach(([key, value]) => {
    baseURL.searchParams.set(`blend-${key}`, value)
  })
}

This method constructs a ~text URL, encodes it in base64, and appends it to the mark64 parameter. By using base64 encoding, we ensure that special characters in the text and font parameters do not interfere with the URL format.

Step 4: Finalizing the URL

The finalize() method stops the chaining and returns the complete URL. This method is essential for outputting the constructed URL after all parameters have been added.

const proxyHandler = {
  get(target, prop) {
    // 👇 return the URL string
    if (prop === "finalize") {
      return function () {
        return baseURL
      }
    } else {
      return function (paramValue) {
        // ...
        appendQueryParam(prop, paramValue)
        return new Proxy(target, proxyHandler)
      }
    }
  },
}

Example: Creating a Social Media Image by Layering imgix Parameters

In this example, we’re going to create an image for a LinkedIn post. With this method, you can create a wide range of images, including social images, OG images, blog headers, promotional banners, and more. The final output will look like this:

LinkedIn Post Image Example

Layer 1: Setting a Base Image

Let’s create this image by layering parameters on top of each other using the buildURL function. Start with the base image, which serves as the background of our final image.

const layerdURL = buildURL({ domain: "luis-ball.imgix.net" }).path(
  "builder-pattern-base-layer.png",
) // Setting the base path

Layer 2: Adding a Mark64 Parameter

Next, add a mark64 parameter to overlay an image or text on top of the base image. We’ll use a text overlay for this example.

mark64({
  pad: 100,
  align: "top, left",
  text: {
    w: 900,
    color: "fff",
    font64: "avenir",
    size: 112,
    txt: "Transforming Images Using AI Without Leaving Your React App",
  },
})

Layer 3: Adding Another Text Layer

Add another text layer using the txt parameter for additional information.

 .txt({
    align: 'bottom, left',
    color: 'fff',
    font64: 'avenir',
    pad: 100,
    size: 40,
    text64: 'Luis Ball - Senior Software Engineer',
  })

Layer 4: Adding a Blend Parameter

  .blend64({
    mode: 'normal',
    url: `${heroImage}`,
  })

Conclusion

Using the builder pattern to create imgix image URLs offers a powerful and flexible approach to image manipulation. By chaining method calls, you can dynamically build complex image URLs with ease, enhancing your ability to deliver optimized images. Here is a look at the entire code for the LinkedIn image example:

const layerdURL = buildURL({ domain: "luis-ball.imgix.net" })
  .path("builder-pattern-base-layer.png") // Setting the base path
  .bri(-15)
  .mark64({
    pad: 100,
    align: "top, left",
    text: {
      w: 900,
      color: "fff",
      font64: "avenir",
      size: 112,
      txt: "Transforming Images Using AI Without Leaving Your React App",
    },
  })
  .txt({
    align: "bottom, left",
    color: "fff",
    font64: "avenir",
    pad: 100,
    size: 40,
    text64: "Luis Ball - Senior Software Engineer",
  })
  .blend64({
    mode: "normal",
    url: `${heroImage}`,
  })
  .auto("compress,format")
  .fm("jpeg")
  .finalize()
// -> https://luis-ball.imgix.net/builder-pattern-base-layer.png?bri=-15&mark64=aHR0cHM6Ly9sdWlzLWJhbGwuaW1naXgubmV0L350ZXh0P3c9OTAwJnR4dC1jb2xvcj1mZmYmdHh0LWZvbnQ2ND1hdmVuaXImdHh0LXNpemU9MTEyJnR4dD1UcmFuc2Zvcm1pbmcrSW1hZ2VzK1VzaW5nK0FJK1dpdGhvdXQrTGVhdmluZytZb3VyK1JlYWN0K0FwcA%3D%3D&mark-pad=100&mark-align=top%2C+left&txt64=THVpcyBCYWxsIC0gU2VuaW9yIFNvZnR3YXJlIEVuZ2luZWVy&txt-font64=YXZlbmly&txt-align=bottom%2C+left&txt-color=fff&txt-pad=100&txt-size=40&blend64=aHR0cHM6Ly9sdWlzLWJhbGwuaW1naXgubmV0L2J1aWxkZXItcGF0dGVyLXByb2ZpbGUtaW1hZ2UucG5n&blend-mode=normal&fm=auto