Rendering .svg files as components
#667
Replies: 9 comments 9 replies
-
|
How would this interact with Apart from those points, I love it and it seems wonderful. |
Beta Was this translation helpful? Give feedback.
-
|
One possible alternative API is more like the existing ---
import { Svg } from 'astro:components'
import Logo from '../assets/logo.svg'
---
<!-- first render: includes initial <symbol> -->
<Svg src={Logo} width={48} height={48} />
<!-- subsequent renders: automatically optimized with <use> -->
<Svg src={Logo} width={48} height={48} />Bikeshedding possible names:
Pros:
Cons:
|
Beta Was this translation helpful? Give feedback.
-
|
For reference this is the way I'm implementing SVG icons with Astro. It's pretty meh and long but works and also, of importance to me, has type safety on the icon names preventing typos. ---
import type { IconType } from '../../domain/types';
import email from './email.svg?raw';
import twitter from './twitter.svg?raw';
import facebook from './facebook.svg?raw';
import linkedin from './linkedin.svg?raw';
import phosphorBooks from './phosphor-books.svg?raw';
import heroIconsCalculator from './heroicons-calculator.svg?raw';
import heroIconsCheck from './heroicons-check.svg?raw';
import heroIconsCode from './heroicons-code.svg?raw';
import heroIconsFeed from './heroicons-feed.svg?raw';
import heroIconsMoon from './heroicons-moon.svg?raw';
import heroIconsPlus from './heroicons-plus.svg?raw';
import heroIconsMinus from './heroicons-minus.svg?raw';
import heroIconsExclamationCircle from './heroicons-exclamation-circle.svg?raw';
import heroIconsChevronUp from './heroicons-chevron-up.svg?raw';
import heroIconsChevronDown from './heroicons-chevron-down.svg?raw';
import heroIconsEye from './heroicons-eye.svg?raw';
import heroIconsMagnifyingGlassPlus from './heroicons-magnifying-glass-plus.svg?raw';
import heroIconsMagnifyingGlassPlusMini from './heroicons-magnifying-glass-plus-mini.svg?raw';
import discord from './discord.svg?raw';
import envelope from './envelope.svg?raw';
export interface Props {
name: IconType;
};
const { name } = Astro.props as Props;
const map: Record<IconType, string> = {
'email': email,
'twitter': twitter,
'facebook': facebook,
'linkedin': linkedin,
'phosphor-books': phosphorBooks,
'heroicons-calculator': heroIconsCalculator,
'heroicons-check': heroIconsCheck,
'heroicons-code': heroIconsCode,
'heroicons-feed': heroIconsFeed,
'heroicons-moon': heroIconsMoon,
'heroicons-plus': heroIconsPlus,
'heroicons-minus': heroIconsMinus,
'heroicons-exclamation-circle': heroIconsExclamationCircle,
'heroicons-chevron-up': heroIconsChevronUp,
'heroicons-chevron-down': heroIconsChevronDown,
'heroicons-eye': heroIconsEye,
'heroicons-magnifying-glass-plus': heroIconsMagnifyingGlassPlus,
'heroicons-magnifying-glass-plus-mini': heroIconsMagnifyingGlassPlusMini,
'discord': discord,
'envelope': envelope,
};
const svg = map[name];
// TODO: Size, colour, hover colour, etc.
---
<Fragment set:html={ svg } /> |
Beta Was this translation helpful? Give feedback.
-
|
useful prior art in React-land is https://react-svgr.com/ - which I appreciate - allows creating a component per asset that can receive standard svg props and styling, inlining the SVG in the document |
Beta Was this translation helpful? Give feedback.
-
|
Working implementation on 3.0, works in 2.0 if you replace runtimePath with
Initial legwork was performed by a previous user and modified by me, however I can't find them or the original code anywhere 😳. Whoever wrote this, well done!
|
Beta Was this translation helpful? Give feedback.
-
|
OK so I explicitly did NOT want to wade into the whole icon ecosystem thing initially, but I'm sure that's what most people are interested in. Very curious if an import-like API that piggybacks on ---
import Camera from 'astro:assets/icons/mdi/camera'
---
<Camera />Here's a quick demo: astro-builtin-icons.mp4 |
Beta Was this translation helpful? Give feedback.
-
|
Great news! This proposal has been accepted and is moving on to Stage 2 of our process! 🎉 What does that mean?The Astro maintainers and technical steering committee have agreed to explore official Astro support for this feature. @natemoo-re has agreed to champion this proposal. What happens now?
Where can we follow along?This discussion will be closed. Please follow along on the accepted proposal. Don't hesitate to share additional feedback, use cases, or concerns on the accepted proposal. |
Beta Was this translation helpful? Give feedback.
-
|
While the official feature is getting worked on, here's a quick workaround for anyone stumbling into this thread like I did. Tested in MDX post files. import svg from './image.svg?raw';
<Fragment set:html={svg} /> |
Beta Was this translation helpful? Give feedback.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
-
Body
Summary
Let's support importing and using
.svgfiles as Astro components.Background & Motivation
Users have reported that
.svgfiles are currently difficult to use—this is a problem we should provide an optimized solution for. Working with.svgs doesn’t need to be difficult or bloat your client-side JavaScript.The Astro team has discussed and experimented with an "icon" primitive since before Astro v1.0 shipped. Now that we have shipped 3.0 with full
astro:assetssupport, I firmly believe all of the pieces are in place to finally make this happen.[TODO] Add some background about how
.svgfiles work in other frameworks (spoiler alert: it's usually not easy and leads users down the wrong path)History
astro-iconwas built as an experimental way to support.svgfiles in Astro, before we had server rendering or an integration API. Though @stramel and I have worked to getastro-iconto a v1.0, I still haven't pulled the trigger and released it due to concerns over bloated server bundles.astro/iconswhich had a significantly reduced scope and came with a pre-baked icon set. Feedback was that it was not flexible enough for most uses, so we punted on it.astro:assets(RFC-0030) added support for loading image files with some metadata, but mostly optimizing bitmap images. Frustratingly, this didn't include.svgfiles, but a community PR added pass-through behavior for.svgfiles. This shipped unflagged in 3.0.Unfortunately, these explorations still haven't solved the problem, which is that users just want to be able to import and render an
.svgfile..svgfiles are possible to support as components that can pass props to the rootsvgelement.Additionally, when exploring
astro-iconv1, I stumbled onto a very promising optimization technique for embedding.svgfiles in an HTML document. Astro is uniquely positioned to take advantage of this spritesheet-inspired approach that sends minimal bytes over the network. The technique involves rendering each.svginto a<symbol>when first used on the page, and subsequently a<use>declaration. This is similar to a traditional spritesheet approach, but is automated without any drawbacks or need to use a Provider pattern.Goals
.svgfiles as if they were an Astro component<svg>element while overriding the existing props<svg>on a page can hurt browser parsing performance. We can automatically reduce the number of nodes by using<symbol>and<use>.<svg>often havexmlnsandxmlns:xlinkandversionattributes. These are not needed in HTML5 and we can automatically drop them to save a few bytes..svgimport remains unchanged.svgcomponents in frameworks without inlining them into the client side JavaScript.svgicons is an anti-pattern. It’s a terrible idea that bloats your JavaScript and creates all sorts of headaches for HMR and clientside performance in production.<Icon>component for each supported framework, we’ll make it easy for users to avoid this anti-pattern. It’s so common now because there aren’t many other good solutions.Non-goals
I'm jumping ahead to the next stage, but I wouldn't consider support for
@iconify/jsonsets a goal for this initially. There are too many gotchas with SSR and tree-shaking that we'd need to figure out. We can leave this to userland for now.Example
User-facing API
Option A (original)
Option B (see comment)
Framework API?
I have created a spike branch to explore this idea.
Beta Was this translation helpful? Give feedback.
All reactions