Generating Social Images with imgix
In this guide, we'll explore how to leverage the builder pattern (opens in a new tab) 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:
- An imgix account (opens in a new tab)
- Basic understanding of JavaScript and imgix parameters
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:
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