I have a Svelte 5 app (not SvelteKit) where the data, including images paths, is provided from JSON.
Images are in src/lib
directory, as advised in the official Svelte doc.
data.json
:
[
{
"title": "tile 1",
"images": [
{ "src": "/src/lib/images/tile1a.jpg" },
{ "src": "/src/lib/images/tile1b.jpg" }
]
},
{
"title": "tile 2",
"images": [
{ "src": "/src/lib/images/tile2a.jpg" },
{ "src": "/src/lib/images/tile2b.jpg" }
]
}
]
The parent component loads the data and pass it to the child components:
<script lang="ts">
import data from "$lib/data.json"
import ChildComponent from "$lib/components/ChildComponent.svelte"
</script>
<main>
{#each data as tile}
<ChildComponent title={tile.title} images={tile.images} />
{/each}
</main>
And the child components use the images files paths as img src attribute:
<script lang="ts">
interface Props {
title: string
images: { src: string }[]
}
const { title, images }: Props = $props()
</script>
<div class="tile">
<span>{title}</span>
{#each images as i}
<img src='{i.src}' />
{/each}
</div>
This works as intended when running with Vite in dev mode (vite dev
), as the images paths exist.
But when running a built version of the app (vite build
, then vite preview
), images are not found, since their paths is not converted to the dist
folder.
I guess the images should be imported as Svelte imports (import Image from '/src/lib/images/tile1a.jpg'
) so that they can be resolved in the distributable build (dist
) directory, but how can one do that with dynamic paths?
You can import the images via glob import and use that mapping to look up the asset URL. Something along the lines of:
<script lang="ts">
import data from "$lib/data.json";
const imageMap: Record<string, string> = import.meta.glob(
"$lib/images/*",
{ eager: true, import: "default" },
);
</script>
<main>
{#each data as tile}
{@const images = tile.images.map(i => ({ ...i, src: imageMap[i.src] }))}
<ChildComponent title={tile.title} {images} />
{/each}
</main>