Nuxt Layers: Mehrere Apps mit geteilter Konfiguration aufbauen
AI generated
{ }
Nuxt 3 · Layers · Architektur
Nuxt Layers für mehrere Apps mit geteilter Konfiguration
Mit extends einen gemeinsamen Basis-Layer für Multi-Brand-Setups aufbauen

Über die extends-Property in der nuxt.config.ts lassen sich ganze Nuxt-Projekte als Basis-Layer wiederverwenden, inklusive Komponenten, Composables, Layouts und Konfiguration. Für Multi-Brand-Setups mit einem gemeinsamen Kern und markenspezifischen Overrides ist das oft eine leichtgewichtigere Lösung als ein klassisches Monorepo mit separaten npm-Paketen.

15 Min. Lesezeit extends · Nuxt 3 Multi-Brand

1. Was sind Nuxt Layers?

Ein Nuxt Layer ist im Kern nichts anderes als ein eigenständiges Nuxt-Verzeichnis mit der gewohnten Ordnerstruktur aus components, composables, pages, layouts und einer eigenen nuxt.config.ts. Ein solcher Layer lässt sich in einem anderen Nuxt-Projekt als Grundlage einbinden, wodurch alle darin enthaltenen Komponenten, Composables und Konfigurationswerte automatisch für das einbindende Projekt zur Verfügung stehen, ganz ohne manuellen Import.

Das Konzept eignet sich besonders für Situationen, in denen mehrere Anwendungen einen erheblichen Teil ihrer Funktionalität teilen, sich aber in Details wie Branding, einzelnen Seiten oder spezifischen Features unterscheiden. Statt Code über Copy-Paste oder ein separates npm-Paket zu teilen, wird der gemeinsame Kern als Layer ausgelagert und von jeder Anwendung über die extends-Property referenziert.

2. Die extends-Property im Detail

Die extends-Property in der nuxt.config.ts nimmt einen Pfad, ein Array von Pfaden oder sogar eine Git-Quelle entgegen und lädt den referenzierten Layer, bevor die eigene Konfiguration angewendet wird. Nuxt führt dabei automatisch ein tiefes Merge für Konfigurationsoptionen, Komponenten-Verzeichnisse, Composables-Auto-Imports und Layout-Definitionen durch, sodass sich Basis-Layer und aufrufendes Projekt nahtlos zu einer einzigen Anwendung zusammenfügen.

Layer können dabei selbst wieder andere Layer per extends einbinden, wodurch sich Hierarchien mit mehreren Ebenen aufbauen lassen, etwa ein sehr generischer Basis-Layer für alle Marken, darauf aufbauend ein regionaler Layer für bestimmte Länder, und ganz oben die konkrete, markenspezifische Anwendung. Jede Ebene kann dabei gezielt Teile der darunterliegenden Ebenen überschreiben.

3. Praxisbeispiel: Ein Basis-Layer mit gemeinsamen Komponenten

Im folgenden Beispiel liegt ein Basis-Layer im Verzeichnis layers/base und enthält eine gemeinsame Header-Komponente sowie eine geteilte Runtime-Config für die API-Basis-URL. Zwei markenspezifische Anwendungen binden diesen Layer über extends ein und ergänzen jeweils nur ihre eigenen, markenspezifischen Seiten und Farbwerte.

Wichtig ist dabei, dass Komponenten aus dem Basis-Layer in der konkreten Anwendung ganz normal ohne zusätzlichen Import verwendet werden können, genau wie lokale Komponenten. Überschreibt die Anwendung eine Komponente mit demselben Namen lokal, hat die lokale Version automatisch Vorrang vor der Version aus dem Layer.


// layers/base/nuxt.config.ts
export default defineNuxtConfig({
  runtimeConfig: {
    public: {
      apiBase: 'https://api.example.com',
    },
  },
});

// layers/base/components/AppHeader.vue
// <template><header>Gemeinsamer Header für alle Marken</header></template>

// apps/brand-a/nuxt.config.ts
export default defineNuxtConfig({
  extends: ['../../layers/base'],
  app: {
    head: { title: 'Marke A' },
  },
  runtimeConfig: {
    public: {
      themeColor: '#0066cc',
    },
  },
});

4. Anwendungsfall: Multi-Brand-Setup

Ein klassisches Multi-Brand-Setup entsteht, wenn ein Unternehmen mehrere Marken mit weitgehend identischer Funktionalität betreibt, etwa denselben Checkout-Prozess, dieselbe Produktsuche und dieselbe Kontostruktur, aber jeweils eigenem Branding, eigener Domain und teils eigenen Zusatzfeatures. Ohne geteilte Basis müsste jede Änderung an der gemeinsamen Logik in jeder Marke einzeln nachgezogen werden, was schnell zu Inkonsistenzen führt.

Mit einem Basis-Layer wird die gemeinsame Logik, also Composables für den Warenkorb, gemeinsame UI-Komponenten und geteilte Middleware, an genau einer Stelle gepflegt. Jede markenspezifische Anwendung bindet diesen Layer ein und ergänzt nur, was tatsächlich markenspezifisch ist: Logo, Farbpalette, spezielle Landingpages oder abweichende Zahlungsanbieter.

5. Markenspezifische Overrides

Overrides funktionieren in Nuxt Layers über die Dateisystem-Konvention: Legt die konkrete Anwendung eine Datei mit demselben relativen Pfad wie im Basis-Layer an, etwa components/AppHeader.vue, gewinnt die Version der Anwendung. Dasselbe Prinzip gilt für Seiten, Layouts und Composables, wodurch sich gezielt einzelne Bausteine ersetzen lassen, ohne den gesamten Layer zu duplizieren.

Für Konfigurationswerte wie runtimeConfig greift stattdessen ein tiefes Merge: Werte aus der Anwendung ergänzen oder überschreiben die Werte aus dem Layer, ohne dass nicht explizit gesetzte Werte verloren gehen. Das erlaubt es, im Basis-Layer sinnvolle Standardwerte zu definieren und in der konkreten Anwendung nur die tatsächlich abweichenden Werte anzupassen.

6. Reihenfolge und Auflösung von Layers

Werden mehrere Layer über ein Array in extends eingebunden, werden sie in der angegebenen Reihenfolge verarbeitet, wobei später genannte Layer Vorrang vor früher genannten haben. Das aufrufende Projekt selbst hat dabei immer die höchste Priorität und kann jeden Wert aus jedem eingebundenen Layer überschreiben.

Bei tief verschachtelten Layer-Hierarchien lohnt es sich, die Auflösungsreihenfolge zu dokumentieren, da Fehler in der Praxis oft daraus entstehen, dass ein Entwickler nicht mehr genau weiß, aus welcher Ebene ein bestimmter Konfigurationswert oder eine bestimmte Komponente tatsächlich stammt. Ein klar benannter, flacher Layer-Baum ist meist wartungsfreundlicher als viele tief verschachtelte Ebenen.

7. Unterschied zu einem klassischen Monorepo mit geteilten npm-Paketen

Ein klassischer Monorepo-Ansatz teilt Code über eigenständige npm-Pakete, die versioniert, gebaut und explizit importiert werden müssen. Das bringt saubere Versionsgrenzen mit sich, verursacht aber auch zusätzlichen Aufwand: Ein gemeinsames Paket muss bei Änderungen neu gebaut und in den abhängigen Projekten aktualisiert werden, bevor die Änderung dort sichtbar wird.

Nuxt Layers verzichten auf diesen Build- und Versionierungsschritt vollständig. Ein Layer wird zur Build-Zeit direkt in das aufrufende Projekt eingebunden, Änderungen am Layer sind im Entwicklungsmodus sofort im abhängigen Projekt sichtbar, ganz ohne separaten Publish-Schritt. Der Preis dafür ist weniger strikte Versionskontrolle, was für eng zusammenarbeitende Teams im selben Repository meist ein guter Kompromiss ist, für vollständig unabhängige Teams mit eigenem Release-Zyklus aber ein Nachteil sein kann.

8. Grenzen und Fallstricke von Nuxt Layers

Ein häufiger Fallstrick ist die Annahme, Layer würden wie unabhängige, isolierte Module funktionieren. Tatsächlich werden alle Layer und das aufrufende Projekt zu einer einzigen Nuxt-Anwendung zusammengeführt, weshalb Namenskollisionen bei Komponenten, Composables oder Routen zwischen verschiedenen Layern zu unerwartetem Verhalten führen können, wenn sie nicht bewusst als Override gedacht sind.

Auch Abhängigkeiten aus der package.json eines Layers werden nicht automatisch installiert, wenn der Layer als lokaler Ordner eingebunden wird. Nutzt ein Basis-Layer eine bestimmte Bibliothek, muss diese in der Regel auch in der package.json der konkreten Anwendung als Abhängigkeit geführt werden, was bei der Planung eines Layer-Systems leicht übersehen wird. Ein sinnvoller Ansatz ist deshalb, alle vom Basis-Layer benötigten Bibliotheken zentral zu dokumentieren, damit neue markenspezifische Anwendungen von Anfang an mit einer vollständigen Abhängigkeitsliste starten.

9. Fazit: Layers als leichtgewichtige Alternative

Nuxt Layers bieten eine leichtgewichtige Möglichkeit, mehrere eng verwandte Anwendungen auf einer gemeinsamen Basis aufzubauen, ohne den organisatorischen Aufwand eines klassischen Monorepos mit separat versionierten Paketen. Für Multi-Brand-Setups, bei denen der Großteil der Logik identisch ist und sich nur Branding sowie einzelne Details unterscheiden, ist das Konzept besonders gut geeignet.

Wer mit Layers arbeitet, sollte die Verantwortlichkeiten zwischen Basis-Layer und konkreter Anwendung klar dokumentieren und Namenskollisionen bewusst vermeiden, statt sich blind auf implizites Überschreiben zu verlassen. Bei wachsender Komplexität mit vielen unabhängigen Teams kann sich später dennoch der Umstieg auf ein Monorepo mit echten, versionierten Paketen lohnen.

Aspekt Nuxt Layers (extends) Monorepo mit npm-Paketen
Einbindung Direkt zur Build-Zeit über extends Expliziter Import eines Pakets
Änderungen sichtbar Sofort im Dev-Modus Erst nach Build und Update der Abhängigkeit
Versionskontrolle Kein separates Versionsschema Eigene Versionsnummern pro Paket
Setup-Aufwand Gering, reine Ordnerstruktur Höher, Build-Pipeline pro Paket nötig
Am besten geeignet für Eng zusammenarbeitende Teams, ähnliche Apps Unabhängige Teams, eigener Release-Zyklus

Mironsoft

Vue-Architektur, Composition API und Nuxt-Performance

Vue-Anwendungen, die mit jedem Feature nicht komplizierter werden?

Wir prüfen bestehende Vue- und Nuxt-Projekte auf unstrukturierte Composables, ungenutzte Reaktivität und aufgeblähte Bundles und bauen daraus eine Architektur, die neue Features aufnimmt, ohne die Codebasis unübersichtlicher zu machen.

Architektur-Review

Composables, State-Management und Komponentenstruktur auf Wartbarkeit prüfen.

Performance-Audit

Reaktivitäts-Overhead, Bundle-Größe und Nuxt-Rendering-Strategie systematisch optimieren.

Nuxt-Integration

SSR/SSG-Setup und API-Anbindung robust und typsicher aufbauen.

10. Zusammenfassung

Nuxt Layers für geteilte Konfiguration: Das Wichtigste auf einen Blick

Kernmechanismus

extends-Property in der nuxt.config.ts für geteilte Basis-Layer

Haupteffekt

Gemeinsame Komponenten, Composables und Config ohne npm-Paket

Typischer Anwendungsfall

Multi-Brand-Setup mit gemeinsamem Kern und markenspezifischen Overrides

Abgrenzung

Kein Build- oder Versionierungsschritt wie bei klassischen Monorepo-Paketen

11. FAQ: Nuxt Layers für geteilte Konfiguration: Das Wichtigste auf einen Blick

1Was genau wird beim Einbinden eines Layers gemergt?
Komponenten-Verzeichnisse, Composables-Auto-Imports, Layouts, Seiten sowie Konfigurationsoptionen wie runtimeConfig werden per tiefem Merge zusammengeführt. Das aufrufende Projekt hat dabei immer Vorrang vor allen eingebundenen Layern.
2Kann ein Layer selbst wieder einen anderen Layer einbinden?
Ja, Layer können über ihre eigene extends-Property weitere Layer einbinden, wodurch sich mehrstufige Hierarchien aufbauen lassen, etwa ein globaler Basis-Layer, ein regionaler Layer und darauf aufbauend die konkrete Anwendung.
3Wie überschreibe ich eine Komponente aus einem Layer?
Eine Datei mit demselben relativen Pfad in der konkreten Anwendung, zum Beispiel components/AppHeader.vue, hat automatisch Vorrang vor der gleichnamigen Komponente aus dem Basis-Layer, ganz ohne zusätzliche Konfiguration.
4Werden Abhängigkeiten eines Layers automatisch installiert?
Nein, bei einem lokal eingebundenen Layer müssen dessen npm-Abhängigkeiten in der Regel zusätzlich in der package.json der konkreten Anwendung eingetragen werden, da Nuxt sie nicht automatisch auflöst.
5Kann ich einen Layer auch aus einem Git-Repository einbinden?
Ja, die extends-Property akzeptiert neben lokalen Pfaden auch Git-Quellen und npm-Pakete, wodurch sich Layer auch über Projektgrenzen hinweg als eigenständige Pakete verteilen lassen.
6Sind Layers besser als ein Monorepo mit npm-Paketen?
Weder noch, es kommt auf den Anwendungsfall an. Layers sind leichtgewichtiger und schneller in der Entwicklung, während ein Monorepo mit versionierten Paketen bei vielen unabhängigen Teams klarere Grenzen und stabilere Versionsgarantien bietet.
7Kann jede Anwendung ihre eigene runtimeConfig behalten?
Ja, runtimeConfig-Werte werden per tiefem Merge zusammengeführt. Der Basis-Layer kann sinnvolle Standardwerte definieren, während jede konkrete Anwendung nur die tatsächlich abweichenden Werte überschreibt.
8Was passiert bei Namenskollisionen zwischen zwei eingebundenen Layern?
Der später in der extends-Liste genannte Layer hat Vorrang vor dem früher genannten. Solche Kollisionen sind ein häufiger Fallstrick und sollten durch klare Namenskonventionen innerhalb der Layer-Hierarchie vermieden werden.
9Eignen sich Layers auch für ein einzelnes Design-System?
Ja, ein häufiges Muster ist ein eigener Layer, der ausschließlich UI-Komponenten, Tailwind-Konfiguration und Icons enthält und von mehreren, ansonsten unabhängigen Anwendungen eingebunden wird.
10Muss ich für Layers ein bestimmtes Nuxt-Modul installieren?
Nein, die extends-Property ist Bestandteil des Nuxt-Core und benötigt kein zusätzliches Modul. Für den Bezug aus Git-Quellen oder npm-Registrys kann je nach Setup lediglich die interne giget-Bibliothek eine Rolle spielen, die Nuxt bereits mitbringt.