react-strapi-img
v0.2.3
Published
react-strapi-img is a wrapper for images, that handles responsive sizes, lazyloading and loading-animation. It is built to consume the image-data from Strapi.
Downloads
352
Maintainers
Readme
react-strapi-img
react-strapi-img is a wrapper for img
, that handles webp-support, responsive sizes, lazyloading and loading-animation. It is optimized to consume image-data from Strapi
, but can be useful in other contexts as well.
🦊 Take a look at the example.
⚠️ This library is in beta-state, which means its API still can change a bit without warning. Everything is working great so far, but it still needs some testing. As soon as I tested it in production and gathered some feedback, I will release verson 1.0.0
. Feel free to join and improve!
What it does
- Wrap image in proportional container, to preserve and determine height.
- Lazyload image.
- Transform Strapi image-
formats
-object intosrcset
. - Add Blured, animated base64-placeholder.
- Use
WebP
-format if supported. - Add
noscript
-image for SEO. - Decode images before rendering for better performance.
PeerDependencies
- react: >= 16.8.0,
- react-dom: >= 16.8.0,
- styled-components: >= 5.2.0
Why another image-loader?
I could not find an existing solution, that connects resized images from Strapi
with NextJS
conveniently and meets all my requirements.
Install
yarn add react react-dom styled-components react-strapi-img
npm install -S react react-dom styled-components react-strapi-img
Setup image-resizing in Strapi
To gain the efficency of srcset
, copy the folder services to the Strapi-folder /extensions/upload
. The scripts will resize every uploaded image:
- to
base64
- to sizes from
400px
to theoriginal image width
in steps of200px
– maximum is3000px
— the original image-size will be added as largest breakpoint
This method is gratefully adapted from here: https://sarpisik.com/blog/how-to-generate-different-image-formats-with-strapi-plugin-upload-part-ii
In react
fetch the image with graphQL
:
query {
image {
url
alternativeText
width
height
formats
}
}
Usage
After setting up Strapi
and uploading some images, use them in your react-components:
import React from "react";
import Image, { Types } from "react-strapi-img";
interface Props {
imageFromStrapi: Types.ImageProps;
}
const MyApp: React.FC<Props> = ({ imageFromStrapi }) => {
const { url, alternativeText, width, height, formats } = imageFromStrapi;
return (
<Image
// you could also spread all props like this: {...imageFromStrapi},
// but for the purpose of demonstration I am adding them one by one
url={url} // required
alternativeText={alternativeText} // optional
width={width} // optional
height={height} // optional
formats={formats} // optional
prefix={
process.env.NODE_ENV === "production"
? "https://api.myapp.net"
: "http://localhost:1337"
} // optional
/>
);
};
export default MyApp;
Props
Except url
all props are optional.
| Name | Type | Default | Description |
| :--------------------- | :------- | :---------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| url | string | | Image-url. Fetch with Strapi. |
| formats | object | | Strapi provides a formats-object for srcset
and base64
. Fetch it and insert it here. |
| sizes | string | | sizes-Tag. Help browsers to make better decisions. https://css-tricks.com/a-guide-to-the-responsive-images-syntax-in-html/ |
| objectFit | string | 'cover' | CSS-property. Useful together with proportionalHeight
|
| objectPosition | string | 'center' | CSS-property. Useful together with proportionalHeight
|
| width | number | | Provided by Strapi. Pass it to preserve original image-proportions. |
| height | number | | Provided by Strapi. Pass it to preserve original image-proportions. |
| proportionalHeight | number | | Provide for custom image-proportion. Crops image. Use along with objectFit
and objectPosition
. |
| placeholder | boolean | true
| Show base64
-Placeholder. |
| rootMargin | string | 50px | Used by Intersection Observer
to determine distance from viewport, when the image should be loaded |
| threshold | number | 0 | Value between 0
and 1
. Used by Intersection Observer
to indicate at what percentage of the target's visibility the observer's callback should be executed. |
| alternativeText | string | | alt
-Attribute of the image. Provided by Strapi
. Pass it for good SEO. |
| className | string | | Custom className for the wrapping div
-tag. |
| style | string | | Custom styles for wrapper. styled-components
template-literal. |
| stylePlaceholder | string | | Custom styles for placeholder-img. styled-components
template-literal |
| styleImg | string | | Custom styles for img-tag. styled-components
template-literal |
| prefix | string | | Prefix all src and srcset. |
| onLoad | function | | Image-onLoad
-callback. |
| onError | function | | Image-onError
-callback. |
ImageProvider
Optionally you can wrap your App in the component ImageProvider
, which lets you determine repeating settings at a central spot. Have a look at this _app.tsx
from a nextJS
-project:
import React from "react";
import { ImageProvider } from "react-strapi-img";
function MyApp({ Component, pageProps, router }) {
return (
<ImageProvider
prefix={process.env.productionPath}
style={`border: 10px solid red;`}
onLoad={(event) => console.log(event.target)}
>
<Component {...pageProps} key={router.route} />
</ImageProvider>
);
}
export default MyApp;
Additionally the ImageProvider
detects webp
-support once, which gives the Image
-Components a tiny performance boost.
ImageProvider
-Props
All props are optional. You can find them here at "ContextProps".
TypeScript-Types
All relevant Types are exported:
- ImageProps
- ProviderProps
- Formats
- ObjectFit
import { Types } from "react-strapi-img";
interface Props {
imageFromStrapi: Types.ImageProps;
}
const MyApp: React.FC<Props> = ({ imageFromStrapi }) => {
return <Image {...imageFromStrapi} />;
};
export default MyApp;
Rendered Output
Initially:
<div class="Wrapper__StyledImageWrapper-sc-1o399dd-0 cqCfje imageWrapper">
<img
src="data:image/jpeg;base64,/9j/2wBDAAYEBQYFBAYGBQYHBwYIChAKCgkJChQODwwQFxQYGBcUFhYaHSUfGhsjHBYWICwgIyYnKSopGR8tMC0oMCUoKSj/2wBDAQcHBwoIChMKChMoGhYaKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCj/wAARCAANABQDASIAAhEBAxEB/8QAGAAAAwEBAAAAAAAAAAAAAAAAAAUHAgb/xAAlEAACAQMCBgMBAAAAAAAAAAABAgMABBEFEgYHEyExQSIycVH/xAAVAQEBAAAAAAAAAAAAAAAAAAADBP/EABgRAAMBAQAAAAAAAAAAAAAAAAABEQID/9oADAMBAAIRAxEAPwCT8sZ4o9Q055XZFF1uJz5AFdhzB4mt7rW4hAz7OiBJj3lk7iopZaleWqp0LiRCjb0w31b+/tYuNRurmXfPNI7gEZLevOKmfJvVo2esUg24guVXU5Pl3IGf3GKKQyvvfc+ST7JoplmIN6rp/9k="
alt='Placeholder for the image "testimg_1d61597ba3.jpg".'
class="Placeholder__StyledPlaceholder-h5tses-0 grjLdz"
/>
<img
src="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw=="
alt="This is testimg_1d61597ba3.jpg"
class="StyledImage-yw93u6-0 bvBGFq no-js-testimg_1d61597ba3"
/>
<noscript>
<style>
.no-js-testimg_1d61597ba3 {
display: none !important;
}
</style>
<img
src="testimg_1d61597ba3.jpg"
alt="This is testimg_1d61597ba3.jpg"
class="StyledImage-yw93u6-0 bvBGFq"
/>
</noscript>
</div>
After image was loaded:
<div class="Wrapper__StyledImageWrapper-sc-1o399dd-0 cOGFGG imageWrapper">
<img
src="testimg_1d61597ba3.jpg"
alt="This is testimg_1d61597ba3.jpg"
class="StyledImage-yw93u6-0 bvBGFq no-js-testimg_1d61597ba3"
srcset="
/400_testimg_1d61597ba3.webp 400w,
/600_testimg_1d61597ba3.webp 600w,
/800_testimg_1d61597ba3.webp 800w,
/1000_testimg_1d61597ba3.webp 1000w,
/1200_testimg_1d61597ba3.webp 1200w,
/1400_testimg_1d61597ba3.webp 1400w,
/1600_testimg_1d61597ba3.webp 1600w,
/1800_testimg_1d61597ba3.webp 1800w,
/2000_testimg_1d61597ba3.webp 2000w,
/2200_testimg_1d61597ba3.webp 2200w,
/2400_testimg_1d61597ba3.webp 2400w,
/2600_testimg_1d61597ba3.webp 2600w,
/2800_testimg_1d61597ba3.webp 2800w,
/3000_testimg_1d61597ba3.webp 3000w
"
/>
<noscript>
<style>
.no-js-testimg_1d61597ba3 {
display: none !important;
}
</style>
<img
src="testimg_1d61597ba3.jpg"
srcset="
/400_testimg_1d61597ba3.webp 400w,
/600_testimg_1d61597ba3.webp 600w,
/800_testimg_1d61597ba3.webp 800w,
/1000_testimg_1d61597ba3.webp 1000w,
/1200_testimg_1d61597ba3.webp 1200w,
/1400_testimg_1d61597ba3.webp 1400w,
/1600_testimg_1d61597ba3.webp 1600w,
/1800_testimg_1d61597ba3.webp 1800w,
/2000_testimg_1d61597ba3.webp 2000w,
/2200_testimg_1d61597ba3.webp 2200w,
/2400_testimg_1d61597ba3.webp 2400w,
/2600_testimg_1d61597ba3.webp 2600w,
/2800_testimg_1d61597ba3.webp 2800w,
/3000_testimg_1d61597ba3.webp 3000w
"
alt="This is testimg_1d61597ba3.jpg"
class="StyledImage-yw93u6-0 bvBGFq"
/>
</noscript>
</div>
Contributing
Every contribution is very much appreciated. You are invited to join me in making this software reliable and performant for everyone.
If react-strapi-img
is helpful for you,
don't hesitate to star it on
GitHub.
License
Licensed under the MIT License, Copyright © 2020-present Andreas Faust. See LICENSE for more information.
Thanks
I want to give special thanks to Sarp for the Strapi
-groundwork and Welly for his library react-cool-img, from which I learned and adapted a lot.