Static assets in public/ fail (redirect or resolve error) with nested entry point
Describe the bug
When the Slidev dev server (v51.6.0) is started with an entry point Markdown file located in a subdirectory (e.g., meetings/myfile.md), static assets from the public/ directory (e.g., /images/myimage.png) are not served or resolved correctly.
This manifests in one of two ways:
- Accessing the asset URL directly (e.g.,
http://localhost:3030/images/myimage.png) redirects to the base route (/), serving the first slide instead of the asset. Images referenced in slides using<img src="/images/...">appear broken. - Alternatively, the Vite pre-transform step fails with a
Failed to resolve import "/images/myimage.png" from "meetings/myfile.md..."error in the console during startup.
This issue does not occur if the entry point Markdown file is at the project root (e.g., slides.md).
Minimal reproduction
Steps to reproduce the behavior locally:
- Ensure Node.js (>=18) and npm are installed.
- Create a new Slidev project:
npm init slidev@latest(This should install@slidev/cliv51.6.0 or similar). - Create the following directory structure within the project:
├── public/
│ └── images/
│ └── test.png # Place any PNG image here
├── meetings/ # Subdirectory for entry point
│ └── meeting.md
├── package.json
└── node_modules/ # after npm install
- Add the following content to
meetings/meeting.md:
# Test Slide
Image test (should appear below):
<img src="/images/test.png" alt="Test Image" width="100">
- From the project root, run the dev server targeting the nested file:
npx slidev meetings/meeting.md --port 3030
-
Open http://localhost:3030 in the browser and navigate to the slide. Observe: The image is broken.
-
Try accessing the image directly: http://localhost:3030/images/test.png. Observe: Results in a
404 Page /images/test.png not founderror. -
Check the terminal console where slidev is running. Observe: Potentially (depending on exact conditions), Vite errors like Failed to resolve import "/images/test.png"... might appear.
Expected behavior:
The image should display on the slide. Direct access to http://localhost:3030/images/test.png should show the image. No Vite errors related to resolving /images/test.png should occur.
10:37:08 AM [vite] (client) Pre-transform error: Failed to resolve import "/images/c4-overview.png" from "meetings/meeting.md__slidev_1.md". Does the file exist?
Plugin: vite:import-analysis
File: /mnt/d/Travail/SandBox/slide-communaute/slidev/meetings/meeting.md__slidev_1.md:21:25
19 | }
20 | import { createElementVNode as _createElementVNode, createTextVNode as _createTextVNode, normalizeProps as _normalizeProps, guardReactiveProps as _guardReactiveProps, withCtx as _withCtx, openBlock as _openBlock, createBlock as _createBlock } from "vue"
21 | import _imports_0 from '/images/c4-overview.png'
| ^
22 |
23 |
10:37:09 AM [vite] Internal server error: Failed to resolve import "/images/c4-overview.png" from "meetings/meeting.md__slidev_1.md". Does the file exist?
Plugin: vite:import-analysis
File: /mnt/d/Travail/SandBox/slide-communaute/slidev/meetings/meeting.md__slidev_1.md:21:25
19 | }
20 | import { createElementVNode as _createElementVNode, createTextVNode as _createTextVNode, normalizeProps as _normalizeProps, guardReactiveProps as _guardReactiveProps, withCtx as _withCtx, openBlock as _openBlock, createBlock as _createBlock } from "vue"
21 | import _imports_0 from '/images/c4-overview.png'
| ^
22 |
23 |
at TransformPluginContext._formatLog (file:///mnt/d/Travail/SandBox/slide-communaute/slidev/node_modules/vite/dist/node/chunks/dep-Bn81Esdm.js:42499:41)
at TransformPluginContext.error (file:///mnt/d/Travail/SandBox/slide-communaute/slidev/node_modules/vite/dist/node/chunks/dep-Bn81Esdm.js:42496:16)
at normalizeUrl (file:///mnt/d/Travail/SandBox/slide-communaute/slidev/node_modules/vite/dist/node/chunks/dep-Bn81Esdm.js:40475:23)
at process.processTicksAndRejections (node:internal/process/task_queues:105:5)
at async file:///mnt/d/Travail/SandBox/slide-communaute/slidev/node_modules/vite/dist/node/chunks/dep-Bn81Esdm.js:40594:37
at async Promise.all (index 3)
at async TransformPluginContext.transform (file:///mnt/d/Travail/SandBox/slide-communaute/slidev/node_modules/vite/dist/node/chunks/dep-Bn81Esdm.js:40521:7)
at async EnvironmentPluginContainer.transform (file:///mnt/d/Travail/SandBox/slide-communaute/slidev/node_modules/vite/dist/node/chunks/dep-Bn81Esdm.js:42294:18)
at async loadAndTransform (file:///mnt/d/Travail/SandBox/slide-communaute/slidev/node_modules/vite/dist/node/chunks/dep-Bn81Esdm.js:35735:27)
at async viteTransformMiddleware (file:///mnt/d/Travail/SandBox/slide-communaute/slidev/node_modules/vite/dist/node/chunks/dep-Bn81Esdm.js:37250:24)
10:37:11 AM [vite] Internal server error: Failed to resolve import "/images/c4-overview.png" from "meetings/meeting.md__slidev_1.md". Does the file exist?
Plugin: vite:import-analysis
File: /mnt/d/Travail/SandBox/slide-communaute/slidev/meetings/meeting.md__slidev_1.md:21:25
19 | }
20 | import { createElementVNode as _createElementVNode, createTextVNode as _createTextVNode, normalizeProps as _normalizeProps, guardReactiveProps as _guardReactiveProps, withCtx as _withCtx, openBlock as _openBlock, createBlock as _createBlock } from "vue"
21 | import _imports_0 from '/images/c4-overview.png'
| ^
22 |
23 |
at TransformPluginContext._formatLog (file:///mnt/d/Travail/SandBox/slide-communaute/slidev/node_modules/vite/dist/node/chunks/dep-Bn81Esdm.js:42499:41)
at TransformPluginContext.error (file:///mnt/d/Travail/SandBox/slide-communaute/slidev/node_modules/vite/dist/node/chunks/dep-Bn81Esdm.js:42496:16)
at normalizeUrl (file:///mnt/d/Travail/SandBox/slide-communaute/slidev/node_modules/vite/dist/node/chunks/dep-Bn81Esdm.js:40475:23)
at process.processTicksAndRejections (node:internal/process/task_queues:105:5)
at async file:///mnt/d/Travail/SandBox/slide-communaute/slidev/node_modules/vite/dist/node/chunks/dep-Bn81Esdm.js:40594:37
at async Promise.all (index 3)
at async TransformPluginContext.transform (file:///mnt/d/Travail/SandBox/slide-communaute/slidev/node_modules/vite/dist/node/chunks/dep-Bn81Esdm.js:40521:7)
at async EnvironmentPluginContainer.transform (file:///mnt/d/Travail/SandBox/slide-communaute/slidev/node_modules/vite/dist/node/chunks/dep-Bn81Esdm.js:42294:18)
at async loadAndTransform (file:///mnt/d/Travail/SandBox/slide-communaute/slidev/node_modules/vite/dist/node/chunks/dep-Bn81Esdm.js:35735:27)
at async viteTransformMiddleware (file:///mnt/d/Travail/SandBox/slide-communaute/slidev/node_modules/vite/dist/node/chunks/dep-Bn81Esdm.js:37250:24) (x2)
Environment
Slidev version: v51.6.0
Node.js version: v22.15.0
Browser: Microsoft Edge 135.0.3179.98
OS: Windows 11 with WSL2 Ubuntu 2o2.04
Package Manager used: npm v11.3
Have the same issue, very annoying
A not elegant workaround is move the public dir to meetings dir.
A not elegant workaround is move the
publicdir tomeetingsdir.
Tried that. Didn't work for me. The build didn't include any public folder nor any of its contents
@MeikelLP I create a playground for it and it works normally:
https://stackblitz.com/edit/github-n1hufsvh?file=package.json
you can try npm run build on the terminal, and check the dist dir, includes the image
Directory structure
- slides/
- 2025/
- 06.md
- 05.md
- public/
- images/
- avatar.png
- images/
- 2025/
- packacke.json
Command
slidev build slides/2025/06.md
Try put public under 2025 folder.
Works thanks ❤
The behavior of <img>'s src attribute in Slidev markdown should be the same as in an ordinary Vue component. It is recommended to use a relative path for <img>'s src attribute:
<img src="./assets/image.png">
meetings/
slides.md
assets/
image.png