-
Notifications
You must be signed in to change notification settings - Fork 10.3k
docs(gatsby-plugin-image): Add image plugin toolkit docs #29483
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 7 commits
503fb3a
3a39ea1
e1d8938
43d0526
9a6fa53
71bd978
9c2e780
0b739ec
c63b21d
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
| @@ -0,0 +1,261 @@ | ||||||
| --- | ||||||
| title: Adding Gatsby Image support to your plugin | ||||||
| --- | ||||||
|
|
||||||
| The [new Gatsby image plugin](https://www.gatsbyjs.com/docs/how-to/images-and-media/using-gatsby-plugin-image) includes React components for displaying images, and these can be used with data from plugins. The plugin handles all of the hard parts of displaying responsive images that follow best practices for performance. In fact we are confident that it is the fastest way to render images in React, as it can handle blur-up and lazy-loading before React hydration. | ||||||
| Support for these are available out of the box in `gatsby-transformer-sharp`, so if your plugin downloads images and processes them locally then your users can use the [`gatsbyImageData` resolver](https://www.gatsbyjs.com/docs/how-to/images-and-media/using-gatsby-plugin-image#dynamic-images). However, if your CDN can deliver images of multiple sizes with a URL-based API, then the plugin includes a toolkit to allow you to give your users the same great experience without needing to download the images locally. It also allows you to create components that display these images dynamically at runtime, without needing to add a GraphQL resolver. | ||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. these components? |
||||||
|
|
||||||
| ## Adding a `gatsbyImageData` GraphQL resolver | ||||||
|
|
||||||
| You can give your users the best experience by adding a `gatsbyImageData` resolver to your image nodes. This allows you to generate low-resolution or traced SVG images as inline data URIs, so your users can have blurred placeholders. You can also calculate the image's dominant color for an alternative placeholder. These are the same placeholders that are included with `gatsby-transformer-sharp`, and will give the best experience for your users. If you are able to deliver these directly from your CMS or other data source then this is ideal, but otherwise you can use helper functions included in `gatsby-plugin-sharp`. | ||||||
|
|
||||||
| There are three steps to add a basic `gatsbyImageData` resolver: | ||||||
|
|
||||||
| 1. [Create the `generateImageSource` function](#create-the-generateimagesource-function) | ||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I might put this earlier to help give context? Not sold on that approach though. |
||||||
| 2. [Create the resolver function](#create-the-resolver-function) | ||||||
| 3. [Add the resolver](#add-the-resolver) | ||||||
|
|
||||||
| ### Create the `generateImageSource` function | ||||||
|
|
||||||
| The `generateImageSource` function is where you generate your image URLs. The image plugin calculates which sizes and formats are needed, according to the format, size and breakpoints requested by the user. For each of these, your function is passed the base URL, width, height and format (i.e. the image filetytpe), as well as any custom options that your plugin needs. You then return the generated URL. The returned object also includes width, height and format. This means you can return a different value from the one requested. For example, if the function requests an unsupported format or size, you can return a different one which will be used instead. | ||||||
|
|
||||||
| ```js:title=gatsby-source-example/gatsby-node.js | ||||||
| // In this example we use a custom `quality` option | ||||||
| const generateImageSource = (baseURL, width, height, format, fit, options) => { | ||||||
| const src = `https://myexampleimagehost.com/${baseURL}?w=${width}&h=${height}&fmt=${format}&q=${options.quality}` | ||||||
| return { src, width, height, format } | ||||||
| } | ||||||
| ``` | ||||||
|
|
||||||
| ### Create the resolver function | ||||||
|
|
||||||
| You then can use the function created in the previous step to build your resolver function. It can be an async function, and it should return the value from `generateImageData`. An example resolver could look like this: | ||||||
ascorbic marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||||||
|
|
||||||
| ```js::title=gatsby-source-example/gatsby-node.js | ||||||
ascorbic marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||||||
|
|
||||||
| import { generateImageData, getLowResolutionImageURL } from "gatsby-plugin-image" | ||||||
|
|
||||||
|
|
||||||
| const resolveGatsbyImageData = async (image, options) => { | ||||||
| // The `image` argument is the node to which you are attaching the resolver, | ||||||
| // so the values will depend on your data type. | ||||||
| const filename = image.src | ||||||
|
|
||||||
| const sourceMetadata = { | ||||||
| width: image.width, | ||||||
| height: image.height, | ||||||
| // In this example, the node has a value like "image/png", which needs | ||||||
| // converting to a value such as "png". If this omitted, the funciton will | ||||||
| // attempt to work it out from the file extension. | ||||||
| format: image.mimeType.split("/")[1] | ||||||
ascorbic marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||
| } | ||||||
|
|
||||||
| const imageDataArgs = { | ||||||
| ...options, | ||||||
| // Passing the plugin name allows for better error messages | ||||||
| pluginName: `gatsby-source-example`, | ||||||
| sourceMetadata, | ||||||
| filename, | ||||||
| placeholderURL | ||||||
| generateImageSource, | ||||||
| options, | ||||||
| } | ||||||
|
|
||||||
| // Generating placeholders is optional, but recommended | ||||||
| if(options.placeholder === "blurred") { | ||||||
| // This function returns the URL for a 20px-wide image, to use as a blurred placeholder | ||||||
| // You need to download the image and convert it to a base64-encoded data URI | ||||||
| const lowResImage = getLowResolutionImageURL(imageDataArgs) | ||||||
|
|
||||||
| // This would be your own function to download and generate a low-resolution placeholder | ||||||
| imageDataArgs.placeholderURL = await getBase64Image(lowResImage) | ||||||
| } | ||||||
|
|
||||||
| // You could also calculate dominant color, and pass that as `backgroundColor` | ||||||
| // gatsby-plugin-sharp includes helpers that you can use to generate a tracedSVG or calculate | ||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm not exactly sure what these helpers are, maybe it could be possible to link to some docs/code (wherever it exists?)
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, I need to document these! |
||||||
| // the dominant color of a local file, if you don't want to handle it in your plugin | ||||||
|
|
||||||
|
|
||||||
| return generateImageData(imageDataArgs) | ||||||
| } | ||||||
|
|
||||||
| ``` | ||||||
|
|
||||||
| ### Add the resolver | ||||||
|
|
||||||
| You should register the resolver using the [`createResolvers` API hook](https://www.gatsbyjs.com/docs/reference/config-files/gatsby-node/#createResolvers). `gatsby-plugin-image/graphql-utils` includes an optional utility function to help with this. It registers the resolver with all of the base arguments needed to create the image data, such as width, aspect ratio, layout and background color. These are defined with comprehensive descriptions that are visible when your users are building queries in GraphiQL. You can pass additional arguments supported by your plugin, for example image options such as quality. However if you want complete control over the resolver args, then you can create it yourself from scratch. We recommend keeping the args similar to the default, as this is what users will be expecting, and it means you benefit from the plugin documentation. At a minimum, you should always expose `layout`, `width` and `height` as args. | ||||||
ascorbic marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||||||
|
|
||||||
| The arguments: | ||||||
|
|
||||||
| - `resolverFunction`: the resolver function that you created at step 1. It receives the node and the arguments and should return the image data object. | ||||||
ascorbic marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||||||
| - `additionalArgs`: an object defining additional args, in the same format used by [Gatsby Type Builders](https://www.gatsbyjs.com/docs/reference/graphql-data-layer/schema-customization/#gatsby-type-builders) | ||||||
|
|
||||||
| For example, to add a `gatsbyImageData` resolver onto a `ProductImage` node that you have previously defined: | ||||||
|
|
||||||
| ```js:title=gatsby-source-example/gatsby-node.js | ||||||
| // Note the different import | ||||||
| import { getGatsbyImageResolver } from "gatsby-plugin-image/graphql-utils" | ||||||
|
|
||||||
| export function createResolvers({ createResolvers }) { | ||||||
| createResolvers({ | ||||||
| ProductImage: { | ||||||
| // loadImageData is your custom resolver, defined at step 2 | ||||||
ascorbic marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||||||
| gatsbyImageData: getGatsbyImageResolver(loadImageData, { | ||||||
| quality: "Int", | ||||||
| }), | ||||||
| }, | ||||||
| }) | ||||||
| } | ||||||
| ``` | ||||||
|
|
||||||
| ## Adding a custom image component | ||||||
|
|
||||||
| If you have a URL-based image API, you can create a custom image component that wraps `<GatsbyImage />` and displays images generated at runtime. If you have a source plugin, this could accept your native image object, but it could equally just take a base URL and generate the image based on that. This is a good solution for image CDNs that aren't handling their own CMS data, and can generate a transformed image from just a source URL and dimensions. | ||||||
ascorbic marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||||||
|
|
||||||
| There are three steps to create a custom image component: | ||||||
|
|
||||||
| 1. [Create your URL builder function](#create-your-url-builder-function) | ||||||
| 2. [Create your image data function](#create-your-image-data-function) | ||||||
| 3. [Create your wrapper component](#create-your-wrapper-component) (optional) | ||||||
|
|
||||||
| ### Create your URL builder function | ||||||
|
|
||||||
| This is similar to the `generateImageSource` described above, but just returns a URL string. This is an example for the same image host: | ||||||
ascorbic marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||||||
|
|
||||||
| ```js | ||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should this go in a particular file?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Not really. It could be in gatsby-node, but is normally in another file |
||||||
| function urlBuilder({ baseUrl, width, height, format, options }) { | ||||||
| return `https://myexampleimagehost.com/${baseURL}?w=${width}&h=${height}&fmt=${format}&q=${options.quality}` | ||||||
| } | ||||||
| ``` | ||||||
|
|
||||||
| If your host supports it, we recommend using auto-format to deliver next-generation image formats to supported browsers. In this case, ignore the `format` option. | ||||||
ascorbic marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||||||
|
|
||||||
| ### Create your image data function | ||||||
|
|
||||||
| This is a similar to the image resolver described above. However because it executes in the browser, the functions that you use should be fast, and synchronous, and you can't use node APIs. You will not be downloading and generating base64 placeholders, for example, or calculating dominant colors. If you have these values pre-calculated then you can pass these in and use them, but they need to be available in the props that you pass to the function at runtime. | ||||||
|
||||||
| This is a similar to the image resolver described above. However because it executes in the browser, the functions that you use should be fast, and synchronous, and you can't use node APIs. You will not be downloading and generating base64 placeholders, for example, or calculating dominant colors. If you have these values pre-calculated then you can pass these in and use them, but they need to be available in the props that you pass to the function at runtime. | |
| This is similar to the image resolver described above. However, because it executes in the browser, the functions that you use should be fast and synchronous. That means you can't use node APIs. You will not be downloading and generating base64 placeholders, for example, or calculating dominant colors. If you have these values pre-calculated then you can pass them in, but they need to be available in the props that you pass to the function at runtime. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not quite. The reason you can't use Node APIs is that you're in the browser.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should just be grammar stuff. Does is change the meaning of the original text to you in some way? If it's only the comma versus period with the "that means" that's fine. Would be nice to break up the sentence a bit more if possible.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm going to rewrite it
Uh oh!
There was an error while loading. Please reload this page.