Skip to content

Conversation

@azat-io
Copy link

@azat-io azat-io commented May 31, 2025

Changes

  • Added svg.optimize and svg.svgoConfig configuration options
  • Integrated SVGO optimization in the SVG processing pipeline
  • Updated type definitions and config schema
  • Added error handling with fallback to original SVG content

Testing

Added unit tests.

Benefits

  • Reduces SVG file sizes automatically during build
  • Configurable optimization with full SVGO plugin support
  • Backward compatible - optimization is opt-in
  • Graceful error handling with fallback

Astro planned to add SVGO earlier:
#12067

Docs

I'll create another PR to docs.

@github-actions github-actions bot added pkg: astro Related to the core `astro` package (scope) docs pr labels May 31, 2025
@codspeed-hq
Copy link

codspeed-hq bot commented May 31, 2025

CodSpeed Performance Report

Merging #13880 will not alter performance

Comparing azat-io:feat/svgo (8986051) with main (9be54c7)

Summary

✅ 6 untouched

@changeset-bot
Copy link

changeset-bot bot commented May 31, 2025

🦋 Changeset detected

Latest commit: 8986051

The changes in this PR will be included in the next version bump.

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@ascorbic
Copy link
Contributor

ascorbic commented Jun 4, 2025

Thanks! This is a new feature, so we will need to discuss it a bit before merging, and it will need to wait for a minor release. There is a chance we will want to put this behind an experimental flag at first too. In the meantime, could you add a changeset? It's used to generate the changelog, so make sure you include enough detail to explain what it is and how it works for new users. Think of it as the first bit of docs.

Copy link
Member

@delucis delucis left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the PR @azat-io! I think this could be a nice feature.

Left a couple of questions based on a quick look.

Also pinging @natemoo-re and @stramel as our resident SVG experts who might have good insights here!

svg: z
.object({
optimize: z.boolean().default(true),
svgoConfig: z.record(z.any()).optional(),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should offer at least proper typing here even if we don’t do full schema validation. For example, if a type is available for the SVGO options, we could use something like:

			svgoConfig: z.custom<SVGOOptions>((value) => value && typeof value === 'object').optional(),

That way users should get a nice editor experience.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done ✅

* @description
* Whether to enable SVG optimization using SVGO during build time.
*
* When enabled, all imported SVG files will be optimized for smaller file sizes
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

all imported SVG files

Will this also apply to usage with Astro’s assets pipeline? For example usage like:

---
import { Image } from 'astro:assets';
import mySVG from './example.svg';
---

<Image src={mySVG} alt="Example SVG" />

@stramel
Copy link
Contributor

stramel commented Jun 6, 2025

Thanks for the PR! I do have some initial thoughts on this.

This approach is similar to what we did in astro-icon which had some downsides and issues.

This might also conflict with an idea @natemoo-re had about shifting the icons into a content-collection instead.

I was also thinking about alternatives to this approach that doesn't tie us so tightly to svgo. Similar to the image optimization setup or offering a optimize function/file. Sharp offers SVG optimization as well.

Another thought was allowing opt-in per SVG though that can get a bit hairy.

Let me know if you have any deeper thoughts. I'll keep thinking about it as well. Interested to hear @natemoo-re 's opinions as well

@azat-io azat-io force-pushed the feat/svgo branch 2 times, most recently from 175772e to f7104bc Compare June 7, 2025 10:54
@azat-io
Copy link
Author

azat-io commented Jun 7, 2025

Made fixes based on comments:

  • added changest
  • improved typing
  • made the feature experimental

svg?: {
/**
* @docs
* @name svg.optimize
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
* @name svg.optimize
* @name experimental.svg.optimize

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done ✅


/**
* @docs
* @name svg.svgoConfig
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
* @name svg.svgoConfig
* @name experimental.svg.svgoConfig

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done ✅

@azat-io
Copy link
Author

azat-io commented Jun 12, 2025

Should I create a pull request in the documentation?

@azat-io
Copy link
Author

azat-io commented Jun 20, 2025

Rebased.

@azat-io
Copy link
Author

azat-io commented Jun 26, 2025

Any feedback?

@azat-io
Copy link
Author

azat-io commented Jul 5, 2025

Ping.

1 similar comment
@azat-io
Copy link
Author

azat-io commented Jul 16, 2025

Ping.

@delucis
Copy link
Member

delucis commented Jul 17, 2025

Hey @azat-io! We’re waiting on getting some more feedback from our SVG experts — apologies for the delay.

In the meantime, it would be good to have an answer to my question here: #13880 (comment)

@azat-io azat-io force-pushed the feat/svgo branch 2 times, most recently from 446db9a to 8a6dd52 Compare July 20, 2025 12:59
@azat-io
Copy link
Author

azat-io commented Aug 6, 2025

Rebased.

@jlarmstrongiv
Copy link

Amazing stuff! I was looking for how to use svgo with astro today and came across this PR. Native support for svgo with a customizable svgo config would be extremely helpful.

@azat-io
Copy link
Author

azat-io commented Oct 5, 2025

Hi @delucis, @stramel, @natemoo-re,

Checking in - it's been 4 months since the last comment. All feedback has been addressed and the PR is ready for review.

What's the status on this?

Copy link
Member

@Princesseuh Princesseuh left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Overall looks good to me, thank you for contributing!

@Princesseuh
Copy link
Member

I am personally of the opinion that tying this to SVGO is good. SVGO has been the reference in SVG optimisation for honestly forever. Some other libraries do offer that feature too, but it's typically worse than SVGO so why bother.

Additionally, I think it's not really worth the complexity added by supporting image service-like for this.

@azat-io
Copy link
Author

azat-io commented Oct 28, 2025

I've made fixes based on your comments, rebased, and resolved conflicts. I also opened a new pull request for documentation:

withastro/docs#12637

Copy link
Member

@sarah11918 sarah11918 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some comments on behalf of docs here, and thank you for the docs PR @azat-io !

  1. Quick question (for everyone) on the experimental flag name. svg is pretty general, and could mean anything about SVGs. (And, could create confusion if there's another experimental feature involving SVGs at the same time.) So just want to make sure this shouldn't be something like experimental.svgOptimization for clarity, and to let the user know exactly what they're signing up for!

  2. Also, wondering about the optimize property which as far as I can tell is basically just setting the flag true? This doesn't seem to match our other patterns that can be just enabled, or passed extra config, like CSP for example. You either set the experimental flag true to enable the basic behaviour, or you pass a config object (but you don't otherwise have a property that retains "turning on the flag" behaviour):

export default defineConfig({
  experimental: {
    csp: {
      directives: [
        "default-src 'self'",
        "img-src 'self' https://images.cdn.example.com"
      ]
    }
  }
});

It feels weird to document "optimize" when it really just means "yes, use this flag" and is required for any of the other options to be set. (Which otherwise are just one object that could even be experimental.svg options directly?) Maybe this is future proofing for when it's not experimental and these will be higher-level options? But it feels clunkier than other patterns we've used as I'm looking at documenting it, so I'm just pointing it out because I'm noticing it!

Like, maybe there's a world where it's similarly just (i.e. the SVGO configuration object just is the config object):

export default defineConfig({
  experimental: {
    svgOptimize: {
       plugins: [
          'preset-default',
       ],
      floatPrecision: 2,
    }
  }
})

Anyway, more pointing out this feels different, and want to make sure everything done here is intentional and a conscious choice! And of course, I am not the subject matter expert here, so maybe what I'm saying doesn't even make sense in this context.

const result = optimize(contents, svgConfig.svgoConfig);
processedContents = result.data;
} catch (error) {
console.warn('SVGO optimization failed:', error);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we error with an AstroError instead of warning?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you advise me on how to do this?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure! You need to do 2 things basically:

  1. Create a new error data in src/core/errors/error-data.ts (path might not be exact but pretty close). You can look at src/core/errors/README.md for guidance and at the rest of the TS file for practical examples
  2. Then here you can throw an AstroError directly, something like this:
catch(cause) {
	throw new AstroError(
		AstroErrorData.SvgoOptimizationFailed,
		{ cause }
	)
}

Let me know if you need any clarification

@azat-io
Copy link
Author

azat-io commented Oct 29, 2025

@sarah11918 @florian-lefebvre

Thanks for another iteration of the review.

What has been done:

  1. Improved changeset, added documentation
  2. Removed @docs from the interface
  3. Fixed AstroConfigType to AstroConfig
  4. Fixed merge issues
  5. Fixed default behavior to false

Copy link
Member

@florian-lefebvre florian-lefebvre left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Little knits

Copy link
Member

@sarah11918 sarah11918 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changeset looks great! Thanks for this lovely addition! A few minor editing polishes from me, and we can continue the discussions about the other usage examples in the docs PR!

/**
* @docs
*/
export const CannotOptimizeSvg = {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@sarah11918 there's now an error that would need your review

Copy link
Member

@sarah11918 sarah11918 Nov 3, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some notes here:

  • This error message should probably tell people what is going to happen and/or what they should do. I believe from the feature it will just fall back to an unoptimized SVG and there's nothing they can actually do about it? So, probably makes sense to additionally say that the original SVG will be used/building with the original SVG instead... something like that.
  • I know sometimes we can say which file led to the error. If so, it's a nicety here to be able to say which SVG could not be optimized. This lets people try to do something with that file in particular, if they want. And, in that case, you could suggest optimizing it manually through something like SVGOMG, if desired, in the error message.
  • These errors are all indexed on a single docs page in the order that they're listed here, and they are in categories. So, if there's a better category/placement, then we add new errors in that location in the file. We can't just stick new ones at the end or they end up in the last category. Right now the existing image errors are in the catch-all "Astro Errors" category, so it probably makes sense to put it in that one. (We could and maybe should have a separate images error section, but that's not the responsibility of this PR!)

* When enabled, all imported SVG files will be optimized for smaller file sizes
* and better performance while maintaining visual quality.
*/
optimize?: boolean;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you brought this up sarah already, about directly having the config object as a root property instead of having optimize: boolean. What was the conclusion of that?

Because I think it could be nice to have config.experimental.svgo: boolean | SvgoConfig or something like that

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done. ✅️️️️

Also made the corresponding changes to the pull request with documentation:
withastro/docs@3005899

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

docs pr pkg: astro Related to the core `astro` package (scope)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

9 participants