Overview
This RFC addresses a need for straightforward image manipulation using Prismic’s Imgix integration.
What and/or who is affected by this RFC?
This affects all Prismic users using JavaScript in their projects with images.
The @prismicio/helpers
library is also affected by this RFC.
Libraries that use @prismicio/helpers
, such as @prismicio/react
and @prismicio/vue
, may also be affected as a result of needing to provide new helpers, but are outside the scope of this RFC.
Why is this being proposed?
Most users are not aware of Imgix’s capabilities and are not taking advantage of it (no data to back this up; just a hypothesis). We can enable users to make full use of Imgix’s features by providing an easy to use API.
Background Information
What is the current situation?
Today, all users have access to Imgix image transformations for all images uploaded to their Prismic repositories. Transformations are performed via URL parameters. For example, an image can be resized to 400px wide with the w
parameter:
https://images.prismic.io/qwerty/image.png?w=400
The Prismic documentation currently recommends using manual string manipulation.
Apply your own transformations
Because images are served through Imgix, you can add any other Imgix transformations that you like. Using string handling, remove, add, or modify the parameters in the URL.
const imageUrl = `https://images.prismic.io/slicemachine-blank/dcea6535-f43b-49a7-8623-bf281aaf1cb2_roller-skating.png?auto=compress,format&rect=255,0,1536,1536&w=500&h=500`
pixelizedImageUrl = imageUrl + `&px=10`
console.log(pixelizedImageUrl)
Source: https://prismic.io/docs/core-concepts/image#apply-your-own-transformations
Imgix provides a few libraries to make this process easier, such as @imgix/js-core
and react-imgix
. Rather than manipulating strings manually, a user can use these libraries to build URLs with a nicer API. For example:
const client = new ImgixClient({
domain: 'testing.imgix.net',
});
const url = client.buildURL('folder/image.jpg', {
w: 1000,
});
// => https://testing.imgix.net/folder/image.jpg?w=1000
What are the current problems?
String manipulation is fragile and requires users to know exactly how the end URL should appear. This is a leaky abstraction; a user must understand Imgix to make use of image transformations. A user must also be careful to craft a correct and valid URL.
When working with a CMS where editors can change content, including image URLs and the URL parameters attached to them, building custom image URLs by hand can quickly become frustrating.
The API for Imgix’s core library is awkward to use since it requires instantiating a client and manually separating the URL’s domain (testing.imgix.net
) from the image’s path (folder/image.jpg
). Prismic users should not have to care about this since images are always coming from images.prismic.io
.
Imgix’s libraries are also relatively large and heavy since they need to support many different use cases. Prismic is typically used for public facing websites where library size is a strong consideration when deciding to integrate a feature. As a result, importing relatively large libraries for string manipulation is undesirable.
Are there any related discussions from elsewhere?
This proposal was sparked by a question from @samlfair. Repeated by @a-trost:
Sam brought up that the url that people get back from prismic for images already has certain things like compression baked in, so to change it they have to do some string manipulation. We walk them through this in the docs, but it's clearly not a great way to go about it. We have a lot of potential with imgix that I don't think most of our users are leveraging. I remember you were making an imgix-lite package for Gatsby before. What's the status with that? And might we want to create a helper or something for working with imgix images, mainly to hack around their SDKs being sub-optimal?
Proposal
Assume @prismicio/helpers
is imported as prismicH
in the following code blocks.
Applying image transformations should be as simple as a single function call to transformImageURL()
:
const url = prismicH.transformImageURL(document.data.myImage.url, {
width: 400
})
// => https://images.prismic.io/my-repo-name/my-image.png?w=400&auto=format,compress
Removing parameters, such as auto
(which is automatically applied to all Prismic images), should be straightforward as well:
const url = prismicH.transformImageURL(document.data.myImage.url, {
auto: undefined
})
// => https://images.prismic.io/my-repo-name/my-image.png
Building responsive images using srcset
with <img>
could be generated with a function called buildImageSrcSet()
:
const srcset = prismicH.buildImageSrcSet(document.data.myImage.url, {
sizes: [400, 800, 1600]
})
// => https://images.prismic.io/my-repo-name/my-image.png?w=400 400w,
// https://images.prismic.io/my-repo-name/my-image.png?w=800 800w,
// https://images.prismic.io/my-repo-name/my-image.png?w=1600 1600w
Similarly, building a srcset
using display pixel densities instead could be generated with a function called buildImageDPRSrcSet()
(DPR = device pixel ratio):
const srcset = prismicH.buildImageDPRSrcSet(document.data.myImage.url, {
ratios: [1, 2, 3]
})
// => https://images.prismic.io/my-repo-name/my-image.png?dpr=1,
// https://images.prismic.io/my-repo-name/my-image.png?dpr=2 2x,
// https://images.prismic.io/my-repo-name/my-image.png?dpr=2 3x
As mentioned earlier, these helper functions could make their way into integration libraries like @prismicio/react
and @prismicio/vue
. Such an API requires more thought and discussion and is outside the scope of this proposal.
How it could be implemented
The core of the proposed functions is a simple URL manipulation function. It uses the native URL and URLSearchParams Web APIs to add, remove, and modify URL parameters.
// Assume ImgixURLParams is an interface of all Imgix parameters
export const buildURL = (url: string, params: ImgixURLParams): string => {
const instance = new URL(url);
for (const camelCasedParamKey in params) {
const paramKey = paramCase(camelCasedParamKey);
const paramValue = params[camelCasedParamKey as keyof typeof params];
if (paramValue === undefined) {
instance.searchParams.delete(paramKey);
} else if (Array.isArray(paramValue)) {
instance.searchParams.set(paramKey, paramValue.join(","));
} else {
instance.searchParams.set(paramKey, `${paramValue}`);
}
}
// Ensure the `s` parameter is the last parameter, if it exists.
// @see https://github.com/imgix/imgix-blueprint#securing-urls
const s = instance.searchParams.get("s");
if (s) {
instance.searchParams.delete("s");
instance.searchParams.append("s", s);
}
return instance.toString();
};
Imgix provides a package named imgix-url-params
that contains all valid URL parameters and their types. It comes in JSON format. With this, TypeScript types can be generated automatically with friendly comments. The parameters can be autocompleted with helpful links to Imgix’s documentation built in.
The srcset
functions would use this core function to build a string. Their implementation should be straightforward and is not included in this RFC.
Supporting a general purpose Imgix library
The proposed helpers are not specific to Prismic. They are valid for any Imgix user. As such, we could treat these helpers as “general purpose” and make them available to anyone as a community open source project.
Such a library could support Imgix features that Prismic users would not use, such as building secure signed URLs and Web Proxy image sources. This could effectively replace Imgix’s official core library, @imgix/js-core
, with a simpler, leaner API (at the expense of possibly not having 100% compatibility with Imgix).
Similar unofficial libraries exist, such as Unsplash’s ts-imgix
(https://github.com/unsplash/ts-imgix). None take the approach of completely replacing Imgix’s core library with near 100% feature support.
This general purpose library is being developed as part of the exploratory phase of this RFC. For the purposes of this RFC, assume functions like transformImageURL()
use this general purpose library in its implementation.
How to provide feedback
Anyone familiar with Prismic’s image handling or wanting an easier image story can provide feedback.
If you have any thoughts, please reply to this issue.
Everything is open for discussion, but the following points are most important:
- What is your opinion on the proposed function names? Do you have suggestions for better names or comments to consider?
transformImageURL(url, params)
buildImageSrcSet(url, options)
buildImageDPRSrcSet(url, options)
- Do you think the proposed APIs could be better?
- Are there any other helpers that could make displaying images from Prismic easier?
- Should we invest time in a new, simpler open source library for general Imgix users (i.e. non-Prismic users)?
Thanks! 🙂