A scaffolded Vite React project keeps a small, predictable file layout. This lesson walks through what index.html, main.tsx, public/, src/assets, and vite.config.ts each actually do, so the build process stops feeling like a black box.
1The Anatomy of a Vite React Project
Scaffolding a project with the React template produces a small, predictable set of files: index.html at the project root, a typed vite.config.ts, a src/ folder holding application code, and a public/ folder for static assets served without processing.
2index.html Is a Real Entry Point
Unlike older tools that treated the HTML template as a static file injected by the build process, Vite treats the root index.html as part of the actual module graph, containing a direct <script type="module"> reference to the app's entry file that Vite processes like any other source file.
3main.tsx: The Bootstrap File
src/main.tsx is the one place where React is manually attached to the DOM: it imports the root App component and calls createRoot on the #root element referenced in index.html. Everything else in the application is reached through component composition from that single mount point.
4public/ vs. src/assets
Files in public/ are copied to the build output unprocessed, at a fixed, predictable URL ā appropriate for things like favicon.ico or robots.txt. Files inside src/, including images, are treated as modules: Vite processes, optimizes, and gives them content-hashed filenames as part of the production build.
5vite.config.ts: The Control Center
vite.config.ts registers plugins like @vitejs/plugin-react, configures dev server behavior, defines path aliases, and tunes production build settings ā all in a single typed file, considerably smaller and easier to reason about than a typical multi-file legacy bundler configuration.
6Step-by-Step Breakdown
The Anatomy of a Vite React Project. Running npm create vite@latest with the React template scaffolds a small, predictable set of files: index.html at the project root, a vite.config.ts, a src/ folder with your app code, and a public/ folder for static assets. Understanding what each piece does removes the mystery from the build process.
index.html Is a Real Entry Point. Unlike older tools where public/index.html was a static template injected with a script tag by the build process, Vite treats index.html at the project root as an actual part of the module graph. It contains a <script type="module" src="/src/main.tsx"> tag directly, and Vite processes it like any other source file.
In a Vite project, how does index.html relate to your React app's entry point?
- āIt directly references main.tsx via a module script tag
- āVite auto-injects an invisible script the developer never sees
main.tsx: The Bootstrap File. src/main.tsx is where your React app actually mounts. It imports the root App component and calls createRoot to attach it to the #root element defined in index.html. This is the one place in the whole app where React is manually wired into the DOM ā everything else is just component composition from here.
public/ vs. src/assets. The public/ folder holds files copied as-is to the output root, unprocessed and unhashed ā good for a favicon.ico or robots.txt that needs a fixed URL. Anything inside src/, including images, is treated as a module: Vite processes it, optimizes it, and gives it a content-hashed filename in the production build.
You need a favicon accessible at a guaranteed, unchanging URL like /favicon.ico. Where should the file live?
- āIn the public/ folder
- āIn src/assets, imported as a module
vite.config.ts: The Control Center. vite.config.ts is where you register plugins (like @vitejs/plugin-react), configure the dev server, set up path aliases, and tune the production build. It's a single, typed configuration file ā far smaller and easier to reason about than a typical multi-file webpack setup.
Mastery Achieved. You now understand a Vite React project's anatomy: index.html as a real entry point, main.tsx as the single DOM mount location, the public/ vs. src/assets distinction, and vite.config.ts as the central configuration file. Next, you'll learn how to safely manage environment variables across dev, staging, and production.
Level Up š
Advanced cheat sheets, SEO tricks, and interview prep for this topic.
Browser Support
Project structure is a build-time concept, applicable regardless of target browser.
Fully applicable.
Fully applicable.
Fully applicable.
Accessibility (A11y)
1index.html Is the Right Place for Base-Level Meta Tags
Language attributes (lang="en"), a proper viewport meta tag, and a descriptive default <title> belong in the root index.html, since it's the one HTML shell every route ultimately loads into.
SEO Implications
- 1
Static Assets in public/ Get Stable, Crawlable URLs
Files placed in public/ are served at predictable, unhashed paths, which matters for assets like robots.txt, sitemap.xml, or social preview images that external tools and crawlers expect at a fixed location.
Best Practices
Default to src/assets for Anything Imported in Code
Prefer importing images and other assets from within src/ rather than referencing public/ paths by string, since imported assets get automatic optimization, cache-busting hashes, and dead-asset elimination during the build.
Keep vite.config.ts Focused and Well-Commented
As a project grows, the config file accumulates aliases, plugins, and build tweaks ā group related settings and comment non-obvious ones so the single source of build truth stays easy to audit.
Frequent Bugs
An image imported from src/assets shows a 404 in the browser after building.
Make sure the import is a proper ES module import (import logo from './logo.png'), not a hardcoded string path ā Vite only processes and hashes assets that are actually imported as modules.
A file placed in public/ isn't showing up at the expected root URL.
Files in public/ are served relative to the site root regardless of nesting inside that folder ā reference them by absolute path (e.g. /favicon.ico), not a relative path from a component file.
Real-World Examples
Adding a Custom Favicon and Social Preview Image
A team needed a favicon.ico at a guaranteed URL for browser tabs and an og-image.png for social media link previews. Both went into public/ since external tools (browsers, social crawlers) expect fixed, predictable URLs rather than Vite's content-hashed asset filenames.
public/
favicon.ico
og-image.png
<!-- index.html -->
<link rel="icon" href="/favicon.ico" />
<meta property="og:image" content="/og-image.png" />