An image plugin for React Static that makes it easy to transform images, provide them to a route, and utilize them in static pages. The plugin supports lazy loaded images, placeholders, and responsive loading.
To use the plugin:
- Add the plugin to React Static in static.config.js
- Import {Images} from 'plugin-react-static-images' in static.config.js and use
let myImage = await Images.get(sources, transformName, options)
to ingest images and apply transformations - Pass the returned images to your route as you would any other data
- Import {ImageComponent} from 'plugin-react-static-images/components' in your page and use it in your JSX:
<ImageComponent img={myImage} />
Additional detail for each step follows.
Add the plugin to React Static's plugins like so:
plugins: [
...other plugins...
'plugin-react-static-images'
]
In static.config.js, import the images object:
import {Images} from 'plugin-react-static-images'
Then, use the Images.get function to retrieve images and apply transformations. The function has this signature:
await Images.get(sources, transformName, options)
Sources
Sources is either a single object, or an array of objects, consisting of the following objects:
{type: "folder", value: pathString}
{type: "file", value: pathString}
{type: "url", value: urlString}
If you supply a single source, then Images.get will return a single image. If you supply an array of sources, then Images.get will return an array of images. If your source is a folder, all images (.jpg, .jpeg, .png, .bmp, .tiff) that are located in the folder will be ingested.
TransformName
For transformName, there is an available default transform available called "builtin" that supports a wide range of transformation options. If desired, you can also create your own custom transform and use it.
To use the default transform, supply "builtin"
Options
For the options object, you can provide the following options if you use the "builtin" transform:
Option | Value | Description |
---|---|---|
usePlaceholder | true/false | If true, a placeholder image is generated for each ingested image. Defaults to false. |
useResponsive | true/false | If true, a set of responsive images is generated for each ingested image and an appropriate image to display will be determined at runtime, including switching to higher resolution images, where available, as the size of the displayed image increases. Defaults to false. |
placeholderMaxDimension | number (pixels) | Placeholder images are generated by scaling down the ingested images. If an ingested image is particularly large to start, the placeholder image may also be very large. Setting this option to a number of pixels will cap the size of the placeholder along the longest dimension. Default is not set. |
maxDimension | number (pixels) | Images that are ingested may be excessively large. Set this option to cap the number of pixels along each image's longest dimension. For example, if you set this option to 2000, then images will not exceed 2000 pixels wide or 2000 pixels tall (depending upon which dimension is longest). |
placeholderQuality | 1-100 | If ingested images are JPEGs, setting this option will determine the JPEG quality of the placeholder images. Defaults to 100. |
quality | 1-100 | If ingested images are JPEGs, setting this option will determine the JPEG quality of the image. Defaults to 100. |
load | "immediate" or "lazy" | If set to immediate, images will load on page load. If set to lazy, images will load when they enter the viewport. This option can be set independently of whether you've set usePlaceholder to true. |
grayscale | true/false | Transform ingested images to grayscale. |
Example 1
The returned image will use a placeholder but the image will also load immediately and the placeholder will be replaced shortly after the page loads. A single image is returned.
let image = await Images.get({ type: "file", value: "./images/image1.jpg" }, "builtin", { usePlaceholder: true })
Example 2
Placeholders will be utilized but images will load lazily. That is, image placeholders will be displayed and, as those placeholders enter the viewport, they will be replaced with the target images. An array of images is returned.
let images = await Images.get({ type: "folder", value: "./images/"}, "builtin", { usePlaceholder: true, load: "lazy" })
Example 3
Both a file and a folder are ingested and no transform is utilized. In this case, images will be ingested but no transformations applied. However, images will still be returned from the function call, which allows you to pass them to your route.
let images = await Images.get([{ type: "file", value: "./images/folder1/image1.png" }, { type: "folder", value: "./images/"})
Example 4
Placeholders will be generated and images will load responsively. So, if your image is set to 90%, as the image scales up, the underlying image src will be updated with a higher resolution image if available.
let images = await Images.get({ type: "folder", value: "./images/" }, "builtin", { usePlaceholder: true, useResponsive: true })
The plugin does not make assumptions about image quality, size, or other factors. When ingesting images, you may want to set the quality and maxDimension options as these options can significantly impact the size of your images. Additionally, the useResponsive option provides dynamically chosen image sizes at the cost of longer build times since additional images have to be created.
Sending your images to your route works like sending other data. In static.config.js:
let myImage = await Images.get({ type: "file", path: "your_path" }, "builtin", { usePlaceholder: true })
return [{
path: '/blog',
getData: () => ({
posts,
myImage
})
}]
In the page for your route, assign your images to variables via useRouteData as usual:
const {posts, myImage} = useRouteData()
Import the ImageComponent:
import {ImageComponent} from 'plugin-react-static-images/components'
Use the ImageComponent component in your JSX. For example:
return(<div>
<ImageComponent img={myImage} style={{width:500}} />
</div>)
If you have an array of images and want to access a single image:
return(<div>
<ImageComponent img={myImages[0]} />
</div>)
An array of images is a standard array, and an ImageComponent is a standard React component, so you could easily create an array of image components for use in your page:
let imageComponents = []
myImages.forEach((value, i) => { imageComponents.push(<ImageComponent key={i} img={value} />) }
The <ImageComponent>
is wrapper around an underlying standard <img>
element. All props that aren't related to the ImageComponent are passed to the underlying <img>
element.
Responsive Image Considerations
Each image that utilizes the responsive image feature has multiple images created at different sizes. Depending on how you intend to display an image, the fact that there are different sized images may have unexpected results. For instance, using a regular img
element, if you're starting with a very large image and you set a maxWidth that's a percentage, and a maxHeight that's a pixel value, you might expect that the image will automatically fill the height that you've specified. With responsive images, however, the maximum size of the image that's displayed at runtime may not be large enough to achieve a full fill of the component, so you would need to additionally set a minHeight to achieve a similar result.
The builtin transform should be sufficient for many use cases. In combination with Images.get, your static site can ingest numerous images and apply transformations to them. You can also utilize multiple Images.get operations in your static.config.js file to apply different transformations to different sets of images.
You might find a use case for which you want to create your own transform, however.
To do so, simply create a new file and export a default function like so:
module.exports.default = ({image, options}) => {
}
The function will receive a TransformableImage
object, and the options passed to Images.get.
The TransformableImage
object provides the following operations:
Function | Description |
---|---|
getFileName() | Returns the current filename without extension |
getExtension() | Returns the extension of the filename |
setFileName(fileName) | Sets the filename |
grayscale() | Converts to grayscale |
blur(amount) | Blurs |
resizeWidth(width) | Resizes width while maintaining proportional height |
resizeHeight(height) | Resizes height while maintaining proportional width |
resize(width, height) | Resizes width and height regardless of proportionality |
scale(amount) | Scales the image by a given amount. 1.0 is original size. |
toBase64() | Returns a base64 data uri |
getWidth() | Returns the width of the image |
getHeight() | Returns the height of the image |
getSize() | Returns the size of the image |
setQuality(amount) | Sets compression quality if JPEG (1-100) |
getOperationsHistory() | Returns operations that have been applied to the image |
Your custom transform must return an object:
return {
image: TransformableImage object
placeholder: TransformableImage object (optional)
responsive: array of TransformableImage objects (optional)
load: "immediate" or "lazy" (optional)
}
Next, simply pass an option to the plugin in static.config.js like so:
plugins: [
[
'plugin-react-static-images',
{
transforms: [
{ name: "yourTransformName", location: path.resolve("your_transform_path") }
]
}
]
]
Then, instead of referencing builtin
in Images.get, you can reference the name of your transformation.
You can pass an option, 'maxAssetSize', to the plugin to determine whether images are base64 encoded and sent via route data, or accessed via a url. Up to and including maxAssetSize, images will be base64 encoded. Above maxAssetSize, images will be referenced via url. The default is 100000.
plugins: [
[
'plugin-react-static-images',
{
maxAssetSize: 50000
}
]
]