Nuxt Plugins vs. Composables: Wann ist was die richtige Wahl?
AI generated
{ }
Nuxt 3 · Plugins · Composables
Nuxt Plugins vs. Composables
Wann app-weite Initialisierung, wann wiederverwendbare Logik gefragt ist

Nuxt Plugins und Composables lösen auf den ersten Blick ähnliche Aufgaben, weil beide Code außerhalb einer einzelnen Komponente unterbringen. Tatsächlich verfolgen sie unterschiedliche Ziele: ein Plugin initialisiert einmalig etwas für die gesamte Anwendung, während ein Composable wiederverwendbare, komponierbare Logik bereitstellt, die in beliebig vielen Komponenten unabhängig voneinander genutzt wird.

14 Min. Lesezeit Nuxt Plugins Composables

1. Der Unterschied im Kern: einmalige Initialisierung vs. wiederverwendbare Logik

Ein Nuxt Plugin läuft genau einmal beim Start der Anwendung, unabhängig davon, wie viele Komponenten später tatsächlich davon Gebrauch machen. Es bekommt Zugriff auf den Nuxt-App-Kontext und kann darüber globale Vue-Funktionalität wie app.use, app.component oder app.directive registrieren, oder es kann Werte über provide bereitstellen, die dann in der gesamten Anwendung verfügbar sind, ohne dass jede Komponente sie einzeln importieren muss.

Ein Composable dagegen ist eine Funktion, die bei jedem Aufruf einen neuen, unabhängigen Ausschnitt reaktiver Logik erzeugt. Ruft eine Komponente useCounter() auf, bekommt sie ihren eigenen Zähler-Zustand, unabhängig davon, ob eine andere Komponente an anderer Stelle dieselbe Funktion ebenfalls aufruft. Diese Isolation pro Aufruf ist der zentrale Unterschied zu einem Plugin, das per Definition nur einmal ausgeführt wird und dessen Ergebnis von allen Verbrauchern geteilt wird.

2. Wann ein Nuxt Plugin die richtige Wahl ist

Ein Plugin passt immer dann, wenn etwas für die gesamte Anwendung genau einmal passieren soll, bevor die erste Komponente überhaupt rendert. Typische Beispiele sind das Registrieren einer globalen Vue-Direktive, das Einbinden einer Drittanbieter-Bibliothek, die selbst einen globalen Zustand oder eine globale Konfiguration verwaltet, oder das Setzen von Interceptors auf einer HTTP-Client-Instanz, die in der gesamten Anwendung dieselbe Basis-Konfiguration teilen soll.

Auch das Bereitstellen einer einzigen, applikationsweit geteilten Instanz eines Dienstes, etwa eines WebSocket-Clients oder eines Analytics-Trackers, gehört klar in ein Plugin, weil es keinen Sinn ergeben würde, bei jedem Komponenten-Mount eine neue Verbindung oder einen neuen Tracker aufzubauen. Das Plugin erzeugt die Instanz genau einmal und stellt sie über provide für alle Komponenten bereit, die sie über inject oder ein begleitendes Composable abrufen können.

3. Eine Drittanbieter-Bibliothek per Plugin registrieren

Das klassische Beispiel ist eine Bibliothek wie ein Toast-Benachrichtigungssystem oder eine Chart-Bibliothek, die als Vue-Plugin ausgeliefert wird und selbst app.use erwartet. In einem Nuxt-Plugin unter app/plugins wird die Vue-App-Instanz aus dem nuxtApp-Kontext entnommen und die Drittanbieter-Bibliothek genau einmal registriert, wonach sie in jeder Komponente ganz normal über ihre eigene, von der Bibliothek dokumentierte API genutzt werden kann.

Wichtig ist dabei, dass Nuxt Plugin-Dateien anhand ihres Dateinamens automatisch client- oder serverseitig ausgeführt werden, sofern man das Suffix .client.ts oder .server.ts verwendet. Eine Bibliothek, die auf window oder document zugreift, muss zwingend als Client-Plugin markiert werden, da sie sonst während des serverseitigen Renderings mit einem Fehler abbricht, weil diese globalen Objekte auf dem Server schlicht nicht existieren.


// app/plugins/toast.client.ts
import ToastLibrary from 'some-toast-library'

export default defineNuxtPlugin((nuxtApp) => {
  nuxtApp.vueApp.use(ToastLibrary, {
    position: 'top-right',
    duration: 4000,
  })
})

4. Wann ein Composable die richtige Wahl ist

Ein Composable passt immer dann, wenn Logik in mehreren Komponenten wiederverwendet werden soll, jede Verwendung aber ihren eigenen, unabhängigen Zustand haben soll. Ein useLocalStorage-Composable etwa, das einen Wert mit dem localStorage synchronisiert, soll bei jedem Aufruf mit einem anderen Schlüssel einen unabhängigen ref erzeugen, nicht einen einzigen, applikationsweit geteilten Zustand, der von allen Aufrufern gleichzeitig gelesen und geschrieben wird.

Auch reine Hilfsfunktionen ohne Nuxt-spezifische Abhängigkeiten, etwa eine useDebounce- oder useMousePosition-Funktion, gehören als Composable in das app/composables-Verzeichnis, wo Nuxt sie automatisch importiert, ohne dass man einen expliziten import-Pfad in jeder Komponente schreiben muss. Composables sind damit das Werkzeug der Wahl für alles, was man sich als eine parametrisierbare, komponierbare Einheit reaktiver Logik vorstellen kann, die in unterschiedlichen Komponenten unterschiedlich konfiguriert wird.

5. provide/inject als Brücke zwischen Plugin und Composable

In der Praxis werden Plugin und Composable häufig gemeinsam eingesetzt: das Plugin erzeugt einmalig eine geteilte Instanz und stellt sie über nuxtApp.provide bereit, während ein begleitendes Composable diese Instanz per useNuxtApp() oder inject abruft und dabei eine sauber typisierte, komfortable API darum herum baut. Auf diese Weise muss keine Komponente direkt mit dem rohen inject-Aufruf oder dem Nuxt-App-Kontext arbeiten, sondern ruft stattdessen einfach useAnalytics() oder useWebsocket() auf.

Dieses Muster trennt sauber zwischen dem Wo und Wie der einmaligen Initialisierung, die im Plugin lebt, und der Art, wie Komponenten auf das Ergebnis zugreifen, die im Composable lebt. Ändert sich später die zugrunde liegende Bibliothek oder deren Konfiguration, muss nur das Plugin angepasst werden, während die Composable-Schnittstelle für alle bereits bestehenden Aufrufstellen unverändert bleiben kann, was Refactorings deutlich risikoärmer macht.


// app/plugins/analytics.client.ts
import { createAnalyticsClient } from 'analytics-sdk'

export default defineNuxtPlugin((nuxtApp) => {
  const analytics = createAnalyticsClient({ apiKey: useRuntimeConfig().public.analyticsKey })
  nuxtApp.provide('analytics', analytics)
})

// app/composables/useAnalytics.ts
export function useAnalytics() {
  const { $analytics } = useNuxtApp()
  return {
    trackEvent: (name: string, payload?: Record<string, unknown>) =>
      $analytics.track(name, payload),
  }
}

6. Häufige Fehlentscheidung: Logik fälschlich als Plugin implementiert

Ein wiederkehrender Fehler ist, eine an sich wiederverwendbare Logik, etwa das Verwalten eines Formular-Zustands oder das Abfragen einer API mit Ladezustand, direkt in einem Plugin zu implementieren, nur weil dort ebenfalls Zugriff auf den Nuxt-App-Kontext besteht. Das Ergebnis ist meist ein einziger, applikationsweit geteilter Zustand, obwohl eigentlich jede Komponente, die diese Logik nutzt, ihre eigene unabhängige Instanz haben sollte, etwa weil zwei verschiedene Formulare auf derselben Seite sonst denselben Ladezustand teilen würden, obwohl sie völlig unabhängige Anfragen auslösen.

Der umgekehrte Fehler kommt seltener vor, ist aber ebenso problematisch: eine Bibliothek, die tatsächlich nur einmal app-weit registriert werden muss, in ein Composable zu packen, das bei jedem Komponenten-Mount erneut versucht, dieselbe globale Registrierung durchzuführen. Bei manchen Bibliotheken führt das lediglich zu unnötiger doppelter Arbeit, bei anderen, etwa bei der Registrierung eines globalen Event-Listeners ohne Deduplizierung, kann es zu handfesten Bugs wie mehrfach ausgelösten Events führen.

7. Client-only, server-only und universelle Plugins

Nuxt unterscheidet über die Dateinamens-Konvention zwischen Plugins, die auf beiden Seiten laufen, solchen, die ausschließlich im Browser laufen sollen (.client.ts), und solchen, die ausschließlich auf dem Server laufen sollen (.server.ts). Diese Unterscheidung ist bei der Entscheidung zwischen Plugin und Composable ein zusätzliches Argument für das Plugin, weil ein Composable diese Umgebungstrennung nicht von sich aus mitbringt und man den entsprechenden Check, etwa über import.meta.client, manuell im Composable-Code ergänzen müsste.

Bei einer Bibliothek, die zwingend eine Browser-API wie window.localStorage oder eine Canvas-Zeichenfläche benötigt, ist ein .client.ts-Plugin daher meist die robustere Wahl gegenüber einem Composable, das bei jedem Aufruf prüfen müsste, ob es gerade auf dem Server oder im Browser läuft. Composables, die tatsächlich sowohl serverseitig als auch clientseitig sinnvoll funktionieren, etwa reine Datentransformationen ohne Browser-Abhängigkeit, profitieren dagegen nicht von dieser Unterscheidung und bleiben bewusst universell einsetzbar.

8. Testbarkeit: Composables gegenüber Plugins

Composables lassen sich in aller Regel deutlich einfacher isoliert testen als Plugins, weil sie als reine Funktionen aufgerufen und ihre Rückgabewerte direkt in einem Unit-Test geprüft werden können, notfalls mit einem minimalen Vue-Testkontext über withSetup oder eine ähnliche Testhilfe. Ein Plugin dagegen ist tief mit dem Nuxt-App-Lifecycle verzahnt und lässt sich meist nur sinnvoll in einem Integrations- oder End-to-End-Test prüfen, der eine vollständige Nuxt-App-Instanz hochfährt.

Diese Testbarkeits-Überlegung ist ein weiteres praktisches Argument dafür, so viel Logik wie möglich in Composables zu bündeln und Plugins bewusst schlank zu halten, im Idealfall nur mit der eigentlichen Registrierung einer Bibliothek oder Instanz, während jede darüber hinausgehende Logik, wie das Formatieren von Werten oder das Zusammenführen mehrerer Datenquellen, in ein separates, unabhängig testbares Composable ausgelagert wird.

9. Eine kurze Entscheidungshilfe für den Alltag

Wer unsicher ist, kann sich an einer einfachen Frage orientieren: Soll dieser Code genau einmal beim Start der Anwendung passieren und danach von allen geteilt werden, oder soll jede Komponente, die ihn nutzt, ihre eigene unabhängige Instanz bekommen? Im ersten Fall gehört der Code in ein Plugin, im zweiten Fall in ein Composable. Bei geteilten Instanzen, auf die mehrere Komponenten zugreifen sollen, ist die Kombination aus einem schlanken Plugin, das nur registriert und bereitstellt, und einem begleitenden Composable, das eine komfortable API darum baut, in der Praxis fast immer die wartbarste Lösung.

Diese einfache Unterscheidung nach Einmaligkeit gegenüber Wiederverwendbarkeit löst die meisten Zweifelsfälle zuverlässig auf und verhindert sowohl das Aufblasen von Plugins mit eigentlich komponierbarer Logik als auch das versehentliche Vervielfachen von Instanzen, die eigentlich nur einmal existieren sollten. Wer diese Grundregel im Team konsequent anwendet, vermeidet die meisten der oben beschriebenen Fehlentscheidungen von vornherein, ohne dass es dafür eine komplizierte Checkliste braucht.

Kriterium Nuxt Plugin Composable Faustregel
Ausführungshäufigkeit Genau einmal beim App-Start Bei jedem Aufruf neu Einmalig -> Plugin, wiederholt -> Composable
Zustand Applikationsweit geteilt Pro Aufruf unabhängig Geteilter Zustand spricht für Plugin
Typisches Beispiel Drittanbieter-Bibliothek per app.use registrieren useDebounce, useLocalStorage, useFetch Registrierung vs. wiederverwendbare Logik
Testbarkeit Nur mit vollem Nuxt-Kontext sinnvoll testbar Isoliert als reine Funktion testbar Mehr Logik in Composables erleichtert Tests
Umgebungssteuerung Über Dateinamens-Suffix .client.ts / .server.ts Manuell über import.meta.client prüfen Browser-API-Abhängigkeit spricht für Plugin

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 Plugins vs. Composables: Das Wichtigste auf einen Blick

Plugin

Einmalige, app-weite Initialisierung, etwa das Registrieren einer Bibliothek

Composable

Wiederverwendbare Logik mit eigenem Zustand pro Aufruf

Brücke

Plugin stellt per provide bereit, Composable liest per useNuxtApp

Häufiger Fehler

Komponentenspezifische Logik fälschlich als geteiltes Plugin implementiert

11. FAQ: Nuxt Plugins vs. Composables: Das Wichtigste auf einen Blick

1Was ist der wichtigste Unterschied zwischen einem Nuxt Plugin und einem Composable?
Ein Plugin läuft genau einmal beim Start der Anwendung und sein Ergebnis wird von allen Komponenten geteilt, während ein Composable bei jedem Aufruf einen neuen, unabhängigen Ausschnitt reaktiver Logik erzeugt.
2Wann sollte ich eine Drittanbieter-Bibliothek als Plugin registrieren?
Immer dann, wenn die Bibliothek selbst app.use erwartet oder eine einzige, app-weit geteilte Instanz verwalten soll, etwa ein Toast-System oder ein Analytics-Client.
3Warum brauchen manche Nuxt Plugins das Suffix .client.ts?
Weil Bibliotheken, die auf window oder document zugreifen, während des serverseitigen Renderings abstürzen würden. Das Suffix sorgt dafür, dass das Plugin ausschließlich im Browser ausgeführt wird.
4Kann ich in einem Composable auch app.use aufrufen?
Technisch möglich ist es, aber nicht sinnvoll, weil ein Composable bei jedem Komponenten-Mount erneut aufgerufen werden könnte und die Registrierung dann mehrfach passieren würde, was bei den meisten Bibliotheken Fehler oder doppelte Effekte verursacht.
5Wie funktioniert die Brücke zwischen Plugin und Composable über provide/inject?
Das Plugin erzeugt einmalig eine Instanz und stellt sie über nuxtApp.provide bereit. Ein begleitendes Composable ruft diese Instanz über useNuxtApp() ab und baut eine komfortable, typisierte API darum.
6Was ist die häufigste Fehlentscheidung bei diesem Thema?
Eigentlich wiederverwendbare, komponentenspezifische Logik wie Formular- oder Ladezustand fälschlich in ein Plugin zu packen, wodurch mehrere Komponenten unbeabsichtigt denselben geteilten Zustand bekommen, statt jeweils eine eigene Instanz.
7Sind Composables leichter zu testen als Plugins?
Ja, deutlich. Composables lassen sich meist als reine Funktionen mit minimalem Vue-Testkontext prüfen, während Plugins in der Regel eine vollständige Nuxt-App-Instanz für einen sinnvollen Test benötigen.
8Muss jedes Plugin eine begleitende Composable-Schnittstelle haben?
Nicht zwingend, aber es ist üblich und empfehlenswert, sobald mehrere Komponenten auf dieselbe, per Plugin bereitgestellte Instanz zugreifen sollen, weil das Composable die rohe inject-Nutzung vor den Komponenten verbirgt.
9Was passiert, wenn ich window in einem universellen, nicht als client markierten Plugin verwende?
Während des serverseitigen Renderings existiert window nicht, sodass das Plugin mit einem ReferenceError abstürzt und im schlimmsten Fall das gesamte serverseitige Rendering der Seite verhindert.
10Lohnt es sich, reine Hilfsfunktionen ohne Nuxt-Bezug als Composable statt als normale Utility-Funktion zu schreiben?
Ja, sobald sie reaktive Werte wie ref oder computed verwenden, da Nuxt Composables im app/composables-Verzeichnis automatisch importiert und in die Komponenten-Struktur integriert, was bei reinen, nicht-reaktiven Hilfsfunktionen ohnehin optional ist.