Ein React-Projekt ist mehr als Code. Es ist eine Sammlung von Dateien, Konfigurationen und Build-Tools, die zusammenarbeiten, um eine lauffähige Anwendung zu erzeugen. Die Struktur dieser Dateien – das Scaffolding – bestimmt, wie wartbar, erweiterbar und verständlich ein Projekt ist.
Moderne React-Projekte nutzen Build-Tools wie Vite, die nicht nur Code bündeln, sondern den gesamten Entwicklungsprozess orchestrieren. Vite hat sich als Standard etabliert, weil es schnell ist, minimalen Overhead hat und mit ES Modules nativ arbeitet statt alles zu einem großen Bundle zu verklumpen.
Dieses Kapitel erklärt die Anatomie eines typischen Vite-React-Projekts: Welche Dateien existieren, was sie tun, wie der Bootstrap-Prozess funktioniert, und wie man die Struktur sinnvoll erweitert.
Ein frisches Vite-React-Projekt hat eine schlanke, aber durchdachte Struktur. Jede Datei hat einen klaren Zweck.
my-react-app/
├── node_modules/
├── public/
│ └── vite.svg
├── src/
│ ├── assets/
│ │ └── react.svg
│ ├── App.css
│ ├── App.tsx
│ ├── index.css
│ └── main.tsx
├── index.html
├── package.json
├── tsconfig.json
├── tsconfig.node.json
└── vite.config.ts
Die Struktur folgt einem klaren Muster: HTML als Einstieg, TypeScript als Logik, CSS als Styling, Konfigurationsdateien für Tooling.
Anders als bei Webpack oder Create React App ist
index.html bei Vite keine Template-Datei, sondern der
tatsächliche Einstiegspunkt. Eine statische HTML-Datei, die Vite direkt
ausliefert.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Vite + React + TS</title>
</head>
<body>
<div id="root"></div>
<script type="module" src="/src/main.tsx"></script>
</body>
</html>Zwei wichtige Elemente:
<div id="root"></div> – Der Mount-Point.
React wird hier einhängen.<script type="module" src="/src/main.tsx"></script>
– Der JavaScript-Einstieg als ES Module.Kein Template-Syntax, keine Platzhalter. Vite lädt die HTML-Datei
direkt und ersetzt im Dev-Modus das <script>-Tag
durch Hot-Module-Replacement-Code. Im Production-Build wird der
Script-Pfad zum gebündelten Output angepasst.
Das ist ein fundamentaler Unterschied zu Webpack, wo
index.html oft durch Plugins generiert wird. Bei Vite ist
HTML der Ausgangspunkt, nicht das Produkt.
main.tsx ist der technische Einstiegspunkt der
React-Anwendung. Hier passiert das Bootstrapping – React verbindet sich
mit dem DOM.
import React from 'react';
import ReactDOM from 'react-dom/client';
import App from './App.tsx';
import './index.css';
ReactDOM.createRoot(document.getElementById('root')!).render(
<React.StrictMode>
<App />
</React.StrictMode>
);
Die Sequenz:
#root-Elementdocument.getElementById('root')! – Das !
ist TypeScript-Syntax: “Ich weiß, dass dieses Element existiert.” Wenn
Sie sich nicht sicher sind, verwenden Sie optionales Chaining:
const rootElement = document.getElementById('root');
if (!rootElement) throw new Error('Root element not found');
ReactDOM.createRoot(rootElement).render(<App />);
Der gesamte Bootstrap-Prozess ist synchron. Sobald
main.tsx ausgeführt wird, rendert React sofort.
App.tsx ist die oberste React-Komponente. Hier beginnt
die UI-Hierarchie.
import { useState } from 'react';
import './App.css';
import reactLogo from './assets/react.svg';
import viteLogo from '/vite.svg';
function App() {
const [count, setCount] = useState(0);
return (
<>
<div>
<a href="https://vite.dev" target="_blank">
<img src={viteLogo} className="logo" alt="Vite logo" />
</a>
<a href="https://react.dev" target="_blank">
<img src={reactLogo} className="logo react" alt="React logo" />
</a>
</div>
<h1>Vite + React</h1>
<div className="card">
<button onClick={() => setCount((count) => count + 1)}>
count is {count}
</button>
</div>
</>
);
}
export default App;
Ein typisches Starter-Template: Logos, ein Counter, ein paar Styles. Nichts Besonderes, aber ein funktionierendes Beispiel.
Die Struktur ist flach. Für größere Projekte extrahiert man Komponenten in separate Dateien:
src/
├── components/
│ ├── Header/
│ │ ├── Header.tsx
│ │ └── Header.css
│ ├── Counter/
│ │ ├── Counter.tsx
│ │ └── Counter.css
│ └── Footer/
│ ├── Footer.tsx
│ └── Footer.css
├── App.tsx
└── main.tsx
Feature-basierte Struktur ist ebenfalls beliebt:
src/
├── features/
│ ├── auth/
│ │ ├── Login.tsx
│ │ ├── Signup.tsx
│ │ └── authSlice.ts
│ ├── todos/
│ │ ├── TodoList.tsx
│ │ ├── TodoItem.tsx
│ │ └── todosSlice.ts
├── App.tsx
└── main.tsx
Die initiale Struktur ist bewusst simpel. Sie erweitern sie, wie das Projekt wächst.
Vite unterstützt CSS nativ. Importieren Sie CSS-Dateien direkt in JavaScript/TypeScript – Vite verarbeitet sie automatisch.
index.css – Globale Styles, die für die gesamte App gelten.
:root {
font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif;
line-height: 1.5;
font-weight: 400;
color-scheme: light dark;
color: rgba(255, 255, 255, 0.87);
background-color: #242424;
}
body {
margin: 0;
display: flex;
place-items: center;
min-width: 320px;
min-height: 100vh;
}Importiert in main.tsx:
import './index.css';
Lädt beim Bootstrap, gilt global.
App.css – Komponenten-spezifische Styles.
.logo {
height: 6em;
padding: 1.5em;
will-change: filter;
transition: filter 300ms;
}
.logo:hover {
filter: drop-shadow(0 0 2em #646cffaa);
}Importiert in App.tsx:
import './App.css';
Technisch sind diese Styles auch global – CSS hat keine echte Scoping-Mechanik. Wenn Sie echtes Scoping wollen, verwenden Sie CSS Modules:
// App.module.css
.logo { /* ... */ }
// App.tsx
import styles from './App.module.css';
<img src={logo} className={styles.logo} />
Vite erkennt .module.css-Dateien automatisch und
verarbeitet sie als Modules.
Alternativen: Tailwind CSS, styled-components, Emotion. Vite unterstützt alle durch Plugins.
Vite unterscheidet zwischen zwei Asset-Verzeichnissen:
public/ – Statische Assets, die direkt kopiert werden ohne Verarbeitung.
public/
└── vite.svg
Zugriff über absoluten Pfad:
<img src="/vite.svg" />
Vite kopiert alles aus public/ 1:1 ins Build-Output.
Keine Transformation, kein Hashing. Verwenden Sie public/
für Assets, die von externen Services referenziert werden (z.B.
robots.txt, favicon.ico).
src/assets/ – Assets, die durch Vite verarbeitet werden.
src/assets/
└── react.svg
Zugriff via Import:
import reactLogo from './assets/react.svg';
<img src={reactLogo} />
Vite verarbeitet diese Assets – kleine Dateien werden inline als Data-URLs, große bekommen Content-Hashes für Caching.
// Development
reactLogo = '/src/assets/react.svg'
// Production
reactLogo = '/assets/react-a3b2c1d4.svg' // Hash für Cache-Busting
Faustregel: public/ für statische Assets, die sich nie
ändern. src/assets/ für alles andere.
Die zentrale Steuerung für Vite. Minimal per Default, erweiterbar nach Bedarf.
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
export default defineConfig({
plugins: [react()],
});Das React-Plugin aktiviert Fast Refresh (Hot Module Replacement für React-Komponenten).
Erweiterte Konfiguration:
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
import path from 'path';
export default defineConfig({
plugins: [react()],
resolve: {
alias: {
'@': path.resolve(__dirname, './src'),
'@components': path.resolve(__dirname, './src/components')
}
},
server: {
port: 3000,
open: true
},
build: {
outDir: 'dist',
sourcemap: true
}
});Aliase erlauben cleane Imports:
// Statt
import Button from '../../../components/Button';
// Mit Alias
import Button from '@components/Button';
Server-Konfiguration steuert den Dev-Server. Build-Konfiguration das Production-Output.
Zwei Konfigurationsdateien für TypeScript:
tsconfig.json – Für Ihren App-Code.
{
"compilerOptions": {
"target": "ES2020",
"useDefineForClassFields": true,
"lib": ["ES2020", "DOM", "DOM.Iterable"],
"module": "ESNext",
"skipLibCheck": true,
"moduleResolution": "bundler",
"allowImportingTsExtensions": true,
"resolveJsonModule": true,
"isolatedModules": true,
"noEmit": true,
"jsx": "react-jsx",
"strict": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"noFallthroughCasesInSwitch": true
},
"include": ["src"]
}Wichtige Optionen:
jsx: "react-jsx" – Moderne JSX-Transformation (kein
import React nötig)strict: true – Alle Strict-Checks aktiviertnoEmit: true – TypeScript produziert kein JavaScript
(Vite tut es)include: ["src"] – Nur src/-Dateien
prüfentsconfig.node.json – Für Build-Tools (Vite-Konfiguration).
{
"compilerOptions": {
"composite": true,
"skipLibCheck": true,
"module": "ESNext",
"moduleResolution": "bundler",
"allowSyntheticDefaultImports": true
},
"include": ["vite.config.ts"]
}Separate Konfiguration, weil Build-Tools in Node.js laufen, nicht im Browser.
Vite’s Architektur unterscheidet sich fundamental von Webpack.
Webpack – Baut alles zu einem (oder mehreren) Bundles, auch im Dev-Modus.
Webpack muss alles transformieren, bündeln, minimieren. Bei großen Projekten wird der Dev-Server langsam. Jede Änderung triggert einen Rebuild des gesamten Bundles (oder zumindest großer Teile).
Vite (Dev) – Serviert ES Modules direkt, ohne Bundling.
Vite lädt Module on-demand. Nur was der Browser anfragt, wird transformiert. Instant startup, auch bei großen Projekten.
Vite (Production) – Baut mit Rollup, optimiert für Production.
Production-Builds sind optimiert, gebündelt, minimiert. Aber im Dev-Modus keine Wartezeit.
| Aspekt | Webpack (Dev) | Vite (Dev) | Vite (Production) |
|---|---|---|---|
| Startup-Zeit | Langsam (>10s bei großen Apps) | Instant (<1s) | N/A |
| Hot Reload | Mittel (ganzes Bundle?) | Instant (nur geänderte Module) | N/A |
| Output | Bundle(s) | Keine Bundles, native ESM | Optimierte Bundles |
| Browser-Support | Alle (transpiliert) | Moderne (ESM) | Alle (transpiliert) |
Vite ist schnell, weil es im Dev-Modus das Bundling überspringt. Moderne Browser können ES Modules nativ laden – Vite nutzt das.
1. Komponenten organisieren nach Feature oder Typ
Feature-basiert (empfohlen für große Apps):
src/
├── features/
│ ├── auth/
│ ├── products/
│ └── cart/
└── shared/
├── components/
└── utils/
Typ-basiert (empfohlen für kleinere Apps):
src/
├── components/
├── hooks/
├── utils/
└── types/
2. Aliase verwenden für cleane Imports
// vite.config.ts
resolve: {
alias: {
'@': path.resolve(__dirname, './src'),
'@components': path.resolve(__dirname, './src/components')
}
}
// Komponente
import Button from '@components/Button';3. Lazy Loading für große Komponenten
import { lazy, Suspense } from 'react';
const Dashboard = lazy(() => import('./features/dashboard/Dashboard'));
function App() {
return (
<Suspense fallback={<Loading />}>
<Dashboard />
</Suspense>
);
}
Vite splittet automatisch lazy-geladene Komponenten in separate Chunks.
4. Environment Variables
Vite unterstützt .env-Dateien:
# .env
VITE_API_URL=https://api.example.com
VITE_APP_TITLE=My App
Zugriff via import.meta.env:
const apiUrl = import.meta.env.VITE_API_URL;
Nur Variablen mit VITE_-Präfix werden exponiert. Das
verhindert versehentliches Leaken von Secrets.
5. Public Assets sparsam nutzen
public/ ist für echte statische Assets. Alles andere in
src/assets/, damit Vite es optimieren kann.
Ein realistisches Projekt-Setup:
my-app/
├── public/
│ ├── favicon.ico
│ └── robots.txt
├── src/
│ ├── assets/
│ │ ├── logo.svg
│ │ └── images/
│ ├── components/
│ │ ├── Button/
│ │ │ ├── Button.tsx
│ │ │ ├── Button.module.css
│ │ │ └── Button.test.tsx
│ │ └── Header/
│ ├── features/
│ │ ├── auth/
│ │ │ ├── Login.tsx
│ │ │ ├── authSlice.ts
│ │ │ └── authAPI.ts
│ │ └── products/
│ ├── hooks/
│ │ ├── useAuth.ts
│ │ └── useLocalStorage.ts
│ ├── utils/
│ │ ├── formatDate.ts
│ │ └── api.ts
│ ├── types/
│ │ └── index.ts
│ ├── App.tsx
│ ├── App.css
│ ├── main.tsx
│ └── index.css
├── .env
├── .env.production
├── index.html
├── package.json
├── tsconfig.json
├── tsconfig.node.json
└── vite.config.ts
Klar strukturiert, erweiterbar, wartbar. Komponenten sind nach Typ
organisiert (components/), Features nach Domäne
(features/), wiederverwendbare Logik in hooks/
und utils/.
Vite’s Scaffolding ist minimal, aber durchdacht. Es gibt genug Struktur für den Start, ohne zu viel vorzuschreiben. Sie erweitern es, wie Ihr Projekt wächst. Das ist das Gegenteil von monolithischen Frameworks, die von Anfang an alles vorgeben. Vite gibt Ihnen Freiheit – und die Werkzeuge, um diese Freiheit sinnvoll zu nutzen.