Dateibasiertes Routing
In Nuxt definieren Dateien deine Routen — keine zentrale routes.rb nötig. Wie Rails Resourcen, aber noch einfacher.
Dateien = Routen
Das Routing in Nuxt funktioniert nach einem simplen Prinzip: Jede .vue-Datei im pages/-Verzeichnis wird automatisch zu einer Route. Kein config/routes.rb, kein manuelles Registrieren.
pages/
├── index.vue → /
├── about.vue → /about
├── contact.vue → /contact
└── blog/
├── index.vue → /blog
└── [slug].vue → /blog/:slug
# Rails-Äquivalent in routes.rb:
# root 'pages#home'
# get '/about', to: 'pages#about'
# get '/contact', to: 'pages#contact'
# resources :blog, only: [:index, :show]Rails-Vergleich: config/routes.rb
In Rails definierst du Routen zentral in config/routes.rb: resources :posts erzeugt 7 Routen. In Nuxt erstellst du stattdessen Dateien. Der Vorteil: Du siehst sofort im Dateibaum, welche Routen existieren — keine Abstraktion dazwischen.
Dynamische Routen mit [id]
Eckige Klammern im Dateinamen erzeugen dynamische Segmente — genau wie :id in Rails-Routen.
<template>
<article>
<h1>Post #{{ route.params.id }}</h1>
<p>Dynamischer Parameter aus der URL</p>
</article>
</template>
<script setup lang="ts">
// useRoute() ist auto-importiert
const route = useRoute()
// route.params.id enthält den Wert aus der URL
// /posts/42 → route.params.id === '42'
// /posts/hello → route.params.id === 'hello'
</script>Mehrere dynamische Segmente sind auch möglich:
pages/
├── posts/[id].vue → /posts/:id
├── users/[userId]/posts.vue → /users/:userId/posts
└── [category]/[id].vue → /:category/:id
# Beispiele:
# /posts/42 → { id: '42' }
# /users/5/posts → { userId: '5' }
# /technik/123 → { category: 'technik', id: '123' }Typ-Sicherheit
Der Parameter ist immer ein string. Für numerische IDs musst du selbst konvertieren: Number(route.params.id). In Rails macht das der Type-Cast in params[:id].to_i — selbes Prinzip.
Catch-all Routen mit [...slug]
Drei Punkte vor dem Parameter fangen alle verbleibenden Pfadsegmente ein. Perfekt für CMS-Seiten, Dokumentation oder verschachtelte Kategorien.
<template>
<div>
<h1>Dokumentation</h1>
<p>Pfad: {{ route.params.slug }}</p>
<!-- slug ist ein Array! -->
</div>
</template>
<script setup lang="ts">
const route = useRoute()
// /docs/getting-started
// → route.params.slug = ['getting-started']
// /docs/api/composables/useFetch
// → route.params.slug = ['api', 'composables', 'useFetch']
</script>pages/docs/[...slug].vue
# Matcht:
# /docs/intro → slug: ['intro']
# /docs/guide/installation → slug: ['guide', 'installation']
# /docs/api/ref → slug: ['api', 'ref']
# Vergleich mit Rails:
# get 'docs/*path', to: 'docs#show'404-Seiten
Eine Datei pages/[...slug].vue im Root fängt alle nicht gematchten Routen — perfekt als Custom-404-Seite. In Rails wäre das die match '*path' Route am Ende der routes.rb.
Verschachtelte Routen
Unterordner erzeugen verschachtelte URL-Pfade. Für echte Layout-Verschachtelung (Nested Routes mit <NuxtPage />) brauchst du eine gleichnamige Vue-Datei neben dem Ordner.
# Für verschachtelte Layouts:
pages/
├── users.vue → Parent (enthält <NuxtPage />)
└── users/
├── index.vue → /users (in Parent eingebettet)
└── [id].vue → /users/:id (in Parent eingebettet)
# users.vue rendert den Rahmen,
# die Dateien in users/ werden darin angezeigt.<template>
<div>
<h1>Benutzerverwaltung</h1>
<nav>
<NuxtLink to="/users">Alle Benutzer</NuxtLink>
</nav>
<!-- Hier wird die Child-Route gerendert -->
<NuxtPage />
</div>
</template>
<!-- Wie in Rails:
layouts/users.html.erb mit <%= yield %>
Das Layout bleibt, der Inhalt wechselt.
-->Rails-Vergleich: Nested Resources
In Rails nutzt du resources :users do; resources :posts; end für verschachtelte Routen. In Nuxt ergibt sich die Verschachtelung aus der Ordnerstruktur. Der Parent (users.vue) mit <NuxtPage /> ist wie ein Layout, das yield enthält.
Navigation mit NuxtLink
Für interne Links verwendest du <NuxtLink> statt <a>. Das aktiviert clientseitiges Routing ohne vollständigen Seitenreload — wie Turbolinks in Rails, aber nativ.
<template>
<nav>
<!-- Einfacher Link -->
<NuxtLink to="/">Startseite</NuxtLink>
<!-- Dynamische Route -->
<NuxtLink :to="'/posts/' + postId">
Zum Post
</NuxtLink>
<!-- Mit Objekt-Syntax -->
<NuxtLink :to="{ path: '/users', query: { page: 2 } }">
Benutzer (Seite 2)
</NuxtLink>
<!-- Externer Link (normaler <a> Tag) -->
<NuxtLink to="https://nuxt.com" external>
Nuxt Docs
</NuxtLink>
<!-- Aktiver Link bekommt automatisch CSS-Klassen -->
<!-- .router-link-active & .router-link-exact-active -->
</nav>
</template>
<script setup lang="ts">
const postId = ref(42)
</script>Prefetching
NuxtLink lädt automatisch die Zielseite vor, sobald der Link im Viewport sichtbar wird. Das macht die Navigation blitzschnell. In Rails brauchst du dafür Turbo Prefetch — in Nuxt ist es eingebaut.
Programmatische Navigation
Manchmal musst du per Code navigieren — nach einem Login, einer Formular-Submission oder einem API-Call. Nuxt bietet dafür navigateTo().
<template>
<form @submit.prevent="handleLogin">
<input v-model="email" type="email" placeholder="E-Mail" />
<input v-model="password" type="password" placeholder="Passwort" />
<button type="submit">Einloggen</button>
</form>
</template>
<script setup lang="ts">
const email = ref('')
const password = ref('')
async function handleLogin() {
try {
await $fetch('/api/auth/login', {
method: 'POST',
body: { email: email.value, password: password.value }
})
// Wie redirect_to in Rails
navigateTo('/dashboard')
// Oder mit Replace (kein Browser-History Eintrag):
// navigateTo('/dashboard', { replace: true })
// Oder externe URL:
// navigateTo('https://example.com', { external: true })
} catch (error) {
console.error('Login fehlgeschlagen:', error)
}
}
</script>Rails-Vergleich: redirect_to
navigateTo('/dashboard') ist wie redirect_to dashboard_path in Rails. Der Unterschied: In Nuxt passiert die Navigation clientseitig ohne Server-Roundtrip. Für serverseitige Redirects in API-Routen gibt es sendRedirect(event, '/ziel').
Route Middleware — Ein Ausblick
Middleware wird vor der Navigation ausgeführt — wie before_action in Rails-Controllern. Perfekt für Auth-Checks, Logging oder Redirects.
// middleware/auth.ts
// Wie before_action :authenticate_user! in Rails
export default defineNuxtRouteMiddleware((to, from) => {
const { isLoggedIn } = useAuth()
if (!isLoggedIn.value) {
// Redirect zum Login, merke Ziel-URL
return navigateTo({
path: '/login',
query: { redirect: to.fullPath }
})
}
})<template>
<div>
<h1>Dashboard</h1>
<p>Nur für eingeloggte Benutzer sichtbar.</p>
</div>
</template>
<script setup lang="ts">
// Middleware zuweisen — wie before_action in Rails
layout: 'app',
middleware: 'auth' // → middleware/auth.ts
})
</script>Drei Arten von Middleware
Inline: Direkt in der Seite definiert.
Named: In middleware/ als Datei — wiederverwendbar.
Global: Dateiname mit .global Suffix — läuft bei jeder Navigation.
Komplettes Beispiel: Blog-Routing
So sieht ein typisches Blog-Routing in Nuxt aus — ohne eine einzige Zeile Router-Konfiguration:
pages/
├── blog/
│ ├── index.vue → /blog (Liste aller Posts)
│ ├── [slug].vue → /blog/:slug (Einzelner Post)
│ ├── kategorie/
│ │ └── [name].vue → /blog/kategorie/:name
│ └── archiv/
│ └── [...date].vue → /blog/archiv/2024/01
# Rails-Äquivalent:
# resources :blog, only: [:index, :show]
# get 'blog/kategorie/:name', to: 'blog#category'
# get 'blog/archiv/*date', to: 'blog#archive'Nächster Schritt
Jetzt kennst du das Routing-System. Als Nächstes schauen wir uns Layouts an — wie du Seitenstrukturen definierst, die mehrere Seiten teilen.