Skip to main content
RapidDev - Software Development Agency
lovable-issues

Fixing Incorrect Static File Paths After Lovable Build

Static file paths break after a Lovable build because Vite processes files differently based on their location. Files in /src get hashed filenames (logo-a3b4c5d.png), while files in /public keep their original names. Fix this by using /public for files you reference by URL path, using JavaScript imports for files in /src, and setting the correct base path in vite.config.ts. Always use root-relative paths starting with / to avoid route-dependent path resolution.

Book a free consultation
4.9Clutch rating
600+Happy partners
17+Countries served
190+Team members
Advanced8 min read~10 minAll Lovable projects using ViteMarch 2026RapidDev Engineering Team
TL;DR

Static file paths break after a Lovable build because Vite processes files differently based on their location. Files in /src get hashed filenames (logo-a3b4c5d.png), while files in /public keep their original names. Fix this by using /public for files you reference by URL path, using JavaScript imports for files in /src, and setting the correct base path in vite.config.ts. Always use root-relative paths starting with / to avoid route-dependent path resolution.

Why static file paths work in preview but break after building

Lovable's development server (Vite dev mode) resolves file paths more flexibly than a production build. In development, Vite can find files through multiple resolution strategies. But after running the build, assets are processed, renamed, and reorganized in the dist/ folder. The key difference is between /public and /src assets. Files in /public are copied directly to dist/ with their original names — /public/logo.png becomes dist/logo.png, accessible at /logo.png. Files in /src that are imported in JavaScript get content hashes added to their names — src/assets/logo.png becomes dist/assets/logo-a3b4c5d.png. If your code references src/assets/logo.png by its original path after build, the file does not exist at that path anymore. The Vite base path in vite.config.ts also plays a role. If base is set to anything other than '/', all asset URLs are prefixed with that path. A base of '/app/' turns /logo.png into /app/logo.png, which breaks if your server does not serve files from that prefix.

  • Referencing /src assets by path instead of using JavaScript imports — the hashed filename is not known at path-reference time
  • The Vite base path in vite.config.ts does not match the deployment URL structure
  • Using relative paths (./image.png) that resolve differently depending on the current route
  • Assets not placed in /public are expected to be at their source paths, which do not exist in the build
  • Hosting platform serves the dist/ folder from a subdirectory but base is set to '/'

Error messages you might see

GET /src/assets/logo.png 404 (Not Found)

The built app does not contain a /src directory. Assets from /src are processed by Vite and output with hashed filenames. Use an import statement or move the file to /public.

Failed to load module script: Expected a JavaScript module script but the server responded with a MIME type of 'application/octet-stream'

The build output is not being served correctly. This often means the dist/ folder was not deployed, or the hosting platform is serving source files instead of built files.

Refused to apply style from 'https://example.com/assets/index.css' because its MIME type ('text/html') is not a supported stylesheet MIME type

The CSS file path returns an HTML page (likely a 404 page) instead of the actual CSS. The asset path in the built HTML does not match where the file actually exists on the server.

Before you start

  • A Lovable project where static files are not loading correctly after deployment
  • Access to browser developer tools (Network tab) to see which files return 404
  • Dev Mode or GitHub access to view the project's file structure and vite.config.ts

How to fix it

1

Understand the difference between /public and /src assets in Vite

Choosing the right location determines whether the file keeps its original name or gets a hashed filename

Vite treats /public and /src assets completely differently. Files in /public are copied to the build output (dist/) with their original filenames and paths. Reference them in your code using root-relative URLs: src='/logo.png' for a file at public/logo.png. Files in /src/assets can be imported in JavaScript using import statements. Vite processes them, adds content hashes for cache busting, and outputs them in dist/assets/. The import returns the final URL including the hash. Use /public for files referenced by URL (images in HTML/CSS, fonts, JSON data files). Use /src imports for files used in JavaScript components (SVG icons as React components).

Before
typescript
// Wrong: referencing a /src file by path in JSX
<img src="/src/assets/logo.png" alt="Logo" />
// This path does not exist after build
After
typescript
// Option A: move to /public and reference by root-relative path
<img src="/images/logo.png" alt="Logo" />
// File location: public/images/logo.png
// Option B: import from /src (Vite handles the path)
import logo from "@/assets/logo.png";
<img src={logo} alt="Logo" />
// Vite resolves this to the correct hashed filename

Expected result: The image loads correctly in both development and production builds.

2

Set the correct base path in vite.config.ts

The base path is prepended to all asset URLs in the built HTML — a wrong value breaks every asset reference

Open vite.config.ts and check the base property. For root-level deployments (your app is at https://example.com/), set base to '/'. For subdirectory deployments (your app is at https://example.com/my-app/), set base to '/my-app/'. If base is missing, it defaults to '/' which is correct for most deployments. An incorrect base path causes every JavaScript, CSS, and asset URL in the built HTML to have the wrong prefix.

Before
typescript
// Incorrect base path for root deployment
export default defineConfig({
base: '/my-app/', // Wrong if deploying to root
plugins: [react()],
});
After
typescript
// Correct base path for root deployment
export default defineConfig({
base: '/', // Correct for lovable.app, Vercel, Netlify root deployments
plugins: [react()],
});

Expected result: All asset URLs in the built HTML start with the correct path prefix matching your deployment.

3

Replace relative paths with root-relative paths

Relative paths resolve differently on different routes, causing assets to break on nested pages

Find all relative asset references in your code (paths that start with ./ or do not start with /). Replace them with root-relative paths that start with /. A relative path like ./images/hero.jpg works on the home page but fails on /dashboard/settings because the browser looks for /dashboard/settings/images/hero.jpg. Root-relative paths always resolve from the site root regardless of the current route.

Before
typescript
// Relative paths — break on nested routes
<img src="./images/hero.jpg" />
<link rel="stylesheet" href="styles/custom.css" />
background: url('images/pattern.png');
After
typescript
// Root-relative paths — work everywhere
<img src="/images/hero.jpg" />
<link rel="stylesheet" href="/styles/custom.css" />
background: url('/images/pattern.png');

Expected result: Assets load correctly on every page, including deeply nested routes.

4

Verify the build output contains all expected files

If files are not in the dist/ folder, they cannot be served — no path configuration will fix a missing file

After building, check the contents of the dist/ folder. In Dev Mode, browse the file tree. If using GitHub, check the build artifacts or deploy preview. Every file referenced in your code must exist in dist/. Static files from /public should appear at the root of dist/. Imported assets should appear in dist/assets/ with hashed filenames. If files are missing, they were not committed to the repository or not included in the build. If debugging build output issues involves understanding Vite's asset pipeline configuration, RapidDev's engineers have resolved this across 600+ Lovable projects.

Before
typescript
// Build output missing expected files
// dist/
// assets/
// index-abc123.js
// index-def456.css
// index.html
// Missing: images/logo.png, fonts/custom.woff2
After
typescript
// Build output with all files present
// dist/
// assets/
// index-abc123.js
// index-def456.css
// images/
// logo.png
// hero.jpg
// fonts/
// custom.woff2
// index.html
// favicon.ico

Expected result: The dist/ folder contains every static file your app references. No 404 errors for assets after deployment.

Complete code example

vite.config.ts
1import { defineConfig } from "vite";
2import react from "@vitejs/plugin-react-swc";
3import path from "path";
4
5export default defineConfig({
6 // Base path: '/' for root deployment
7 // Change to '/subdir/' if deploying to a subdirectory
8 base: "/",
9
10 plugins: [react()],
11
12 resolve: {
13 alias: {
14 "@": path.resolve(__dirname, "./src"),
15 },
16 },
17
18 build: {
19 outDir: "dist",
20 // Assets smaller than 4kb are inlined as base64
21 assetsInlineLimit: 4096,
22 rollupOptions: {
23 output: {
24 // Organize output by type
25 assetFileNames: (assetInfo) => {
26 // Keep fonts in a fonts directory
27 if (assetInfo.name?.match(/\.(woff2?|eot|ttf|otf)$/)) {
28 return "fonts/[name]-[hash][extname]";
29 }
30 // Keep images in an images directory
31 if (assetInfo.name?.match(/\.(png|jpe?g|gif|svg|webp|ico)$/)) {
32 return "images/[name]-[hash][extname]";
33 }
34 return "assets/[name]-[hash][extname]";
35 },
36 },
37 },
38 },
39});

Best practices to prevent this

  • Place static assets in /public for predictable, unhashed file paths in production
  • Use JavaScript imports for assets that need processing (SVG components, optimized images)
  • Always use root-relative paths (starting with /) for assets in /public — never relative paths
  • Set the Vite base path to '/' for root deployments and '/subdir/' for subdirectory deployments
  • Commit all asset files to GitHub before deploying — files not in the repo will not be in the build
  • Check the dist/ folder contents after building to verify all expected assets are present
  • Use Supabase Storage for user-uploaded content instead of /public — uploaded files persist across builds
  • Add cache-control headers for hashed assets (they are safe to cache indefinitely since the hash changes with content)

Still stuck?

Copy one of these prompts to get a personalized, step-by-step explanation.

ChatGPT Prompt

My Lovable project has broken image and asset paths after deploying. Assets work in preview but return 404 in production. Here is my vite.config.ts: [paste your vite config here] And here are the 404 errors from the Network tab: [paste the failed asset URLs] Please: 1. Identify which assets need to move to /public vs stay in /src with imports 2. Fix the base path in vite.config.ts if needed 3. Convert all relative paths to root-relative paths 4. Show me how to verify the build output contains all files

Lovable Prompt

Fix broken asset paths in my project. Check @vite.config.ts and ensure the base path is set to '/'. Find all static assets in src/assets/ that are referenced by URL path (not imported in JavaScript) and move them to the /public folder. Update all references in components to use root-relative paths like /images/filename.png. Do not change any assets that are properly imported using import statements.

Frequently asked questions

Why do my images work in Lovable preview but break after build?

Vite's dev server resolves file paths more flexibly than production. After build, files in /src get hashed filenames and files referenced by their source path return 404. Move static assets to /public or use JavaScript import statements for /src assets.

What is the difference between /public and /src assets in Vite?

Files in /public are copied to the build output as-is with their original names. Files in /src/assets are processed by Vite and get hashed filenames for cache busting. Use /public for files you reference by URL path. Use /src imports for files used in JavaScript.

What does the base path in vite.config.ts do?

The base property sets the URL prefix for all asset references in the built HTML. Keep it as '/' for standard root-level deployments. Only change it if deploying to a subdirectory like /my-app/ on a shared domain.

How do I check which assets are missing after a build?

Open the deployed site in your browser, open developer tools, go to the Network tab, and reload. Filter for failed requests (status 404). Each 404 entry shows the exact URL the browser tried to load, helping you identify missing assets.

Should I use relative or absolute paths for assets?

Always use root-relative paths starting with / for assets in /public. Relative paths (starting with ./) resolve differently on different routes. On a nested page like /dashboard/settings, a relative path looks for the asset inside that path instead of at the root.

What if I can't fix this myself?

If your build output has many broken paths across different asset types and hosting configurations, RapidDev's engineers can audit and fix all of them. They have resolved Vite asset path issues across 600+ Lovable projects.

RapidDev

Talk to an Expert

Our team has built 600+ apps. Get personalized help with your issue.

Book a free consultation

Need help with your Lovable project?

Our experts have built 600+ apps and can solve your issue fast. Book a free consultation — no strings attached.

Book a free consultation

We put the rapid in RapidDev

Need a dedicated strategic tech and growth partner? Discover what RapidDev can do for your business! Book a call with our team to schedule a free, no-obligation consultation. We'll discuss your project and provide a custom quote at no cost.