I’m using Vite + React with both SSR and SPA builds.
When I run the dev server or build for production, Vite automatically injects image preload tags like this:
<div id="root">
<link rel="preload" as="image" href="/images/geonix-logo.svg">
<div>...</div>
</div>
I never manually added these preload links anywhere in my code (not in index.html, not in React components).
What I checked:
-
These tags are not in
index.html. -
They are not imported or rendered manually in any components.
-
I searched the entire codebase for image preload links (
<link rel="preload" as="image">), but couldn’t find any in the source. They only appear in the build output.-
he same happens in both dev and production builds.
-
It occurs consistently with any image used in React components.
.⚡ Temporary workaround I tried
As a temporary fix, at the stage of sending the final SSR HTML,
I extract these image preload tags from inside the root, remove them, and then insert them manually into the<head>before sending the response.This works but doesn’t feel like the best solution.
My Vite config
import { defineConfig, loadEnv } from "vite"; import react from "@vitejs/plugin-react-swc"; import path from "node:path"; import { getProxyOptions } from "./server/proxy/getProxyOptions.js"; export default defineConfig(({ mode, isSsrBuild }) => { const isDev = mode === "development"; const isSpa = process.env.SPA === "true"; const env = loadEnv(mode, process.cwd(), "REACT_APP_"); const defineEnv = Object.entries(env).reduce((acc, [key, val]) => { acc[`process.env.${key}`] = JSON.stringify(val); return acc; }, {}); const proxyOption = getProxyOptions(env); return { root: isSpa ? path.resolve(__dirname, "spaRoot") : path.resolve(__dirname, "."), plugins: [react()], publicDir: path.resolve(__dirname, "public"), define: defineEnv, build: { minify: "esbuild", sourcemap: false, cssMinify: true, copyPublicDir: !isSsrBuild, rollupOptions: { output: { manualChunks: undefined } }, }, ssr: isDev ? {} : { noExternal: [/^@forproxyband/main-ui-kit/, "classnames", "lodash", "uuid"], }, server: { proxy: proxyOption && isDev ? { "/api": proxyOption } : undefined, }, resolve: { alias: { "@": path.resolve(__dirname, "src"), "/src": path.resolve(__dirname, "src"), }, }, }; });My questions
-
Where exactly do these image preload tags come from in Vite’s runtime?
-
How can I disable or control this behavior (e.g. disable image preloads or move them to the
<head>)? -
Why are they inserted inside the
#rootelement instead of the <head>?
-
-