├── .github └── FUNDING.yml ├── .gitignore ├── .npmrc ├── .prettierrc ├── LICENSE ├── README.md ├── eslint.config.js ├── generate-tsd.js ├── jsconfig.json ├── package-lock.json ├── package.json ├── playwright.config.js ├── postcss.config.js ├── src ├── ambient.d.ts ├── app.d.ts ├── app.html ├── app.pcss ├── lib │ ├── FxParallax.svelte │ ├── FxReveal.svelte │ ├── Picture.svelte │ ├── SvelteImg.svelte │ ├── index.js │ ├── utils.js │ └── vite.js └── routes │ ├── +layout.js │ ├── +layout.svelte │ ├── +page.svelte │ └── assets │ ├── 640 │ ├── 01.jpg │ ├── 02.jpg │ ├── 03.jpg │ ├── 04.jpg │ ├── 05.jpg │ ├── 06.jpg │ ├── 07.jpg │ ├── 08.jpg │ ├── 09.jpg │ ├── 10.jpg │ ├── 11.jpg │ └── 12.jpg │ ├── 1920 │ ├── 1.jpg │ ├── 2.jpg │ ├── 3.jpg │ ├── 4.jpg │ └── 5.jpg │ ├── hero.jpg │ └── pllx.jpg ├── static ├── .nojekyll └── favicon.png ├── svelte.config.js ├── tailwind.config.js ├── tests └── test.js └── vite.config.js /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: zerodevx 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | /build 4 | /.svelte-kit 5 | /dist 6 | .env 7 | .env.* 8 | !.env.example 9 | vite.config.js.timestamp-* 10 | vite.config.ts.timestamp-* 11 | temp/ 12 | test-results/ 13 | -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | engine-strict=true 2 | resolution-mode=highest 3 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "useTabs": false, 3 | "singleQuote": true, 4 | "trailingComma": "none", 5 | "printWidth": 100, 6 | "plugins": ["prettier-plugin-svelte", "prettier-plugin-tailwindcss"], 7 | "overrides": [{ "files": "*.svelte", "options": { "parser": "svelte" } }], 8 | "semi": false, 9 | "proseWrap": "always", 10 | "svelteSortOrder": "options-scripts-markup-styles", 11 | "svelteIndentScriptAndStyle": false 12 | } 13 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | ISC License 2 | 3 | Copyright (c) 2024, Jason Lee 4 | 5 | Permission to use, copy, modify, and/or distribute this software for any 6 | purpose with or without fee is hereby granted, provided that the above 7 | copyright notice and this permission notice appear in all copies. 8 | 9 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10 | WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11 | MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12 | ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13 | WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14 | ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15 | OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # svelte-img 2 | 3 | > High-performance responsive/progressive images for SvelteKit. 4 | 5 | Automatically transform local images into multiple widths and next-gen formats, then render a 6 | minimally invasive LQIP-included HTML representation into your SvelteKit project. 7 | 8 | Includes special effects: 9 | 10 | - [x] Fade-in on image reveal 11 | - [x] Parallax vertical scroll effect 12 | 13 | Hope you like cats. Demo: https://zerodevx.github.io/svelte-img/ 14 | 15 | ## Install 16 | 17 | Install the package: 18 | 19 | ``` 20 | $ npm i -D @zerodevx/svelte-img 21 | ``` 22 | 23 | Add `imagetools` plugin into your `vite.config.js`: 24 | 25 | ```js 26 | import { defineConfig } from 'vite' 27 | import { sveltekit } from '@sveltejs/kit/vite' 28 | import { imagetools } from '@zerodevx/svelte-img/vite' 29 | 30 | export default defineConfig({ 31 | plugins: [sveltekit(), imagetools()] 32 | }) 33 | ``` 34 | 35 | Optionally, to silence typescript 36 | [warnings](https://github.com/JonasKruckenberg/imagetools/issues/160) on image imports, create a new 37 | file at `src/ambient.d.ts`: 38 | 39 | ```js 40 | // Squelch warnings of image imports from your assets dir 41 | declare module '$lib/assets/*' { 42 | var meta 43 | export default meta 44 | } 45 | ``` 46 | 47 | ### Under the hood 48 | 49 | Local image transformations are delegated to the excellent 50 | [`vite-imagetools`](https://github.com/JonasKruckenberg/imagetools) with a custom `run` directive. 51 | This preset generates optimised images with sensible defaults, including a `base64` low-quality 52 | image placeholder. 53 | 54 | Invoke the preset with the `?as=run` query param: 55 | 56 | ```js 57 | import imageMeta from 'path/to/asset?as=run' 58 | ``` 59 | 60 | ## Usage 61 | 62 | Use anywhere in your Svelte app: 63 | 64 | 65 | ```html 66 | 71 | 72 | Very meow 73 | ``` 74 | 75 | The image component renders into: 76 | 77 | ```html 78 | 79 | 83 | 87 | 91 | Very meow 101 | 102 | ``` 103 | 104 | ## Features 105 | 106 | ### Change default widths/formats 107 | 108 | By default, `svelte-img` generates 9 variants of an original full-sized image - at `480/1024/1920` 109 | widths in `avif/webp/jpg` formats; and a `16px webp/base64` low-quality image placeholder (LQIP). 110 | 111 | To change this globally, edit your `vite.config.js`: 112 | 113 | ```js 114 | import ... 115 | 116 | // By default, `run` is set to 'w=480;1024;1920&format=avif;webp;jpg' (9 variants) 117 | export default defineConfig({ 118 | plugins: [ 119 | sveltekit(), 120 | imagetools({ 121 | profiles: { 122 | // Now we change `run` to generate 4 variants instead: 640/1280w in webp/jpg 123 | run: new URLSearchParams('w=640;1280&format=webp;jpg') 124 | } 125 | }) 126 | ] 127 | }) 128 | ``` 129 | 130 | > [!NOTE] 131 | > `runDefaultDirectives` is deprecated and will be removed in the next major; use `profiles` 132 | > instead. When a profile is not used, behaviour falls back to standard `vite-imagetools`, which in 133 | > turn take defaults from `defaultDirectives` as usual, so both can co-exist. 134 | 135 | ### Profiles 136 | 137 | Use profiles to manage multiple defaults. Define in your `vite.config.js`: 138 | 139 | ```js 140 | export default defineConfig({ 141 | plugins: [ 142 | sveltekit(), 143 | imagetools({ 144 | profiles: { 145 | sm: new URLSearchParams('w=640&format=webp;jpg'), 146 | lg: new URLSearchParams('w=640;1280;1920&format=webp;jpg') 147 | } 148 | }) 149 | ] 150 | }) 151 | ``` 152 | 153 | Then invoke in your app: 154 | 155 | ```js 156 | import sm from '$lib/a/1.jpg?as=sm' // use `sm` profile 157 | import lg from '$lib/a/2.jpg?as=lg' // use `lg` profile 158 | import normal from '$lib/a/3.jpg?as=run' 159 | ``` 160 | 161 | ### On a per-image basis 162 | 163 | Widths/formats can be applied to a particular image. From your `.svelte` file: 164 | 165 | 166 | ```html 167 | 172 | 173 | cat 174 | ``` 175 | 176 | > [!NOTE] 177 | > Order of `format` matters - the _last_ format is used as the fallback image. 178 | 179 | If just **one** variant is generated, then only the `` tag renders, so: 180 | 181 | 182 | ```html 183 | 188 | 189 | cat 190 | ``` 191 | 192 | Renders into: 193 | 194 | ```html 195 | cat 204 | ``` 205 | 206 | ### Change LQIP width 207 | 208 | The `run` directive takes an optional parameter that sets the LQIP's width. Using `?as=run` defaults 209 | to `16px` LQIP - functionally equivalent to `?as=run:16`. Increase for a higher quality LQIP (eg. 210 | `?as=run:32` for `32px` LQIP) at the expense of a larger inline `base64` (larger HTML size). 211 | 212 | To disable LQIP, set `?as=run:0`. 213 | 214 | For a dominant single-colour background, set `?as=run:1`, so: 215 | 216 | 217 | ```html 218 | 222 | 223 | 224 | cat 225 | ``` 226 | 227 | Renders into: 228 | 229 | 230 | ```html 231 | 232 | 233 | 234 | 235 | ``` 236 | 237 | ### Other transformations 238 | 239 | The full [repertoire](https://github.com/JonasKruckenberg/imagetools/blob/main/docs/directives.md) 240 | of transformation directives offered by 241 | [`vite-imagetools`](https://github.com/JonasKruckenberg/imagetools) can be used. 242 | 243 | 244 | ```html 245 | 250 | 251 | cat 252 | ``` 253 | 254 | ### Responsive Image Sizes 255 | 256 | Use the `sizes` attribute to define media conditions that provide hints as to which image size to 257 | select when those conditions are true. Read up more on 258 | [responsive images and the picture element](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/picture). 259 | 260 | ```html 261 | 265 | 266 | 271 | cat 272 | ``` 273 | 274 | Renders into: 275 | 276 | ```html 277 | 278 | 283 | 288 | 293 | cat 302 | 303 | ``` 304 | 305 | ### Lazy loading 306 | 307 | `svelte-img` utilises the browser's native lazy loading capability by setting the `loading="lazy"` 308 | attribute on the rendered `` tag by default. This is supported by 309 | [most modern browsers](https://caniuse.com/loading-lazy-attr). To load an image eagerly instead: 310 | 311 | 312 | ```html 313 | 317 | 318 | cat 319 | ``` 320 | 321 | ### Batch loading local images 322 | 323 | Use `Vite`'s `import.meta.glob` [feature](https://vitejs.dev/guide/features.html#glob-import). 324 | 325 | 326 | ```html 327 | 337 | 338 | {#each images as src} 339 | cat 340 | {/each} 341 | ``` 342 | 343 | ### Remote images from an API 344 | 345 | Use the `svelte-img` component on its own by passing a `src` object, like so: 346 | 347 | 348 | ```html 349 | 361 | 362 | cat 363 | ``` 364 | 365 | ### Blurred image placeholders 366 | 367 | Natively, browsers do already apply _some_ blur when displaying low resolution images. That's enough 368 | for me, but you can apply your own using CSS. 369 | 370 | 371 | ```html 372 | 382 | 383 |
384 | (loaded = true)} /> 385 |
386 |
387 | 388 | 403 | ``` 404 | 405 | ## Special Effects 406 | 407 | ### Fade-in on reveal 408 | 409 | Reveal images with a fade-in effect (aka medium.com) when they are loaded **and** in the viewport. 410 | 411 | 412 | ```html 413 | 417 | 418 | cat 419 | 420 | 431 | ``` 432 | 433 | ### Parallax 434 | 435 | Apply a vertical parallax scrolling effect to an image, where `factor` is a decimal value between 0 436 | and 1, that controls how much slower the element scrolls, relative to the scrolling speed: 437 | 438 | - A value closer to 0 is faster, while a value closer to 1 is slower. 439 | - A value of 1 behaves normally. 440 | - A value of 0 effectively makes the element fixed on the page. 441 | 442 | The default factor is `0.75`. 443 | 444 | 445 | ```html 446 | 450 | 451 | cat 452 | 453 | 459 | ``` 460 | 461 | ## Development 462 | 463 | Library is packaged via [SvelteKit](https://kit.svelte.dev/docs/packaging). Standard Github 464 | [contribution workflow](https://docs.github.com/en/get-started/quickstart/contributing-to-projects) 465 | applies. 466 | 467 | ### Tests 468 | 469 | End-to-end testing via [Playwright](https://github.com/microsoft/playwright). To run tests 470 | headlessly: 471 | 472 | ``` 473 | $ npm run test 474 | ``` 475 | 476 | ## Changelog 477 | 478 | Please refer to the [releases](https://github.com/zerodevx/svelte-img/releases) page. 479 | 480 | ## License 481 | 482 | ISC 483 | -------------------------------------------------------------------------------- /eslint.config.js: -------------------------------------------------------------------------------- 1 | import js from '@eslint/js' 2 | import svelte from 'eslint-plugin-svelte' 3 | import prettier from 'eslint-config-prettier' 4 | import globals from 'globals' 5 | 6 | /** @type {import('eslint').Linter.Config[]} */ 7 | export default [ 8 | js.configs.recommended, 9 | ...svelte.configs['flat/recommended'], 10 | prettier, 11 | ...svelte.configs['flat/prettier'], 12 | { 13 | languageOptions: { 14 | globals: { 15 | ...globals.browser, 16 | ...globals.node, 17 | grecaptcha: true, 18 | gtag: true 19 | } 20 | }, 21 | rules: { 22 | 'no-tabs': 'error', 23 | 'no-unexpected-multiline': 'error', 24 | 'svelte/no-at-html-tags': 'off' 25 | } 26 | }, 27 | { 28 | ignores: ['build/', '.svelte-kit/', 'dist/', 'temp/'] 29 | } 30 | ] 31 | -------------------------------------------------------------------------------- /generate-tsd.js: -------------------------------------------------------------------------------- 1 | import { sveld } from 'sveld' 2 | 3 | sveld({ 4 | input: 'dist/index.js', 5 | typesOptions: { 6 | outDir: 'dist' 7 | } 8 | }) 9 | -------------------------------------------------------------------------------- /jsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./.svelte-kit/tsconfig.json", 3 | "compilerOptions": { 4 | "esModuleInterop": true, 5 | "forceConsistentCasingInFileNames": true, 6 | "resolveJsonModule": true, 7 | "skipLibCheck": true, 8 | "sourceMap": true, 9 | "module": "NodeNext", 10 | "moduleResolution": "NodeNext", 11 | "allowJs": true, 12 | "checkJs": true 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@zerodevx/svelte-img", 3 | "version": "2.1.2", 4 | "description": "High-performance responsive/progressive images for SvelteKit", 5 | "author": "Jason Lee ", 6 | "scripts": { 7 | "dev": "vite dev", 8 | "build": "npm run lint && vite build", 9 | "preview": "vite preview", 10 | "check": "svelte-kit sync && svelte-check --tsconfig ./jsconfig.json", 11 | "check:watch": "svelte-kit sync && svelte-check --tsconfig ./jsconfig.json --watch", 12 | "format": "prettier --write .", 13 | "lint": "prettier --check . && eslint && npm run check", 14 | "test": "playwright test", 15 | "package": "npm run lint && svelte-package && node generate-tsd && npx publint", 16 | "deploy": "npx gh-pages -d build -t -f" 17 | }, 18 | "dependencies": { 19 | "vite-imagetools": "^6.2.9" 20 | }, 21 | "peerDependencies": { 22 | "svelte": "^3.55.1 || ^4.0.0 || ^5.0.0" 23 | }, 24 | "devDependencies": { 25 | "@eslint/js": "^9.9.0", 26 | "@fontsource-variable/inter": "^5.1.0", 27 | "@iconify-json/mdi": "^1.2.0", 28 | "@iconify/tailwind": "^1.1.3", 29 | "@playwright/test": "^1.47.2", 30 | "@sveltejs/adapter-static": "^3.0.5", 31 | "@sveltejs/kit": "^2.6.0", 32 | "@sveltejs/package": "^2.3.5", 33 | "@sveltejs/vite-plugin-svelte": "^3.1.2", 34 | "@tailwindcss/typography": "^0.5.15", 35 | "autoprefixer": "^10.4.20", 36 | "daisyui": "^4.12.10", 37 | "eslint": "^9.11.1", 38 | "eslint-config-prettier": "^9.1.0", 39 | "eslint-plugin-svelte": "^2.44.1", 40 | "globals": "^15.9.0", 41 | "prettier": "^3.3.3", 42 | "prettier-plugin-svelte": "^3.2.7", 43 | "prettier-plugin-tailwindcss": "^0.6.8", 44 | "sveld": "^0.20.2", 45 | "svelte": "^4.2.19", 46 | "svelte-check": "^4.0.3", 47 | "tailwindcss": "^3.4.13", 48 | "typescript": "^5.6.2", 49 | "vite": "^5.4.8" 50 | }, 51 | "type": "module", 52 | "exports": { 53 | ".": { 54 | "types": "./dist/index.d.ts", 55 | "svelte": "./dist/index.js" 56 | }, 57 | "./vite": { 58 | "types": "./dist/vite.d.ts", 59 | "default": "./dist/vite.js" 60 | } 61 | }, 62 | "svelte": "./dist/index.js", 63 | "types": "./dist/index.d.ts", 64 | "sideEffects": false, 65 | "files": [ 66 | "dist" 67 | ], 68 | "license": "ISC", 69 | "repository": "github:zerodevx/svelte-img", 70 | "keywords": [ 71 | "svelte", 72 | "sveltekit", 73 | "responsive", 74 | "progressive", 75 | "image", 76 | "lazy-load" 77 | ] 78 | } 79 | -------------------------------------------------------------------------------- /playwright.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('@playwright/test').PlaywrightTestConfig} */ 2 | const config = { 3 | testDir: 'tests', 4 | testMatch: '**/*.js', 5 | webServer: { 6 | command: 'npm run dev', 7 | port: 5173, 8 | reuseExistingServer: true 9 | } 10 | } 11 | 12 | export default config 13 | -------------------------------------------------------------------------------- /postcss.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('postcss-load-config').Config} */ 2 | export default { 3 | plugins: { 4 | tailwindcss: {}, 5 | autoprefixer: {} 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /src/ambient.d.ts: -------------------------------------------------------------------------------- 1 | declare module './assets/*' { 2 | var img 3 | export default img 4 | } 5 | -------------------------------------------------------------------------------- /src/app.d.ts: -------------------------------------------------------------------------------- 1 | // See https://kit.svelte.dev/docs/types#app 2 | // for information about these interfaces 3 | declare global { 4 | namespace App { 5 | // interface Error {} 6 | // interface Locals {} 7 | // interface PageData {} 8 | // interface Platform {} 9 | } 10 | } 11 | 12 | export {} 13 | -------------------------------------------------------------------------------- /src/app.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | %sveltekit.head% 8 | 9 | 10 |
%sveltekit.body%
11 | 12 | 13 | -------------------------------------------------------------------------------- /src/app.pcss: -------------------------------------------------------------------------------- 1 | /* Write your global styles here, in PostCSS syntax */ 2 | @tailwind base; 3 | @tailwind components; 4 | @tailwind utilities; 5 | -------------------------------------------------------------------------------- /src/lib/FxParallax.svelte: -------------------------------------------------------------------------------- 1 | 62 | 63 | 64 | 65 |
(inview = false)} 72 | > 73 | 80 |
81 | 82 | 94 | -------------------------------------------------------------------------------- /src/lib/FxReveal.svelte: -------------------------------------------------------------------------------- 1 | 39 | 40 | {#if len(meta)} 41 |
(inview = true)} 47 | > 48 | (loaded = true)} on:click src={meta} /> 49 |
50 |
51 | {/if} 52 | 53 | 82 | -------------------------------------------------------------------------------- /src/lib/Picture.svelte: -------------------------------------------------------------------------------- 1 | 9 | 10 | {#if srcs.length} 11 | 12 | {#each srcs as [format, srcset]} 13 | 14 | {/each} 15 | 16 | 17 | {:else} 18 | 19 | {/if} 20 | -------------------------------------------------------------------------------- /src/lib/SvelteImg.svelte: -------------------------------------------------------------------------------- 1 | 58 | 59 | 70 | 71 | {#if len(img)} 72 | 73 | 74 | 86 | 87 | {/if} 88 | -------------------------------------------------------------------------------- /src/lib/index.js: -------------------------------------------------------------------------------- 1 | import Img from './SvelteImg.svelte' 2 | import FxReveal from './FxReveal.svelte' 3 | import FxParallax from './FxParallax.svelte' 4 | import { observe } from './utils.js' 5 | 6 | export default Img 7 | export { FxReveal, FxParallax, observe } 8 | -------------------------------------------------------------------------------- /src/lib/utils.js: -------------------------------------------------------------------------------- 1 | /** @type {IntersectionObserver} */ 2 | let observer 3 | 4 | /** @param {Element} node */ 5 | function observe(node) { 6 | observer = 7 | observer || 8 | new IntersectionObserver((entries) => { 9 | for (const detail of entries) { 10 | const { isIntersecting, target } = detail 11 | target.dispatchEvent(new CustomEvent(isIntersecting ? 'enter' : 'leave', { detail })) 12 | } 13 | }) 14 | observer.observe(node) 15 | return { 16 | destroy() { 17 | observer.unobserve(node) 18 | } 19 | } 20 | } 21 | 22 | function len(obj) { 23 | return obj && Object.keys(obj).length 24 | } 25 | 26 | function lqipToBackground(lqip) { 27 | return lqip[0] === '#' ? lqip : `url(data:image/webp;base64,${lqip}) no-repeat center/cover` 28 | } 29 | 30 | export { observe, len, lqipToBackground } 31 | -------------------------------------------------------------------------------- /src/lib/vite.js: -------------------------------------------------------------------------------- 1 | import { imagetools, pictureFormat } from 'vite-imagetools' 2 | 3 | function run(cfg) { 4 | return async function (metadatas) { 5 | /** @type {any} */ 6 | const pic = pictureFormat()(metadatas) 7 | const lqip = (cfg && parseInt(cfg)) ?? 16 8 | if (lqip) { 9 | const { image } = metadatas.find((i) => i.src === pic.img.src) 10 | if (lqip > 1) { 11 | const buf = await image 12 | .clone() 13 | .resize({ width: lqip }) 14 | .toFormat('webp', { quality: 20 }) 15 | .toBuffer() 16 | pic.img.lqip = buf.toString('base64') 17 | } else { 18 | const { dominant } = await image.stats() 19 | const { r, g, b } = dominant 20 | pic.img.lqip = '#' + ((1 << 24) | (r << 16) | (g << 8) | b).toString(16).slice(1) 21 | } 22 | } 23 | return pic 24 | } 25 | } 26 | 27 | function main({ 28 | profiles = {}, 29 | /** @deprecated will be removed in next major */ 30 | runDefaultDirectives = new URLSearchParams('w=480;1024;1920&format=avif;webp;jpg'), 31 | defaultDirectives = new URLSearchParams(), 32 | exclude = '{build,dist,node_modules}/**/*', 33 | extendOutputFormats = (i) => i, //noop 34 | ...rest 35 | } = {}) { 36 | const dict = { 37 | run: runDefaultDirectives, 38 | ...profiles 39 | } 40 | return imagetools({ 41 | defaultDirectives: (url) => { 42 | const key = url.searchParams.get('as')?.split(':')[0] 43 | return Object.keys(dict).includes(key) ? dict[key] : defaultDirectives 44 | }, 45 | extendOutputFormats: (builtins) => ({ 46 | ...extendOutputFormats(builtins), 47 | ...Object.keys(dict).reduce((a, c) => ({ ...a, [c]: run }), {}) 48 | }), 49 | exclude, 50 | ...rest 51 | }) 52 | } 53 | 54 | export { main as imagetools, run } 55 | -------------------------------------------------------------------------------- /src/routes/+layout.js: -------------------------------------------------------------------------------- 1 | export const prerender = true 2 | export const trailingSlash = 'always' 3 | -------------------------------------------------------------------------------- /src/routes/+layout.svelte: -------------------------------------------------------------------------------- 1 | 6 | 7 | 8 | DEMO | svelte-img 9 | 13 | {#if !dev} 14 | 15 | 23 | {/if} 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /src/routes/+page.svelte: -------------------------------------------------------------------------------- 1 | 25 | 26 |
27 | cat 28 |
29 |
30 |

svelte-img

31 |
v{version}
32 |

High-performance responsive/progressive images for SvelteKit.

33 | 34 | 35 | VISIT GITHUB REPO 36 | 37 |
38 |
39 |
40 | 41 |
42 |
43 | Render the bare minimum, minimally invasive, LQIP-included HTML code to represent responsive 44 | images, served in multiple widths and next-gen formats. 45 |
46 |

Install

47 |
{esc(`$ npm i -D @zerodevx/svelte-img`)}
48 |

Add imagetools plugin into your vite.config.js:

49 |
{esc(`import { defineConfig } from 'vite'
 51 | import { sveltekit } from '@sveltejs/kit/vite'
 52 | import { imagetools } from '@zerodevx/svelte-img/vite'
 53 | 
 54 | export default defineConfig({
 55 |   plugins: [sveltekit(), imagetools()]
 56 | })`)}
58 |

Use

59 |

Anywhere in your svelte app:

60 |
{esc(`