Skip to content

Built-in SVG Components#1035

Merged
florian-lefebvre merged 8 commits into
withastro:mainfrom
stramel:built-in-svg-component-stage-3
Apr 14, 2025
Merged

Built-in SVG Components#1035
florian-lefebvre merged 8 commits into
withastro:mainfrom
stramel:built-in-svg-component-stage-3

Conversation

@stramel
Copy link
Copy Markdown
Contributor

@stramel stramel commented Oct 2, 2024

Summary

This RFC proposes adding native support for importing and rendering .svg files as Astro components, with optimized rendering techniques to minimize performance impact. It aims to allow .svg files to be treated as components that accept props and be optimized for repeated use on a page using <symbol> and <use> elements.

An SVG can be imported directly into an Astro component and used as a component that will only embed itself once.

---
import Logo from '../assets/logo.svg'
---

<Logo/>

Results in:

<svg height="24" width="24" role="img">
	<!-- SVG Content -->
</svg>

Links

@stramel stramel mentioned this pull request Oct 2, 2024
Copy link
Copy Markdown
Member

@ematipico ematipico left a comment

Choose a reason for hiding this comment

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

Thank you @stramel for the hard work! I left some preliminary feedback :)

Comment thread proposals/0052-svg-components.md
Comment thread proposals/0052-svg-components.md Outdated
Comment thread proposals/0052-svg-components.md Outdated
Comment thread proposals/0052-svg-components.md Outdated
Comment thread proposals/0052-svg-components.md Outdated
Copy link
Copy Markdown

@lorenzolewis lorenzolewis left a comment

Choose a reason for hiding this comment

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

Super excited to see this one move forward! Just a quick note on the framing of this when it gets to docs.

Also, for Sprites, do you think explaining the benefits makes sense, or is this perhaps only an implementation detail? I'm not familiar at all so just curious.

Comment thread proposals/0052-svg-components.md Outdated
@abdo-spices
Copy link
Copy Markdown

abdo-spices commented Oct 2, 2024

thanks, for hard working and supporting <use> as I requested

@matthewp
Copy link
Copy Markdown
Contributor

matthewp commented Oct 4, 2024

Can you explain how you are implementing the <use> behavior? Does the user need to author their SVG to be a sprite, and you detect that and use <use> Or it is a configuration to enable that, or a prop, or?

Comment thread proposals/0052-svg-components.md
@lorenzolewis
Copy link
Copy Markdown

lorenzolewis commented Oct 4, 2024

If a developer removes an SVG element from the DOM (using client-side JS) that holds the symbols (I.e. the source), will that break other SVGs on the page using that symbol?

@stramel
Copy link
Copy Markdown
Contributor Author

stramel commented Oct 5, 2024

If a developer removes an SVG element from the DOM (using client-side JS) that holds the symbols (I.e. the source), will that break other SVGs on the page using that symbol?

Yes, that will break the rest of the SVGs using that symbol. This is one of the nuances to using this sprite setup. Another option would be to put all the symbols into a single SVG that could be placed on the page (perhaps in a layout for simplicity). This would avoid that nuance but would complicate things in the future from a lazy loading perspective, generating the build, and would a slightly more code (though this is probably negligible)

@stramel
Copy link
Copy Markdown
Contributor Author

stramel commented Oct 5, 2024

Can you explain how you are implementing the <use> behavior? Does the user need to author their SVG to be a sprite, and you detect that and use <use> Or it is a configuration to enable that, or a prop, or?

The user doesn't need to author the SVG in any certain way for it to be used as a sprite.

The sprite behavior is:

  • get the attributes off the <svg> element
  • get the children of the <svg> element
  • generate a unique id
  • create a <symbol> element with the viewBox attribute and the unique id
  • create a <use> element with an xlink:href attribute that references the unique id (this is the link between the symbol and use)
  • create a new <svg> element that applies the attributes and additional props on it
  • add the <symbol> and <use> elements as children to the new <svg> element

All future SVGs will not create and add the <symbol> element leaving only the <use> element which references the original definition.

@stramel stramel requested a review from matthewp October 5, 2024 04:52
@lorenzolewis
Copy link
Copy Markdown

If a developer removes an SVG element from the DOM (using client-side JS) that holds the symbols (I.e. the source), will that break other SVGs on the page using that symbol?

Yes, that will break the rest of the SVGs using that symbol. This is one of the nuances to using this sprite setup. Another option would be to put all the symbols into a single SVG that could be placed on the page (perhaps in a layout for simplicity). This would avoid that nuance but would complicate things in the future from a lazy loading perspective, generating the build, and would a slightly more code (though this is probably negligible)

I can imagine this will present some difficult support scenarios, especially if adding a framework-specific way on top of this is a future goal as you've mentioned in the original RFC.

Would there be any downside to having a separate SVG defining the symbols on the page without that specific element being responsible for rendering anything itself (i.e. only holding the symbols, not displaying them)?


Thinking outside the box a bit here, I could imagine a setup like this:

  1. User imports and uses an SVG. This is is just treated normally without any symbol or use injected.
  2. A user opts-in to the symbol and use setup by importing a special component and putting it in their page:
---
import { SvgSymbol } from 'astro:assets' // naming tbd
import Logo from '../assets/logo.svg';
import Layout from './layouts/BaseLayout.astro'
---

<Layout>
  <SvgSymbol />
  <Logo />
  <h1>I'm a cool page</h1>
</Layout>

With the above case, a user is specifically opting-in to the sprite behavior and it is clear where it is used. My mind is thinking a bit on how View Transitions were implemented: https://docs.astro.build/en/guides/view-transitions/#adding-view-transitions-to-a-page

@stramel
Copy link
Copy Markdown
Contributor Author

stramel commented Oct 6, 2024

There are more gotchas when using Sprites which is why we should make that opt-in rather than the default. Even with using a Sprite component to hold the aggregate of SVGs on the page.

Would there be any downside to having a separate SVG defining the symbols on the page without that specific element being responsible for rendering anything itself (i.e. only holding the symbols, not displaying them)?

As I was mentioning previously, this does complicate the implementation, could prove hard for SSR lazy-loading later, and very slight increase in code size.

That being said, I think it might be worth it for the trade-off for avoiding an edge-case on the Sprite usage and possible improvement for framework usage.

Would love @natemoo-re's thoughts on it.

@alvinometric
Copy link
Copy Markdown

Just wanted to say that doing this:

---
  import ThumbIcon from '../assets/icons/ui/thumbs-up.svg?raw';
---

<Fragment set:html={ThumbIcon} />

Has been pretty great for me. I like the idea of "it just works" rather than having to find this in the docs. But I'd rather not overcomplicate things if it creates problems elsewhere.

@Lofty-Brambles
Copy link
Copy Markdown

A QoL thing: It'd be nice if I can style the svg without having to use a global modifier, if I import it within the component.

@stramel
Copy link
Copy Markdown
Contributor Author

stramel commented Oct 13, 2024

A QoL thing: It'd be nice if I can style the svg without having to use a global modifier, if I import it within the component.

I'm not quite sure I'm following. Can you provide an example or for details?

@Lofty-Brambles
Copy link
Copy Markdown

I'm not quite sure I'm following. Can you provide an example or for details?

Yes!

---
import RandomSVG from ".@src/imgs/duck_icon.svg";
---

<RandomSVG />

<style>
    svg { /* This would target the svg element that this results in. */ }
</style>

Comment thread proposals/0052-svg-components.md Outdated
@stramel
Copy link
Copy Markdown
Contributor Author

stramel commented Oct 24, 2024

I'm not quite sure I'm following. Can you provide an example or for details?

Yes!

---
import RandomSVG from ".@src/imgs/duck_icon.svg";
---

<RandomSVG />

<style>
    svg { /* This would target the svg element that this results in. */ }
</style>

I'm not sure there is much we can do about that. The styling is primarily leaning on the feature-set of Astro. You could use the style/class attributes.

@teinett
Copy link
Copy Markdown

teinett commented Nov 25, 2024

I just checked the documentation for this feature: https://5-0-0-beta.docs.astro.build/en/reference/experimental-flags/svg/.

I would love to use the feature in sprite mode. Example: theme switcher https://github.com/web-standards-ru/web-standards.ru/blob/main/src/includes/scheme-switcher.njk using SVG sprite https://github.com/web-standards-ru/web-standards.ru/blob/main/src/images/sprite.svg.

Resulted code should be:

<svg class="scheme-switcher__icon" aria-hidden="true" width="20" height="20">
    <use href="/images/sprite.svg#duo"></use>
</svg>

Currently, I don't understand:

  • how to add this use part for the exact icon in my SVG sprite
  • how to add part aria-hidden="true" when using the AstroJS component. Will it receive additional props?

@stramel
Copy link
Copy Markdown
Contributor Author

stramel commented Nov 25, 2024

I just checked the documentation for this feature: 5-0-0-beta.docs.astro.build/en/reference/experimental-flags/svg.

I would love to use the feature in sprite mode. Example: theme switcher web-standards-ru/web-standards.ru@main/src/includes/scheme-switcher.njk using SVG sprite web-standards-ru/web-standards.ru@main/src/images/sprite.svg.

Resulted code should be:

<svg class="scheme-switcher__icon" aria-hidden="true" width="20" height="20">
    <use href="/images/sprite.svg#duo"></use>
</svg>

Currently, I don't understand:

  • how to add this use part for the exact icon in my SVG sprite
  • how to add part aria-hidden="true" when using the AstroJS component. Will it receive additional props?

how to add this use part for the exact icon in my SVG sprite

If you are wanting to switch all SVGs to use the sprite mode, you can change the setting in the Astro config:

{
  experimental: {
    svg: { mode: 'sprite' }
  }
}

This will automatically create a sprite and allow you to reuse the symbols.

how to add part aria-hidden="true" when using the AstroJS component. Will it receive additional props?

You can just pass component props like you normally would. It will receive all props you pass. Docs
Using your example (https://github.com/web-standards-ru/web-standards.ru/blob/main/src/includes/scheme-switcher.njk),

---
import Moon from '~/assets/moon.svg'
import Duo from '~/assets/duo.svg'
import Sun from '~/assets/sun.svg'
---
<section class="scheme-switcher" aria-label="Цветовая тема">
    <button class="scheme-switcher__button" type="button" value="dark" aria-pressed="false" title="Тёмная">
        <Moon class="scheme-switcher__icon" aria-hidden="true" width="20" height="20" />
        <span class="visually-hidden">Тёмная</span>
    </button>
    <button class="scheme-switcher__button" type="button" value="auto" aria-pressed="false" title="Системная">
        <Duo class="scheme-switcher__icon" aria-hidden="true" width="20" height="20">
        <span class="visually-hidden">Системная</span>
    </button>
    <button class="scheme-switcher__button" type="button" value="light" aria-pressed="false" title="Светлая">
        <Sun class="scheme-switcher__icon" aria-hidden="true" size="20" title="Светлая" />
        <!-- you can use `size` instead of specifying both `width` and `height` -->
        <!-- you can use `title` instead of adding a `span` with the text -->
    </button>
</section>

@matthewp
Copy link
Copy Markdown
Contributor

@stramel can you update the RFC to explain the mode option?

@teinett
Copy link
Copy Markdown

teinett commented Nov 28, 2024

If you are wanting to switch all SVGs to use the sprite mode, you can change the setting in the Astro config...

My project has both individual SVGs, such as the project logo, and SVG sprites.

I would like to work with SVGs in both formats at the same time, rather than either single SVGs or sprites. This is currently impossible, as far as I understand?

@stramel
Copy link
Copy Markdown
Contributor Author

stramel commented Nov 28, 2024

If you are wanting to switch all SVGs to use the sprite mode, you can change the setting in the Astro config...

My project has both individual SVGs, such as the project logo, and SVG sprites.

I would like to work with SVGs in both formats at the same time, rather than either single SVGs or sprites. This is currently impossible, as far as I understand?

This is possible as well, you can specify a default mode in the Astro config and specify a mode on the icons to overwrite the default as well.

Copy link
Copy Markdown

@mayank99 mayank99 left a comment

Choose a reason for hiding this comment

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

This is great to see! I used to have to save my SVGs as .astro and spread Astro.props manually, whereas now I can keep using .svg, with props automatically forwarded. 🙌

I have three small pieces of feedback which I've left as comments below.

Comment thread proposals/0052-svg-components.md Outdated
Comment on lines +227 to +253
### ARIA Attributes

- **Role Attribute:** By default, Astro could set `role="img"` on SVGs when they are used for non-decorative purposes.
- **Title Element:** If the `.svg` file needs and accessible title/label, Astro can inject a `<title>` element.

**Example:**

```astro
---
import Logo from '../assets/logo.svg'
---

<!-- Pass an accessible title that will be injected -->
<Logo title="Company Logo" />
```

This would generate the following output:


```astro
<svg width="24" height="24" role="img">
<title>Company Logo</title>
<!-- SVG Content -->
</svg>
```

While this proposal offers strong defaults for accessibility, it is essential to give developers full control. Any automatically generated accessibility attributes (aria-label, role, etc.) should be overridable by the developer at the component level, ensuring that specific use cases or custom behaviors can be supported.
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

I don't think role="img" should be added by default.

When the SVG is not labelled, it would be interpreted as "image" (similar to an image without alt text), which is unhelpful. Further, when aria-hidden is specified from outside, I would definitely expect the role="img" to not be present.

Also, converting the title attribute into a <title> element feels a bit surprising as well. Is there a way to opt out of this?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

This is a good point. Our goal was to provide strong defaults for a11y. This (role="img") and <title> come over from astro-icon. From everything I have read, role="img" and <title> is a solid default for svgs.

Not sure how we could make the DX better here. @natemoo-re might have some insight on this as well.

references:
mdn
accessible SVGs
dequeue
w3c
css tricks

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

From everything I have read, role="img" and <title> is a solid default for svgs.

But it's not the default currently. By default, only role="img" is added. The title is optional.

And while role="img"+<title> is fine for labelling a static SVG, my comment is about the implementation in Astro.

  1. If the SVG contains meaningful content inside it, role="img" may hide it by default. This is surprising behavior.
  2. The title attribute gets confusingly converted into a <title> element. This is surprising behavior.
    • The title attribute is widely discouraged, so developers may feel uneasy using it and leave the SVG unlabelled.

It may have been fine for a third-party library like astro-icon, but a framework should do the least surprising thing. So I would suggest making role="img" opt-in, and allow <title> to be passed as children.

---
import MySvg from "somewhere.svg";
---
<MySvg role="img">
  <title>The label</title>
</MySvg>

This is more predictable, more flexible, and also addresses my point above where both role="img" and aria-hidden="true" were present.

(Side note: in my personal experience, it's far more common to hide the SVG using aria-hidden="true" and instead make sure to label the encompassing element. The label is often dependent on the surrounding context, and the tooltip added by <title> is usually undesirable as well.)

Copy link
Copy Markdown
Member

@delucis delucis Dec 4, 2024

Choose a reason for hiding this comment

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

Agree with @mayank99 here. For various reasons I'll almost always have markup that looks something like the following (abbreviated for clarity):

<span>
  Text label
  <svg aria-hidden="true">...</svg>
</span>

(Substitute button, a, etc. for span as appropriate, and potentially wrap the label in visually-hidden class if necessary.)

In fact one of the most common accessibility remediations I've had to apply to sites using astro-icon is going through adding aria-hidden="true" to everywhere the icon component is used.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I do think I agree with you. Would still like to hear @natemoo-re and @matthewp 's perspectives as well.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Tested and aria-labelledby does make it work. No role needed. This seems like a pretty good combination to me, you get:

  • Voiceover
  • Mouse hover tooltip
  • Content in the page so translation works

And by omitting role="img" it doesn't announce it to be an image. What are the downsides to doing this?

Copy link
Copy Markdown
Contributor

@matthewp matthewp Dec 16, 2024

Choose a reason for hiding this comment

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

@stramel As no one is happy with the current behavior, if you wanted to remove this from the RFC for now, while we continue to try and reach a consensus on a direction, that would be fine with me.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Not sure it's too helpful, but I would not expect an SVG integration to muck with a11y settings as a general-purpose tool. astro-icon makes more sense since it's an icon library, but as a framework integration for svgs as icons and as illustrations, I would expect control to be handed to me as the developer to implement my SVG's correctly. I'm not sure there's a one-size fits all approach that works for all usecases and doesn't step on toes.

Maybe an approach closer to <Image> where alt is a required prop would be prudent? Could write custom lints that require title or aria-label or aria-hidden to be set, but that might be a bit complicated.

Copy link
Copy Markdown

@metagrapher metagrapher Feb 25, 2025

Choose a reason for hiding this comment

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

Thanks for the breakdown of alternatives! I think we're getting to the heart of the issue now, and that's about use-cases and design patterns. I have thoughts to drill down further, but first I want to hit on this point which I think is unrelated:

I’m not saying no-one’s allowed to use <title>. I’m saying <title> is not a good enough method to enable using it in a nonstandard way, which is a way of promoting this usage. Want to add it to your SVG files? Absolutely. Go ahead!

I don't buy the non-standard argument some have made. Of course it's non-standard, but so is importing an SVG, so are Titlecase components, so is .astro. syntax. This is not a standards body, we're defining our own API.

There's a nuanced point that's being talked past here which is that by making a place for it in the documentation you are promoting it. In this case it is about best and better, and not about what's good or "seemed like a good idea (at the time)". I wish to tell you how frustrating it is when a developer decides that they know better than the standard implementation and unexpected things happen. I lose several hours. Clients lose thousands of dollars.

You are making your own API so you become a standards body.

This is important nuance to understand here. You may not be the standard--you are not WHATWG--, but you become a standard: you are Astro, and this is an RFC.

I think this nuance has been missing from a lot of Astro's perspective of late, and I'm saying something about it because it's pushing me and other developers away from the platform I love.

I don't think there's an expectation of props mapping directly to <svg> element props. We have lots of components in Astro core that have their own props APIs. The <Code /> component doesn't map directly to a <code> element, nor does <Image /> map to an <img />. Generally speaking, components define their API in frameworks.

That this particular component spreads unknown props onto the <svg> element is a positive thing that makes it more powerful; but it shouldn't be a constraint we place on ourselves not to make the API better where appropriate.

This SVG implementation is important to me as I've been an advocate of SVG for over 10 years now. It's going to make or break my continued advocacy and evangelization of the platform to my clients and colleagues.

SVG and Web Components are the future of the internet whether we like it or not, and so we should strive to support the standard and not do unexpected behavior. When we do, it needs to be well documented and there needs to be a strong compelling reason for deviating from standards.

This is a tenet of the modern web won with blood, sweat, and tears. I mean that literally. As the old guy in the room who had to support IE5.5, you want to support standards as much as possible and you want to not be special. If you don't believe me, just look at "quirks mode" or Adobe flash.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I'm wondering if there's a different approach that we could take where we don't necessarily expose the inner workings but instead expose what developers are looking for, which is fitting their use-case. Having a prop that allows developers to opt-in to a easy setup for what they're looking for might be a better option. These examples will likely be bad but hopefully get my point across.

  • as="icon" applies role=img and requires a label
  • as="hidden' applies aria-hidden=true

and have an option for the user to opt-out and control the a11y themselves.

Comment thread proposals/0052-svg-components.md Outdated
Comment on lines +103 to +107
### Sprite

An SVG Sprite is when an `<svg>` element defines multiple SVGs (or symbols) using the `<symbol>` element and attaching a unique identifier to each SVG as an `id`. These are then able to be referenced using the `<use>` element with an `href` attribute that points to the `id` defined on the `<symbol>` element.

When a `.svg` file is first imported and rendered in Astro, the system will convert the file into a `<symbol>` element. This will be inserted into the `<svg>` element. This approach ensures that all subsequent renders of that `.svg` can use a `<use>` element, referencing the ID of the initial `<symbol>`.
Copy link
Copy Markdown

@mayank99 mayank99 Nov 30, 2024

Choose a reason for hiding this comment

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

It seems like setting mode: "sprite" currently only inlines SVG symbols into the current page. This is fine as a default, but what would be ideal is if an external sprite sheet was generated, so that it could be cached and reused between pages. (Maybe Astro could even detect if SVGs are used in multiple pages? Although, that sounds a bit complicated.)

The external .svg file can be preloaded as well (either automatically when opting into this mode, or by providing developers with the URL).


As an added benefit of externalizing the sprite sheet, it would become easier to use SVGs in framework components. Imagine a component-agnostic API like this:

import { getSvgHref } from "astro:assets";
<svg>
  <use href={getSvgHref("../icons/heart.svg")} />
</svg>

External sprite sheets is largely my preferred approach of using SVGs today. I know framework support is listed as a "future" goal, but I'm mentioning this now because the decisions made today will likely have an impact on it. "SVGs-as-components" in client JS frameworks can be useful in advanced cases (like animation) but is usually an anti-pattern.


This can always be added in the future and doesn't need to block the feature from shipping today, but maybe the configuration option should be renamed from "sprite" to something like "inline-sprite"?

I did see your earlier comment related to this as well.

Copy link
Copy Markdown
Contributor Author

@stramel stramel Dec 4, 2024

Choose a reason for hiding this comment

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

TBH I'm not overly happy with the mode or sprite functionality. I added mode to allow for more flexibility, such as external sprites, sprite sheet, and the inline sprites (current) for the future.

I have a few thoughts that I need to tackle.

I'm also not super happy with the framework SVG story but it currently follows the standard in Astro. Hoping some of my thoughts will help with this as well.

I would love to have a sprite component that aggregates the SVGs into the component. Allowing for external or embedded.

We also discussed maybe something that just exports a use so that you could create your own sprites.

Across pages external is a must.

I'll try to summarize my thoughts for improving this and the future.

Comment thread proposals/0052-svg-components.md Outdated
Copy link
Copy Markdown

@tanishqmanuja tanishqmanuja left a comment

Choose a reason for hiding this comment

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

If you measure the progress only keeping the Goals in mind. This looks food but still there is no native support for dynamic import or making multiple imports easy :( I mean honestly no one wants 10+ svg import lines at the top of the file for example say in a brands pages.

@stramel
Copy link
Copy Markdown
Contributor Author

stramel commented Apr 12, 2025

If you measure the progress only keeping the Goals in mind. This looks food but still there is no native support for dynamic import or making multiple imports easy :( I mean honestly no one wants 10+ svg import lines at the top of the file for example say in a brands pages.

Dynamic import is supported 🙂

WRT the SVG imports, it isn't too hard to throw together an Icon component that utilizes dynamic import to associate a name with a file.

@florian-lefebvre florian-lefebvre merged commit dc3e5c8 into withastro:main Apr 14, 2025
@delucis
Copy link
Copy Markdown
Member

delucis commented Apr 14, 2025

Huge shout out to @stramel for getting this proposal across the line 👏

And thanks to everyone who provided feedback. Really valuable especially to the have the expert accessibility perspectives as part of this discussion.

@ADTC
Copy link
Copy Markdown

ADTC commented Apr 18, 2025

It can be added later, it's not a big deal. Once 5.7 is merged and released, feel free to send a PR

@florian-lefebvre thank you. Unfortunately I'm not clear on the PR processes here, and I'm not clear on the correct way to implement this either.

The one removed from PR #13488 commit a513941 was an alias (and that was the reason for its removal):

// packages/astro/src/assets/utils/svg.ts
import type { AstroComponentFactory } from '../../runtime/server/index.js';

export type SVGComponent = AstroComponentFactory;

// packages/astro/src/types/public/index.ts
export type { SVGComponent } from '../../assets/utils/svg.js';

Personally though, I prefer add this to my projects, as it's not an alias:

export type SVGComponent = typeof import('astro/client/*.svg').default

Note to others: I add it without the export to the global type definitions file.

@florian-lefebvre
Copy link
Copy Markdown
Member

SvgComponent is not an alias anymore I think but (props: astroHTML.JSX.SVGAttributes) => any so I think it would make sense to expose it. What do others think?

@delucis
Copy link
Copy Markdown
Member

delucis commented Apr 18, 2025

Is SvgComponent not still compatible with AstroComponentFactory? My only hesitation is that imported SVGs now have a slightly unique shape that is a combination of an Astro component and ImageMetadata. If there's anything about that we'd like to keep as an internal API we could encourage people sticking to using one of those existing types? Although I guess maybe you want to type in a way that supports rendering directly and use with the <Image> component, in which case having the combined type available would be helpful.

@florian-lefebvre
Copy link
Copy Markdown
Member

IMO the important part is that the props are only for svg attrs instead of Record<string, any>

@delucis
Copy link
Copy Markdown
Member

delucis commented Apr 18, 2025

Ah right, good point. Then yeah probably good to expose it 👍

@ADTC
Copy link
Copy Markdown

ADTC commented Apr 18, 2025

one of those existing types

You mean like ImageMetadata? Tried it and I cannot render SVG like this: <MySVG /> without getting type errors. I can't do <Image src={MySVG}> because if an SVG is rendered using an img tag, then the use of currentColor in SVG will fail to inherit the parent element's color.

Maybe the type should be Astro component then? I'm not sure what that type is that right now. AstroComponent? But then yes, it won't allow type checking props unique to SVG.

Thank you for moving this discussion forward!

@florian-lefebvre
Copy link
Copy Markdown
Member

Once we figure out where to put the type, I can help you land the PR 🚀

@florian-lefebvre
Copy link
Copy Markdown
Member

@ADTC alright so here is what you can do:

We'll have to check if it can be used anywhere else but that should be a good start!

@ADTC
Copy link
Copy Markdown

ADTC commented Apr 18, 2025

@florian-lefebvre for the first one, am I correct to do this?

export type SvgComponent = ((_props: Props) => any) & ImageMetadata

Or is there something more specific than any?

Also, do we prefer the capitalization SvgComponent instead of SVGComponent?

EDIT: SvgComponent is generally the preferred and more idiomatic choice in the TypeScript/React ecosystem.

@florian-lefebvre
Copy link
Copy Markdown
Member

florian-lefebvre commented Apr 18, 2025

I think it should be the following

export type SvgComponent = (props: astroHTML.JSX.SVGAttributes) => any

I don't think we have anything better than any and ImageMetadata is not part of the component, it's to make it compatible with astro:assets metadata imports.

Let's start with that, we can discuss on the implementation PR directly 👍

@tanishqmanuja
Copy link
Copy Markdown

@ADTC
Copy link
Copy Markdown

ADTC commented Apr 18, 2025

@florian-lefebvre Oh yeah, I forgot to mention this part: 😄

type Props = astroHTML.JSX.SVGAttributes;

I will go see about that PR.

PS: @tanishqmanuja thanks for sharing. It won't be necessary here to do it that way, and probably not advisable.

@ADTC
Copy link
Copy Markdown

ADTC commented Apr 18, 2025

ImageMetadata is not part of the component, it's to make it compatible with astro:assets metadata imports.

It's also possible to use SVG as the src attribute of an img tag: <Image src={MySVG}>

This might be prevented if we don't include ImageMetadata, right? I'd include it, to avoid a breaking change.

@florian-lefebvre
Copy link
Copy Markdown
Member

florian-lefebvre commented Apr 18, 2025

Do it and we'll discuss on the PR, it will be simpler than here

@ADTC
Copy link
Copy Markdown

ADTC commented Apr 18, 2025

@florian-lefebvre here it is: withastro/astro#13651

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.