@svelte-put/preprocess-inline-svg
svelte preprocessor to inline static svg at build time
Introduction
Current solutions (that I know of) for inlining SVGs in Svelte land require either runtime logics or a component-oriented strategy. These solutions are acceptable in most cases but have been proven to be problematic when additional styling / attributes are needed for the inlined svg element.
This package tries to achieve zero runtime footprint with no additional component. The idea is to enable this:
minimal usage api
<svg inline-src="google/info"></svg>
which will automatically read from the source svg and inline it at build time. Additional styling and attributes can now be added idiomatically as with any other html element.
Prior Art
- svelte-inline-svg : runs at runtime as Svelte component.
- vite-plugin-svelte-svg : runs at build time as Svelte component; svg can be processed with svgo .
- svg-to-svelte : convert SVG files to svelte components.
Runtime Solution
This processor is best for static svgs like icons and pictograms. For dynamic svgs (i.e loaded from network), use @svelte-put/inline-svg - an action-based strategy with a similar api.
runtime alternative
<svg use:inlineSrc={'https://example.com/icon.svg'}></svg>
Installation
terminal
npm install --save-dev @svelte-put/preprocess-inline-svg
Quick Start
preprocess-inline-svg
provides both a vite plugin
and a svelte preprocessor with the same core functionality.
Use svelte preprocessor if you are not using vite
. Otherwise, vite plugin is
recommended.
Given the following (vite.config.ts,js
or svelte.config.js
)
quick start - config
import { inlineSvg } from '@svelte-put/preprocess-inline-svg/vite';
import { sveltekit } from '@sveltejs/kit/vite';
import { defineConfig } from 'vite';
export default defineConfig({
plugins: [
// make sure inlineSvg comes before `svelte` or `sveltekit` plugin
inlineSvg(
[
{
directories: 'src/assets/icons',
attributes: {
class: 'icon',
width: '20',
height: '20',
},
},
{
directories: 'src/assets/pictograms',
},
],
{
inlineSrcAttributeName: 'inline-src',
},
),
sveltekit(),
],
});
and the asset files as follow
quick start - assets
src/assets
|
|-- icons
|-- svelte.svg
|
|-- google
|-- arrow-right.svg
|-- simpleicons
|-- github.svg
|
|-- pictograms
|-- diagram.svg
we can now do
quick start - usage
<!-- this will have width="20" height="20" as specified in the config -->
<svg inline-src="svelte" />
<!-- nested -->
<svg inline-src="google/arrow-right.svg" />
<!-- .svg can be omitted -->
<svg inline-src="simpleicons/github" />
<!-- with custom attributes -->
<svg inline-src="diagram" width="100" height="100" />
<!-- alternatively, you can provide a per-case path that is relative to the current source file -->
<svg inline-src="./local-icon.svg" />
<!-- if the source svg is not found for any of the above, an error will be thrown -->
The .svg
extension may be omitted from the path provided to
inline-src
.
Attributes & Inner HTML
Attributes provided to the source svg
element will be kept after build
and override the existed ones in the inlined SVG.
merging attributes
<svg inline-src="path/icon" width="100" height="100" class="c-icon"></svg>
InnerHTML of the source svg
element will be replaced with the that
from the inlined SVG.
merging attributes
<svg inline-src="path/icon">anything in here will be replaced</svg>
If you have a use case where it is useful to append/prepend the innerHTML of inlined SVGs rather than replace it, please raise an issue over at github . For now, let's keep things simple.
Typing the `inline-src` Attribute
This feature is only available when the vite plugin is used, see Quick Start for more information about vite plugin vs svelte preprocessor config.
preprocess-inline-svg
vite plugin provides automatic typing generation (enabled by
default) for the
inline-src
attribute when multiple svg sources are configured. There are a couple of
steps needed to get this working.
1. Set the inlineSrcAttributeName
option to a non data
attribute (this
is because currently data attributes do not get intellisense with svelte-check
, for
some strange reason).
setting inlineSrcAttributeName
import { inlineSvg } from '@svelte-put/preprocess-inline-svg/vite';
import { defineConfig } from 'vite';
export default defineConfig({
plugins: [
inlineSvg(
[
// ... sources as in Quick Start section
],
{
inlineSrcAttributeName: 'inline-src',
},
),
// ...
],
});
2. Set typing in a additional-svelte-typing.d.ts
file, following instruction here from the svelte language-tools team
src/additional-svelte-typing.d.ts
declare namespace svelteHTML {
interface SVGAttributes {
'inline-src'?: import('@svelte-put/inline-svg/preprocess').Source;
}
}
The inline-src
attribute should be strongly typed now. For example, with the assets
as shown in the
Quick Start section, the typing should be the following:
Source
// import('@svelte-put/inline-svg/preprocess').Source
export type Source =
| `./${string}`
| `../${string}`
| 'svelte'
| 'google/arrow-right'
| 'simpleicons/github'
| 'diagram';
To disable this typing generation, set sourceTypingGeneration
option to
false
.
Limitations
preprocess-inline-svg
only works in svelte markup, i.e in the template part of svelte
source files. The following will not work
only support svelte markup
<script>
let html = `<svg inline-src="path/icon"></svg>`;
</script>
<!-- eslint-disable-next-line svelte/no-at-html-tags -->
{@html html}
preprocess-inline-svg
does not support inline-src
as a variable. I.e, the
following will not work
dynamic attribute is not support
<script lang="ts">
export let icon: string;
</script>
<svg inline-src={icon} />
This is because it is difficult for the preprocessor to analyze a variable to determine its immutability at build time, i.e a variable is meant to be changed. In these case, some workarounds are
- use
if-else
statements to switch between differentinline-src
as literal strings, or - use @svelte-put/inline-svg as a runtime action-based strategy instead.
If you have an idea for improvements, please raise an issue over at github . Thanks!
Customization
All options are optional. By default inline-svg
can be used with no config at all, in
which case svg source paths are resolved relative to the svelte source file it is specified in.
Note that path alias is not supported in inline-src
! For example,
"$lib/src/assets/..."
will not work.
Alternatively, the directories
option can be specified (as seen in the "Quick Start"
section) to conveniently omit the need for verbose pathnames.
Input to inline-svg
can be provided as a single object, or an array of such (as seen
in the "Quick Start" section), helpful for organization purposes or when different configurations
are needed for different directories.
When input is an array of configs, the following applies:
There can only be one config object without the directories
option (default config).
If multiple are found, an error will be thrown at build. If none is provided, the internal default
config is used.
Each svg source will then be searched top down in the config until a match is found, or else an error will be thrown.
Local svg (local to svelte source file) always has the highest priority and will use default config as described above.
Option
Description
directories
directories relative to which the svg source paths will be resolved.
type: string[] | string
default: []
attributes
default attributes to add to the svg element, will override the attributes from the svg
source, but be overridden by the attributes from the element itself (in svelte source)
type: Record<string, string>
default: {}
serializeOptions
options for hast-util-to-html during serialization
type: Object
default: { space: 'svg', allowDangerousCharacters: true }
inlineSrcAttributeName
the attribute to get the svg source from
type: string
default: 'inline-src'
keepInlineSrcAttribute
whether to keep the inline src attribute after build (for reference, for example)
type: boolean
default: false
Further Work
This package is kept minimal as it has served its purpose for all of my need. If you find this package to lack certain features that are essential to your use cases, please file an issue at github and let me know. Thank you!
Some possibilities:
- Support for svgo?
- Support for fetching remote svg in
inline-src
at build time?