What I want
I have very big images in my assets, which slows down the site by a lot for slower networks. (you can read more about the topic on this lighthouse linked page)
- I would like to compress them at build time (
ng build --prod
). - For local development, it is irrelevant (
ng serve
). - Optimally I would like to generate multiple versions for different screen sizes (
example.jpg
→ should become:example_x265.jpg
,example_x128.jpg
, …)
What I have tried
The most promising guide I have found for that is this one here, which describes how to use the imagemin package in combination with the ngx-build-plus package.
Unfortunately, after following the tutorial, I get the following error:
[error] TypeError: Cannot assign to read only property 'main.977fe6373cfd108d.js' of object '#<Object>' at ImageminPlugin._callee2$ (/.../node_modules/imagemin-webpack-plugin/dist/index.js:264:48) at tryCatch (/.../node_modules/babel-runtime/node_modules/regenerator-runtime/runtime.js:62:40) // ...
Is there any way to compress asset images on build?
Angular Version: 13.1.0
Note: I do not want to know how to upload images to third party storage solutions.
I specifically want to create a compressed version of my static assets on build time.
Advertisement
Answer
You can use a gulpfile with either gulp-responsive
or gulp-sharp-responsive
.
I personally use the latter, because it has support for the AVIF
format.
To integrate it nicely with your Angular project, you can follow these steps:
- Install the dependencies:
npm i --save-dev gulp gulp-sharp-responsive
- Create a
gulpfile.js
in your project root with the following content
const { src, dest } = require("gulp"); const sharpResponsive = require("gulp-sharp-responsive"); const compress = () => src("images/*.{png,jpg}") .pipe( sharpResponsive({ formats: [ // jpeg { width: 256, format: "jpeg", rename: { suffix: "-256" } }, { width: 512, format: "jpeg", rename: { suffix: "-512" } }, { width: 1024, format: "jpeg", rename: { suffix: "-1024" } }, // webp { width: 256, format: "webp", rename: { suffix: "-256" } }, { width: 512, format: "webp", rename: { suffix: "-512" } }, { width: 1024, format: "webp", rename: { suffix: "-1024" } }, // avif { width: 256, format: "avif", rename: { suffix: "-256" } }, { width: 512, format: "avif", rename: { suffix: "-512" } }, { width: 1024, format: "avif", rename: { suffix: "-1024" } }, ], }) ) .pipe(dest("src/assets/compressed")); module.exports = { compress, };
- Create a folder in your project root, where your uncompressed image files are located (In this example it is called
images
) - Add a preinstall script to your
package.js
, so that your gulpfile is called on every build
"scripts": { "prebuild": "gulp compress", // ... },
If you call npm run build
now, it will compress your images and move them in the specified assets folder, before actually running ng build
.
Now you can use the image files with a picture
/source
combination like in the following snippet. Keep in mind that the order of the source tags is important.
<!-- {{image}} is the image name --> <picture *ngIf="image"> <!-- avif --> <source srcset="assets/compressed/{{image}}-256.avif" media="(max-width: 512px)" type="image/avif" /> <source srcset="assets/compressed/{{image}}-512.avif" media="(max-width: 1024px)" type="image/avif" /> <source srcset="assets/compressed/{{image}}-1024.avif" media="(max-width: 2048px)" type="image/avif" /> <!-- webp --> <source srcset="assets/compressed/{{image}}-256.webp" media="(max-width: 512px)" type="image/webp" /> <source srcset="assets/compressed/{{image}}-512.webp" media="(max-width: 1024px)" type="image/webp" /> <source srcset="assets/compressed/{{image}}-1024.webp" media="(max-width: 2048px)" type="image/webp" /> <!-- jpeg --> <source srcset="assets/compressed/{{image}}-256.jpg" media="(max-width: 512px)" type="image/jpeg" /> <source srcset="assets/compressed/{{image}}-512.jpg" media="(max-width: 1024px)" type="image/jpeg" /> <source srcset="assets/compressed/{{image}}-1024.jpg" media="(max-width: 2048px)" type="image/jpeg" /> <!-- original --> <img src="assets/compressed/{{ image }}-1024.jpg" /> </picture>