React Ordnerstruktur die skaliert

Eine Ordnerstruktur ist nicht „nur Ästhetik". Sie kodiert Grenzen, Eigentümerschaft und Änderungssicherheit in Ihre Codebasis. Wenn Ihr Team mehr als ein paar Features pro Monat ausliefert, zeigt sich der Unterschied zwischen „funktioniert" und „skaliert" schnell: längere Pull Requests, mysteriöse Regressionen, zirkuläre Importe und eine wachsende Scheu davor, „gemeinsamen" Code anzufassen.
Dieser Leitfaden gibt Ihnen eine produktionsreife React-Ordnerstruktur, die skaliert, sowie die Regeln, die sie zum Funktionieren bringen. Er richtet sich an Engineers und Tech Leads, die React-Code wartbar halten möchten, während die App und das Team wachsen.
Was „skalierbar" für eine React-Codebasis bedeutet
Eine skalierbare Struktur ist eine, die sich noch vorhersehbar anfühlt, nachdem:
- Sie 20+ Screens und mehrere User Journeys hinzugefügt haben
- Sie Berechtigungen, Feature Flags und mehrere Layouts eingeführt haben
- Sie neue Entwickler ohne Stammwissen einarbeiten
- Sie die Arbeit auf Teams aufteilen (oder zumindest auf klar definierte Verantwortungsbereiche)
Das Ziel ist nicht, das eine wahre Layout zu finden. Das Ziel ist, Änderungen günstig zu machen.
Die 3 Ordnerstrukturen, die Teams typischerweise ausprobieren (und warum eine gewinnt)
Die meisten React-Repos beginnen mit einer dieser Organisationsachsen:
| Strategie | Wie sie aussieht | Wofür sie optimiert | Wo sie zuerst bricht |
|---|---|---|---|
| Layer-first | /components, /hooks, /utils, /services | Einfacher Start, leichtes Auffinden „einer Komponente" | „Shared" wird zur Sammelstelle, Features werden verstreut |
| Route-first | /routes/orders, /routes/settings | Funktioniert gut mit verschachtelten Routern | Cross-Route-Wiederverwendung wird unklar, Domain-Logik leckt |
| Feature-first (empfohlen) | /features/billing, /features/invite-team | Klare Eigentümerschaft, lokales Denken, paralleles Arbeiten | Erfordert explizite Abhängigkeitsregeln, um unordentliches Teilen zu verhindern |
Für die meisten Produkt-Teams skaliert feature-first am besten, weil es der Art entspricht, wie Sie liefern: Sie liefern Features, keine „Komponenten". Sie können immer noch geteilte UI und geteilte Infrastruktur haben, aber diese müssen absichtlich eingeschränkt sein.
Eine Ordnerstruktur-Vorlage, die skaliert
Hier ist eine pragmatische Struktur, die für React-SPAs (Vite + React Router) funktioniert und für Meta-Frameworks (Next.js, Remix) angepasst werden kann. Die Namen sind weniger wichtig als die Trennung.

Empfohlenes Top-Level-Layout
src/
app/
providers/
layout/
config/
bootstrap.tsx
routes/
_shell/
home/
settings/
features/
invite-team/
api/
model/
ui/
index.ts
billing/
api/
model/
ui/
index.ts
entities/
user/
model/
api/
index.ts
organization/
model/
api/
index.ts
shared/
ui/
lib/
api/
config/
test/
factories/
msw/
types/
Diese Struktur setzt eine einfache Idee durch:
routes/orchestriert: Seitenzusammenstellung, Loader, Route-Level-Error-Boundaries, Zugriffskontrollenfeatures/liefert Arbeit: benutzerorientierte Fähigkeiten (Invite Team, Billing, Export CSV)entities/enthält Domain-Bausteine: User, Organization, Invoice (Typen, Invarianten, Domain-Helfer)shared/ist langweilige Infrastruktur: generische UI-Primitiven und Bibliotheken ohne Geschäftsbedeutung
Wenn Sie bereits ein Next.js App Router-Repo haben, entspricht das Äquivalent darin, Ihre app/-Routen als Orchestrierungsschicht zu behalten, während features/, entities/ und shared/ framework-agnostisch bleiben.
Was wohin gehört (mit durchsetzbaren Regeln)
Eine skalierbare Struktur braucht Regeln, die Menschen unter Druck wiederholen können. Der einfachste Weg, dies umsetzbar zu machen, besteht darin, „erlaubte Abhängigkeiten" zu definieren.
Abhängigkeitsregeln (der Teil, den die meisten Teams überspringen)
| Ordner | Darf importieren von | Sollte nicht importieren von | Warum |
|---|---|---|---|
shared/ | Nur shared/ | features/, routes/, entities/ | Hält die Shared-Schicht wirklich wiederverwendbar |
entities/ | shared/, andere entities/ (vorsichtig) | features/, routes/ | Verhindert, dass Domain-Konzepte von UI-Workflows abhängen |
features/ | shared/, entities/ | Andere features/ (standardmäßig) | Vermeidet „Feature-Spaghetti" und versteckte Kopplung |
routes/ | features/, entities/, shared/ | Nichts (es ist die Spitze) | Routen komponieren die App, sie dürfen mehr wissen |
Standardregel: Features sollten nicht von anderen Features importieren.
Wenn Sie den Drang dazu spüren, haben Sie normalerweise eines davon entdeckt:
- Ein fehlendes Domain-Konzept (zu
entities/befördern) - Eine wirklich wiederverwendbare UI-Primitive (zu
shared/ui/befördern) - Einen Workflow, der eigentlich ein Produktbereich ist (Features zusammenführen oder eine klarere Grenze definieren)
Was innerhalb eines feature/ lebt
Halten Sie Feature-Interna langweilig und konsistent. Diese Vorlage funktioniert gut:
features/invite-team/
api/
inviteTeam.ts
useInviteTeamMutation.ts
model/
schemas.ts
types.ts
ui/
InviteTeamDialog.tsx
InviteTeamForm.tsx
index.ts
Richtlinien:
api/enthält Netzwerk- und Server-State-Integration (Fetcher, Query Hooks, Mutation Hooks).model/enthält Validierung, Typen und Geschäftsregeln für das Feature (oft Zod-Schemas plus abgeleitete Typen).ui/enthält Komponenten, die im Feature-Kontext bedeutungsvoll sind.index.tsist die einzige öffentliche Oberfläche (exportieren Sie die minimalen Dinge, die die Route benötigt).
Dadurch wird jedes Feature zu einem kleinen Modul, über das Sie nachdenken, es testen und refaktorieren können, ohne das gesamte Repo zu scannen.
Was in entities/ gehört
Entities sind Ihre Domain-Nomen. Sie sind keine „Modelle" im MVC-Sinne, sondern stabile Konzepte, auf die mehrere Features verweisen könnten.
Gut passende Beispiele:
entities/user/model/permissions.tsentities/invoice/model/money.tsentities/organization/api/getOrganization.ts
Gegenbeispiele (in features/ belassen):
- „InviteTeamDialog-Zustandsmaschine" (Feature-Workflow)
- „Billing-Seiten-Filter und URL-State" (Route + Feature-Orchestrierung)
Dateibenennungs- und Export-Konventionen, die Churning verhindern
Eine Ordnerstruktur hilft, aber Skalierungsfehler entstehen oft durch inkonsistente Exporte und Benennung. Wählen Sie Standardeinstellungen, die Refaktorierungen günstig machen.
Ein praktisches Konventions-Set
| Anliegen | Empfehlung | Warum es skaliert |
|---|---|---|
| Komponentendateien | PascalCase.tsx für React-Komponenten | Macht UI in Diffs leicht erkennbar |
| Nicht-Komponenten-Module | camelCase.ts | Trennt Verhaltensmodule von UI |
| Öffentliche Exporte | Nur über index.ts pro Modul | Macht Modulgrenzen explizit |
| Importe | Absolute Aliase bevorzugen (nicht ../../..) | Verbessert Refaktorierungen und Lesbarkeit |
Ein Hinweis zu „Barrel Files":
- Verwenden Sie
index.ts, um die öffentliche API eines Features oder einer Entity zu definieren. - Vermeiden Sie tiefe Barrel-Muster, die die Hälfte des Repos re-exportieren. Sie verschleiern Abhängigkeiten und können Builds verlangsamen.
Routing als Orchestrierungsnaht (React Router und Next.js)
Ein häufiger Fehler ist es, Routen mit Domain-Logik, Daten-Shaping und UI-Details zu füllen. Ein skalierbarer Ansatz ist das Gegenteil: Routen komponieren.
React Router Beispiel
routes/settings/route.tsxkomponiert:- Zugriffskontrollen
- Datenladen
- Layout
- Feature-Komponenten
Es sollte nicht enthalten:
- Low-Level-Fetch-Aufrufe
- Schema-Validierungslogik
- Komplexe Transformationslogik, die isoliert getestet werden sollte
Next.js App Router Anpassung
In Next.js ist die Routennaht Ihr app/.../page.tsx (und manchmal layout.tsx). Das gleiche Prinzip gilt:
- Halten Sie
page.tsxals Kompositionsschicht. - Stecken Sie Feature-Logik in
features/...und importieren Sie sie. - Halten Sie geteilte Utilities in
shared/.
Dies verhindert die langsame Drift, bei der Ihr app/-Verzeichnis zu Ihrer gesamten Anwendung wird.
Gemeinsame UI: der schnellste Weg, eine „Rumpelkammer" zu schaffen
Jedes Team erstellt letztendlich components/ und beginnt, alles dort hinzuzufügen.
Eine skalierbare Shared-UI-Schicht ist absichtlich begrenzt:
shared/ui/enthält Primitiven ohne Geschäftsbedeutung (Button, Modal, Tabs).- Feature-Komponenten bleiben in
features/<name>/ui/, auch wenn sie einmal oder zweimal wiederverwendet werden.
Ein guter Test ist die Benennung:
- Wenn es nach einem Geschäftskonzept benannt ist (InvoiceStatusBadge), ist es keine Shared UI.
- Wenn es eine Primitive ist (Badge), kann es sein.
Dies verhindert die „Shared-Component-Steuer", bei der die Änderung einer UI-Komponente unerwartet fünf nicht verwandte Screens bricht.
Datenzugriff: Struktur als Sicherheitsmechanismus
Skalierende React-Apps bedeuten normalerweise skalierbaren Server State. Unabhängig davon, ob Sie TanStack Query, RTK Query, SWR oder Framework-Loader verwenden, ist die Organisation wichtig.
Praktische Standardeinstellungen:
- Stecken Sie feature-spezifische Query Hooks in das Feature (
features/billing/api/useInvoicesQuery.ts). - Stecken Sie entity-level Fetcher und Typen in Entities (
entities/user/api/getUser.ts). - Stecken Sie den Low-Level-HTTP-Client in Shared (
shared/api/httpClient.ts).
Dieses Setup macht die Abhängigkeitsrichtung offensichtlich: Shared Client, Entity-Fetcher, Feature-Hooks, Route-Komposition.
Tooling, das die Struktur durchsetzt (damit sie die Realität überlebt)
Eine Ordnerstruktur, die von Disziplin abhängt, wird unter Deadlines versagen. Setzen Sie Regeln in CI durch.
Wertvolle Leitplanken:
- TypeScript-Pfadaliase (für lesbare Importe und einfachere Verschiebungen)
- ESLint
no-restricted-imports, umfeatures/*vom Import andererfeatures/*zu blockieren - ESLint-Importregeln, um tiefe Importe wie
features/x/ui/internal/Thingzu verhindern - Eine „Nur öffentliche API"-Konvention (Import aus
features/invite-team, nicht ausfeatures/invite-team/ui/...)
Diese sind nicht teuer hinzuzufügen und zahlen sich das erste Mal aus, wenn sie eine versehentliche zirkuläre Abhängigkeit stoppen.
Ein Migrationsplan für ein bestehendes unordentliches React-Repo
Sie brauchen kein Rewrite. Ein Folder-Reset kann inkrementell sein.
Eine sichere Sequenz:
- Beginnen Sie damit,
shared/einzuführen und echte Primitiven dorthin zu verschieben (Button, Modal,formatDate). - Wählen Sie einen stark veränderten Bereich und verwandeln Sie ihn in ein Feature-Modul (
features/invite-team). Fügen Sie eineindex.tshinzu und importieren Sie nur darüber. - Fügen Sie eine ESLint-Einschränkung hinzu, die neue Cross-Feature-Importe verhindert, auch wenn Sie noch einige Legacy-Importe haben.
- Befördern Sie stabile Nomen nur dann in
entities/, wenn zwei oder mehr Features sie tatsächlich benötigen. - Halten Sie Routen als Orchestrierung. Jedes Mal, wenn Sie eine Route berühren, schieben Sie Logik in Features/Entities hinunter.
Wenn Sie dies über einige Sprints hinweg tun, „gewinnt" die Struktur durch Schwerkraft und das alte Layout hört auf zu wachsen.
Warum das auch außerhalb von Software-Teams wichtig ist
Selbst in Nicht-Software-Branchen wird wartbarer UI-Code zu einem operativen Vorteil. Denken Sie an Unternehmen, die physische Infrastruktur verkaufen und installieren, wie ein Anbieter von Photovoltaikanlagen, Notstrom und Elektroinstallationen. Sie brauchen oft interne Portale für Angebotserstellung, Terminplanung, Wartung und Kundenkommunikation. Wenn Sie diese Art von React-App aufbauen, macht eine Feature-First-Struktur es einfacher, von „einem Dashboard" zu einem Multi-Workflow-System zu wachsen. Als reales Beispiel eines solchen Geschäftskontexts siehe Notstrom & Elektrotechnik Sven Sanny.
Wann Sie externe Hilfe hinzuziehen sollten
Wenn Sie den Schmerz bereits spüren (langsames Onboarding, fragiler Shared Code, Pull Requests, die 30 Dateien über nicht verwandte Bereiche hinweg berühren), ist die schnellste Lösung normalerweise ein kurzer Architektur- und Codebase-Review mit Fokus auf:
- Modulgrenzen und Abhängigkeitsrichtung
- Refaktorierungsplan mit risikoarmen Bruchstellen
- Ein minimales Set von durchgesetzten Regeln (Lint, CI-Gates)
Wolf-Tech bietet Full-Stack-Entwicklung und Code-Quality-Beratung für Teams, die bestehende React-Codebasen modernisieren oder skalieren möchten. Wenn Sie ein zweites Paar erfahrener Augen auf Ihre Struktur haben möchten, bevor sie zu Legacy wird, können Sie Optionen auf wolf-tech.io erkunden.

