Open Rich-Harris opened 3 years ago
Snowpack has some image optimization plugins (https://www.snowpack.dev/plugins) and he said that one off them looked to fit his needs. Would plugins only be used in dev mode because Rollup is used in production mode?
Plugins are used in both cases. Rollup just bundles/analyses the resulting vanilla JS
Ok, it sounds like this is already addressed then, so we could probably close this
Maybe — need to take a look and see if there's anything missing from this: https://twitter.com/rchrdnsh/status/1336386125552779264
That description isn't very helpful, does the plugin transform the sourcecode as well as optimising the image? If it only does the image optimisation, it would be pretty limiting in terms of lazy-loading, generating srcsets, etc. Likewise some way to set the priority of the image (by adding preload tags to the head) would also be good. next/image
does all of this.
Image components aren't always the best solution but they offer a lot of flexibility depending on your needs.
I think the most comprehensive implementations will need to be framework specific, although I haven't thought about it a great deal.
With regards to this, I think a rollup/vite plugin for https://www.npmjs.com/package/sharp would likely be able to fulfil these requirements.
Hi all, thank you for considering this feature...
Just found this new (2 months old, it seems) plugin for Vite...starting to mess around with it with no success yet...seems potentially promising, however :-)
Hi all, thank you for considering this feature...
Just found this new (2 months old, it seems) plugin for Vite...starting to mess around with it with no success yet...seems potentially promising, however :-)
I'm in a close contact with the author and he's rewriting the whole library into core library and different bundler adapters including vite.
You can easily just do this to generate 6 different images together with their width and height info:
import {width, height, src} from "./image.jpg?format=webp;jpg&width=480;1024;1920&metadata"
Hey guys! I'm a little late to the party, but whatever 👋🏻
With regards to this, I think a rollup/vite plugin for https://www.npmjs.com/package/sharp would likely be able to fulfil these requirements.
vite-imagetools (or the core library more specifically) is basically just a wrapper around sharp, that let's you "execute" sharps image function in the import statement.
The basic idea though is to provide a "common api" that lets you specify how you want your image to be transformed and all the rest (wether the transformation is done by sharp or libvips directly, what buildtools you use, etc.) are completely irrelevant to the developer as all information is encoded in the url and not some config file.
It's not super reliable in vite yet(because vite is sometimes a bit quirky with static assets) and the docs are horrible, but I'm working on improvements and other buildtool integrations so if you have any questions, bug, feature requests just let me know 👍🏻
@JonasKruckenberg really appreciate your work on the vite-imagetools library. I was able to get vite-imagetools integrated with the current release of sveltekit and the node adapter!
const imagetools = require('vite-imagetools')
vite: { plugins: [imagetools({force: true})] }
3. wherever you want to import the transformed image (example shown below is in src/routes/$layout.svelte) :
Edit: With srcset:
import Logo1 from '../../static/title.png?w=300;400;500&format=webp&srcset'
Is it a better idea to put your images like this in the src
folder instead of importing from static
which requires weird ..
directory traversal?
Is it a better idea to put your images like this in the
src
folder instead of importing fromstatic
which requires weird..
directory traversal?
Definitely; I will likely end up moving them to src/static and updating svelte.config.cjs to reflect the updated location of the static assets.
hmmmm...
might it be worth considering adding some sort of official Svelte/Kit support for some sort of assets
folder in the src
folder for images that will be processed by plugins such as these in the future?
hmmmm...
might it be worth considering adding some sort of official Svelte/Kit support for some sort of
assets
folder in thesrc
folder for images that will be processed by plugins such as these in the future?
What more is SvelteKit supposed to do than is already possible per the comment above?
well, i don't have to make a lib
folder myself, as it's already there, with a nice and ergonomic alias as well, which would be super awesome to have for an images
folder...but maybe it's easy to make as alias, as I simply don't know how...but to be more specific, making image processing part of the default configuration out of the box, as it's such an important consideration for performant sites and apps...or maybe even a svelte-add
for image processing in the future...
Example integration of vite-imagetools and sveltekit with $static alias for src/static below:
const node = require('@sveltejs/adapter-node');
const pkg = require('./package.json');
const imagetools = require('vite-imagetools')
const path = require('path')
/** @type {import('@sveltejs/kit').Config} */
module.exports = {
kit: {
files: {
assets: 'src/static',
},
vite: {
resolve: {
alias: {
$static: path.resolve('src/static')
}
},
plugins: [imagetools({force: true})]
}
},
};
<script>
import Logo1 from '$static/title.png?webp&meta'
</script>
<img src={Logo1.src} alt="alt description">
<script>
import Logo1 from '$static/title.png?w=300;400;500&format=webp&srcset'
</script>
<img srcset={Logo1} type="image/webp" alt="testattribute"/>
very nice :-) We should probably move this kind of stuff to discord, but thank you!
There is so much boilerplate to write, even more if you need backward compatibility with WebP in a picture tag. But since svelte is a compiler, can't we just have an image transformation attribute right on the img tag that can create whatever we want - including lazy loading or fade-in transitions and more?
Well I went down the rabbit hole of trying to use vite-imagetools with SvelteKit yesterday. I have a site using Netlify CMS with local JSON files and wanted to dynamically load and resize images at build time along the lines of
<script lang="ts" context="module">
import page from "$data/pages/home.json";
export async function load() {
const { default: hero } = await import(`../static/${page.hero}.jpg?height=550&format=webp`)
return {
props: {
hero
}
}
}
</script>
It looks like it may be a limitation related to dynamic-import-vars, but when digging through that project I couldn't tell if the query parameters are expected to break. Based on the examples in vite-imagetools
I would have assumed query params alone wouldn't break production builds, but who knows.
I made sure to follow the workarounds for known limitations of the rollup plugin, like having the import starts with ./ or ../
and the file extension is included for the glob serach. It works fine in dev and actually works in production without the query params, I was hoping the dynamic-import-vars logic would have been able to strip off the query params and find the original image file.
I made a barebones repro of this at https://github.com/tonyfsullivan/sveltekit-imagetools-repro. Not sure if my use case here is just an outlier, it works flawlessly when the image file names are basic strings rather than dynamic variables.
Does anyone know if this should have worked for me, or where the bug/feature request would land? At the moment I have no idea if this would be related to vite
, vite-imagetools
, @rollup/plugin-dynamic-import-vars
, or if it's specific to SvelteKit somehow.
I don't know enough about svelte-kit to really debug this, but I can confirm that it is not an issue with vite-imagetools
as the plugin never gets told about the image in build mode. Seems to be related to vite-imagetools#34 so a problem with with the resolution of the import glob pattern
I wrote a little preprocessor to make using vite-imagetools even easier. Modifiers can be directly appended to the img's src or srcset prop and the imports will be added by the preprocessor.
const svelte = require("svelte/compiler")
/**
* @typedef Options
* @property {{ [tag: string]: string[] }} tags
*/
/**
* @typedef {import('svelte/types/compiler/interfaces').TemplateNode} TemplateNode
*/
/**
* @param {Options} [options]
* @returns {import('svelte/types/compiler/preprocess/types').PreprocessorGroup}
*/
function imagePreprocess(options = {}) {
const imports = {}
const tags = options.tags || {
img: ["src", "srcset", "data-src", "data-srcset"],
source: ["src", "srcset", "data-src", "data-srcset"],
}
return {
markup: ({ content, filename }) => {
const preparedContent = content
.replace(/<style[^>]*>[\w\W]+<\/style>/g, (match) => " ".repeat(match.length))
.replace(/<script[^>]*>[\w\W]+<\/script>/g, (match) => " ".repeat(match.length))
let ast
try {
ast = svelte.parse(preparedContent)
} catch (e) {
console.error(e, "Error parsing content")
return
}
/** @type {TemplateNode[]} */
const matches = []
svelte.walk(ast, {
enter: (node) => {
if (!["Element", "Fragment", "InlineComponent"].includes(node.type)) {
return
}
if (tags[node.name]) {
matches.push({ node, attributes: tags[node.name] })
}
},
})
const dependencies = []
const code = matches.reduce(
/**
* @param {{content: string, offset: number}} processed
* @param {{node: TemplateNode, attributes: string[]} match
* @param {number} index
*/
(processed, match, index) => {
const attributes = (match.node.attributes || []).filter(
(a) => a.type === "Attribute" && match.attributes.includes(a.name)
)
if (
attributes.length === 0 ||
(match.node.attributes || []).find((a) => a.name === "rel" && a.value[0].data === "external")
) {
return processed
}
let { content, offset } = processed
for (const attribute of attributes) {
if (attribute.value[0]?.type === "Text") {
const value = attribute.value[0]
if (value.data.startsWith("http")) continue
const symbol = `__IMAGE_${index}__`
const replacement = `{${symbol}}`
if (!imports[filename]) imports[filename] = {}
imports[filename][symbol] = value.data
dependencies.push(value.data)
content = content.substring(0, value.start + offset) + replacement + content.substring(value.end + offset)
offset += replacement.length - value.data.length
}
}
return { content, offset }
},
{ content, offset: 0 }
).content
return { code, dependencies }
},
script: ({ content, attributes, filename }) => {
if (!attributes.context) {
const localImports = Object.entries(imports[filename] || {})
if (localImports.length > 0) {
const dependencies = localImports.map(([symbol, path]) => path)
const code =
localImports.map(([symbol, path]) => `import ${symbol} from "${path}"`).join("\n") + "\n" + content
return { code, dependencies }
}
}
},
}
}
module.exports = imagePreprocess
I am interested in creating an image component for svelte but I am not sure how to approach it. We have build time approaches like:
svelte-image
vite-imagetools
A know limitation (https://github.com/matyunya/svelte-image/issues/31, https://github.com/JonasKruckenberg/imagetools/issues/5) of these approaches is that the images cannot be dynamic i.e. inside variables, instead they have to be referenced as string literals for preprocessing to work.
I have a few questions for what would a runtime implementation would look like?
getStaticProps
/getServerSideProps
etc?Some discussions on https://github.com/snowpackjs/astro/issues/492 that might be related.
I am interested in creating an image component for svelte but I am not sure how to approach it. We have build time approaches like:
- Svelte preprocessor like
svelte-image
- Vite / Rollup Plugin like
vite-imagetools
A know limitation (matyunya/svelte-image#31, JonasKruckenberg/imagetools#5) of these approaches is that the images cannot be dynamic i.e. inside variables, instead they have to be referenced as string literals for preprocessing to work.
I have a few questions for what would a runtime implementation would look like?
- Where would this code live? In an endpoint?
- Where should generated assets be stored?
- Are there any hooks in svelte kit that we can use to emit generated assets?
- Do we need additional hooks like NextJS
getStaticProps
/getServerSideProps
etc?Some discussions on snowpackjs/astro#492 that might be related.
hey, I played a bit with dynamic on demand optimization.
https://github.com/leimantas/svelte-image-caravaggio/blob/main/src/lib/components/Image.svelte
<Image/>
components are nice to add loading transitions which CSS can't yet do. But wrapping each image in such a stateful component harms loading performance. Perhaps such a task is better for a Svelte transition that hooks on image load
event (doesn't exist yet, just an idea):
<img transition:fade src="image.webp" alt="fade in when image loads">
Image processing on localhost becomes a huge build bottleneck when having to process multiple images each time per commit. It's not scalable. The job is better suited from a CMS or serverless function that provides an API similar to imagetools
, and most importantly caches the results:
<img src="image.jpg?w=300" srcset="image.jpg?w=300,image_2x.jpg?w=600 2x" alt="responsive widths from api">
On the other hand, network performance can be improved when one downloads all remotely sourced images and serves them from a single host, because we create fewer new connections. A config option downloadRemoteImages
in svelte.config.js
would suffice, or?
Another room for processing would be "width" and "height" parameters on images. Could also be an option on SvelteKit's config.
I've been working with SvelteKit and have tacked this issue with custom scripts wrapped around the framework. Would be nice to see this stuff get integrated. Perhaps I can upstream my changes if thats okay?
It would be nice for image processing to be available in SSG. I'm trying to switch from Gatsby to SvelteKit and the lack of a working <Image />
tag is the main thing that prevents me from doing so.
@paulkre I also think it would be great to see an image component similar to Gatsby! Here's an attempt at one which uses Vite Imagetools for processing. It's not perfect, and importing image data in pages is definitely verbose, but I think it emulates the Gatsby Image effect relatively well.
I've also put together a starter blog that uses this component and the metadata object in MDSvex to have optimised post thumbnails using the Image Component like you might find in a Gatsby blog.
Thank you everyone for the incredible work on Svelte & SvelteKit!
For anyone looking for a good library until this issue gets resolved: I just discovered https://github.com/xiphux/svimg which is very similar to https://github.com/matyunya/svelte-image but is maintained well and up-to-date :)
Anyone got a working configuration for svimg + Svelte Kit? I tried to set it up, but it doesn't seem to be doing much. It just creates empty folders for the processed image paths, but I see no errors, or really no other clues for what could be going wrong.
svimg does not currently support dynamic images, so maybe you are running into that issue...it seems like image processing is something that kit should probably try to handle, as none of the solutions so far seem to be able to handle dynamic images at all...that might be something that only kit can end up doing...but I'm not sure about that...starting down the path to try and figure that out...
I've looked at all the various image processing solutions and feel that https://github.com/JonasKruckenberg/vite-imagetools is probably the best and have been slowly working to make support for it a bit more official. You can dynamically choose from a list of images with it. But if you want something even more dynamic like handling an image pulled out of a database, that's going to be outside of what would make sense to build into SvelteKit.
Hi ben! XD
Caveat to everything I write...I am an amateur dev with little to no knowledge of build pipelines or how any of vite or kit actually work...no team either...just me...stumbling alone...in the dark :-)
So, I think that maybe this issue is almost 100% of my image use cases, which I am currently using vite-imagetools
for...
but it's...
not ideal...
Let's say I define images in my frontmatter for mdsvex files, like so:
---
title: Notes
subtitle: Every journey begins with a single sound.
portrait_image: /words/notes/portrait_notes.jpg
landscape_image: /words/notes/landscape_notes.jpg
alt: Making notes with a fountain pen.
---
<script context="module">
import NotesPortraitAVIF from '$images/words/notes.jpg?width=240;280;360;480;720;960;1920&format=avif&srcset'
import NotesLandscapeAVIF from '$images/words/notes.jpg?width=480;960;1280;1920;2560;3840&format=avif&srcset'
metadata.srcset_portrait_avif = NotesPortraitAVIF;
metadata.srcset_landscape_avif = NotesLandscapeAVIF;
import NotesPortraitJPG from '$images/words/notes.jpg?width=240;280;360;480;720;960;1920&format=jpg&srcset'
import NotesLandscapeJPG from '$images/words/notes.jpg?width=480;960;1280;1920;2560;3840&format=jpg&srcset'
metadata.srcset_portrait_jpg = NotesPortraitJPG;
metadata.srcset_landscape_jpg = NotesLandscapeJPG;
</script>
...I have to define all of the srcset
s for different image types and crops manually for EVERY mdsvex file, and add them to the metadata in a module script, as you can see from the above code.
...so this sort of works...although I cannot ACTUALLY use AVIF images at all, because netlify will not build the site, because AVIF images take too much processing power and time??? to make...I think...still hazy on that one, tbh...talking to the netlify folks about it more in the near future...
Then this kind of frontmatter would be used in the following way on a svelte page in an [#each}
block:
{#each sortedWords as {
slug,
title,
subtitle,
portrait_image,
landscape_image,
srcset_portrait_avif,
srcset_landscape_avif,
srcset_portrait_jpg,
srcset_landscape_jpg
} }
<Card
{slug}
{title}
{subtitle}
{portrait_image}
{landscape_image}
{srcset_portrait_avif}
{srcset_landscape_avif}
{srcset_portrait_jpg}
{srcset_landscape_jpg}
/>
{/each}
...where the Card
component uses the <picture>
element to use the correct srcset
, among other things.
So, as you can see, I currently have to manually add all this information to the frontmatter of every article, and I can't even use AVIF images at all because they simply stop the build entirely, it would seem.
So ideally I would want to be able to avoid all of this and only define the images in the frontmatter:
---
title: Notes
subtitle: Every journey begins with a single sound.
portrait_image: /words/notes/portrait_notes.jpg
landscape_image: /words/notes/landscape_notes.jpg
alt: Making notes with a fountain pen.
---
...then all this stuff:
'$images/words/notes.jpg?width=480;960;1280;1920;2560;3840&format=jpg&srcset'
for every possible image type and crop, etc...is defined in a config file for images or something...dunno...
Then transforming the images would happen locally and cached when dev
happens so no images would be transformed by the server. Image transforms should also not happen every time a dev
or build
is run, either, as that would take forever and be very wasteful, especially with AVIF image transforms. Only NEW images would be transformed, and ideally only once, or whenever a change is made...some type of diffing system maybe?
The reason I say kit should handle it because only kit and vite know about what would be passed into a prop in a component in an {#each}
block at dev
or build
time...I think...???
BUT...I have no idea how kit or vite work at all, so I have no idea if that is the best idea or even possible, etc...
So, this is what I would like to be able to do...dunno how to go about it or what the best approach might be...maybe kit should NOT do it, as you mentioned, but if so, do you have any thoughts as to how it might be done? I'm just starting to learn about node
and npm
scripts, so maybe that could be a possibility as well...
Anyhoo, I hope this makes sense, and maybe there is a way to solve these issues in some capacity XD
Hey @rchrdnsh, this can be made much nicer with a couple of options. First I suggest using the defaultDirectives
option so that you don't have to specify query parameters for the most part. Here's the vite.config.js
I use in my project:
import * as path from 'node:path';
import { sveltekit } from '@sveltejs/kit/vite';
import { imagetools } from 'vite-imagetools';
const fallback = {
'.heic': 'jpg',
'.heif': 'jpg',
'.avif': 'png',
'.jpeg': 'jpg',
'.jpg': 'jpg',
'.png': 'png',
'.tiff': 'jpg',
'.webp': 'png',
'.gif': 'gif'
};
/** @type {import('vite').UserConfig} */
const config = {
plugins: [
imagetools({
defaultDirectives: (url) => {
const ext = path.extname(url.pathname);
return new URLSearchParams(`format=avif;webp;${fallback[ext]}&as=picture`);
}
}),
sveltekit()
]
};
export default config;
I haven't tried putting images in frontmatter yet, but for my .svelte
files I'm using svelte-preprocess-import-assets
to automatically extract and import image URLs from code like <Image src="./_images/venostent.svg" alt="VenoStent" />
. Then imagetools will automatically create webp and avif versions of the file in this example. Here's an example of setting up the preprocessor:
import sveltePreprocess from 'svelte-preprocess';
import { vitePreprocess } from '@sveltejs/kit/vite';
import { importAssets } from 'svelte-preprocess-import-assets';
/** @type {import('@sveltejs/kit').Config} */
export default {
preprocess: [
importAssets({
sources: (defaultSources) => {
return [
...defaultSources,
{
tag: 'Image',
srcAttributes: ['src']
}
]
}
}),
vitePreprocess()
]
...
My Image
component roughly looks like:
<script>
/** @type {string | import('vite-imagetools').Picture} */
export let src;
/** @type {string} */
export let alt;
</script>
{#if typeof src === 'string'}
<img {src} {alt} {...$$restProps} />
{:else}
<picture>
{#each Object.entries(src.sources) as [format, images]}
<source srcset={images.map((i) => `${i.src} ${i.w}w`).join(', ')} type={'image/' + format} />
{/each}
<img src={src.img.src} width={src.img.w} height={src.img.h} {alt} {...$$restProps} />
</picture>
{/if}
I realize of course this is a fair bit of configuration required at the moment. We've just done new releases of both imagetools
and svelte-preprocess-import-assets
today to make this all work pretty well. The next step will be packaging it all up together to be easier to setup.
Thank you for the reply @benmccann XD
I must admit, however, that i do not really understand any of it ATM...but I will try!
Can't find anything about defaultDirectives
in the imagetools
docs, tho...
But thank you for helping and putting time and effort into this issue :-)
Can't find anything about defaultDirectives in the imagetools docs, tho...
See https://github.com/JonasKruckenberg/imagetools/blob/main/packages/vite/README.md#defaultdirectives
thank you!
trying this all out in a kit project and i'm getting the following error:
supportedExtensions is not defined
ReferenceError: supportedExtensions is not defined
at Object.defaultDirectives (file:///Users/rchrdnsh/Code/Svelte/RYKR-kit/vite.config.js.timestamp-1665513195017.mjs:21:11)
at Context.load (file:///Users/rchrdnsh/Code/Svelte/RYKR-kit/node_modules/vite-imagetools/dist/index.mjs:36:68)
at Object.load (file:///Users/rchrdnsh/Code/Svelte/RYKR-kit/node_modules/vite/dist/node/chunks/dep-6b3a5aff.js:41076:46)
at async loadAndTransform (file:///Users/rchrdnsh/Code/Svelte/RYKR-kit/node_modules/vite/dist/node/chunks/dep-6b3a5aff.js:37304:24)
...not sure what that is or where it should go or where it is coming from...I guess...
this is what my vite config looks like at the moment...I suppose I need to make a supportedExtensions
?
import { sveltekit } from '@sveltejs/kit/vite';
// import { defineConfig } from 'vite';
import { imagetools } from 'vite-imagetools';
import mkcert from 'vite-plugin-mkcert';
/** @type {import('vite').UserConfig} */
const config = {
// server: {
// hmr: false
// },
ssr: {
noExternal: [
'svelte-stripe-js',
'style-value-types',
'popmotion',
'framesync'
]
},
plugins: [
sveltekit(),
// imagetools(),
imagetools(
{
defaultDirectives: (url) => {
const extension = url.pathname.substring(url.pathname.lastIndexOf('.') + 1);
if (supportedExtensions.includes(extension)) {
return new URLSearchParams({
format: 'avif;webp;' + extension,
picture: true
});
}
return new URLSearchParams();
}
}
),
mkcert({hosts: ['localhost', 'app.local']})
]
};
export default config;
@rchrdnsh I updated my post to include that line. Hope it helps
I am also interested in the putting images in frontmatter use case and am very pleased by the progress. Thank you Ben and others involved in moving this forward.
EDIT Re:
I realize of course this is a fair bit of configuration required at the moment. We've just done new releases of both
imagetools
andsvelte-preprocess-import-assets
today to make this all work pretty well. The next step will be packaging it all up together to be easier to setup.
…Is it worth making a demo of vite-imagetools all hooked up? Or will it get easier quickly enough that it's worth waiting instead of turning Ben's comment into a repo? 🤔💭
oh geez! i must have missed the extensions, my bad @benmccann 😬
const supportedExtensions = ['png', 'jpg', 'jpeg'];
...seems to work now...or, at least...I'm apparently missing images, lol...gonna sort them all out now 😁
ok, was confused about the hidden messages...i get that you added it now...
ok, so, I'm trying to apply this technique to markdown frontmatter image and I am getting the following error, after it worked ok on the markdown inline images:
Cannot convert undefined or null to object
TypeError: Cannot convert undefined or null to object
at Function.entries (<anonymous>)
at eval (/src/library/images/Image.svelte:21:137)
at Object.$$render (/node_modules/svelte/internal/index.mjs:1771:22)
at eval (/src/routes/words/+page.svelte:48:92)
at Module.each (/node_modules/svelte/internal/index.mjs:1737:16)
at Object.default (/src/routes/words/+page.svelte:46:29)
at eval (/src/library/layout/Grid.svelte:19:74)
at Object.$$render (/node_modules/svelte/internal/index.mjs:1771:22)
at Object.default (/src/routes/words/+page.svelte:43:94)
at eval (/src/library/layout/Container.svelte:41:54)
...dunno what's going on, but maybe because the image file path has not been determined yet at the time the code runs its undefined in the Image
component?
Made an example repo with Imagetools all wired up, no frontmatter example yet, but it's a start and PRs + input very welcome. Uses a NASA pic, hooray public domain.
The vercel branch is the production branch on Vercel, it specifies the Vercel adapter and passes the edge: true
option to the adapter for Edge Functions. Rich covered this at Vite Conf.
Again, any advice or suggestions appreciated.
UPDATE 1, 20 Oct: Now available on Netlify as well, building off the netlify branch. Had to fight with sveltejs/adapter-netlify a bit, ended up downgrading 1.0.0-next.81
to 1.0.0-next.78
to get build and static file serving working in unison (should I file a bug?).
UPDATE 2, 21 Oct: Appears though iOS 16 adds support for AVIF, AVIF images do not currently render in iOS Safari if your phone is in Lockdown Mode. Lockdown Mode can be disabled on a per website basis.
@rdela Thank you for the example repo! Any hope to see a solution to optimize images in markdown (body and frontmatter)? It's something that Gatsby is doing well and it's missing a bit in SvelteKit…
Is there a way you could import a directory of static images and have svelte/vite batch process them? So rather than having to specific a specific file path in vite-imagetools, you could just specific a folder, or maybe a blob? And then vite-imagetools processes all of them, waits for all the promises to resolve, and returns an array of images?
@elibenton — I've had luck batch importing/glob'ing images with this component. Note that all targeted images live in $lib/assets/picture/*
.
Ideally I could find a way to dynamically import images, though I'm interested in trying Ben's approach above first. Until then I have this component.
<script context="module">
const pictures = import.meta.glob(
'/src/lib/assets/picture/*.{heic,heif,avif,jpg,jpeg,png,tiff,webp,gif}',
{
query: {
format: 'webp;avif;jpg',
width: '300;600;1200',
picture: '',
flatten: '',
background: '#ffffff'
},
import: 'default',
eager: true
}
);
</script>
<script>
/** REQUIRED */
/** @type {String} required */
export let src = undefined;
/** @type {String} required */
export let alt = undefined;
/** OPTIONAL */
/** @type {Boolean} */
export let draggable = false;
/** @type {('sync' | 'async' | 'auto')} */
export let decoding = 'async';
/** @type {('lazy' | 'eager' | 'auto')} */
export let loading = 'lazy';
/** @type {String} */
let classes = '';
export { classes as class };
/** @type {String|{{ fallback: { w: Number, h: Number, src: String }, sources?: Object }}} the selected image */
$: picture = pictures[src];
</script>
<picture>
{#each Object.entries(picture.sources) as [format, images]}
<source srcset={images.map((img) => `${img.src} ${img.w}w`).join(', ')} type="image/{format}" />
{/each}
<img
class={classes}
{decoding}
{loading}
{draggable}
src={picture.fallback.src}
{alt}
width={picture.fallback.w}
height={picture.fallback.h}
/>
</picture>
Hi all, if none of the available options are working for you, and you are looking to:
import.meta.glob
messstatic/
instead of src/lib/
I have written a new tool: web-image-gen.
It does not use the vite pipeline, but plays along nicely with it.
@brev thank you for the ressource! It doesn't seems to parse and optimize markdown images, right?
Updated Aug 2023 by @benmccann - this thread has gotten quite long, so I've summarized some key points here. We have some docs on the site (https://kit.svelte.dev/docs/assets) that share some helpful basics. This issue discusses a potential future implementation within SvelteKit
Static optimization with an
Image
componentVite's build pipeline will handle assets that you
import
: https://vitejs.dev/guide/assets.htmlYou can use
vite-imagetools
to transform those images as part of Vite's pipeline. E.g. the most common transform might be to generate avif and webp versions of an image. You can use thedefaultDirectives
option to set a project-wide default or you can add query parameters to handle images individually. You can also import a directory of images using Vite's import.meta.glob with its query option.You could use something like bluwy/svelte-preprocess-import-assets to let users simply write
img
tags that get converted to import statements for Vite to process.A discussion of how to set this up is included at https://github.com/sveltejs/kit/issues/241#issuecomment-1274046866 and further demonstrated in https://github.com/sveltejs/kit/issues/241#issuecomment-1286296389.
Static optimization powered by a preprocessor
A problem with using an
Image
component in Svelte is that it requires the usage of:global
to style it and it's difficult to handle events on it. It's possible some of these issues could be addressed in Svelte itself (e.g. there was a community proposal regarding event forwarding https://github.com/sveltejs/rfcs/pull/60), but at the current time there are no concrete plans around this.One solution to this would be to use a preprocessor to convert:
Into something like:
This actually scales very well since https://github.com/sveltejs/svelte/pull/8948.
However, this approach doesn't work as well for something like the SvelteKit showcase or
import.meta.glob
because it requires the presence of animg
tag.Dynamic optimization
You could also implement a function to alter a URL and output the CDN's URL (e.g. https://github.com/sveltejs/kit/pull/10323). Including this manually may become cumbersome, but as with the static optimization case, you could use something like bluwy/svelte-preprocess-import-assets to let users simply write
img
tags that get converted to use this function. The unpic library is an example of this approach. This approach works really well when users have a CDN available and some hosts like Vercel have CDNs as included offerings. For users that want to deploy to somewhere like GitHub pages the static approach might work better and so it may make sense to offer dynamic optimzation alongside one of the static optimization approaches.