Skip to content

Kapitel 5: Seiten & Komponenten

📖 Lernziele

In diesem Kapitel lernen Sie:

  • ✅ Seitenkomponenten erstellen (page.js)
  • ✅ Layout-Komponenten verwenden (layout.js)
  • ✅ Eigene Komponenten entwickeln und verwenden
  • ✅ Statische Assets verarbeiten (Bilder, Fonts, CSS)
  • ✅ Metadaten für SEO konfigurieren

5.1 Seitenkomponenten (page.js)

🎯 Was ist eine Seitenkomponente?

Jede page.js Datei definiert den Inhalt einer Route.

Beispiel: app/page.js (Startseite)

jsx
// Server Component (Standard)
export default function HomePage() {
  return (
    <main>
      <h1>Willkommen auf meiner Next.js Seite</h1>
      <p>Dies ist die Startseite.</p>
    </main>
  );
}

📝 Seite mit Client-Interaktivität

Wenn Sie Hooks oder Event-Handler benötigen:

jsx
'use client'; // WICHTIG: Macht es zu einer Client Component

import { useState } from 'react';

export default function HomePage() {
  const [count, setCount] = useState(0);
  
  return (
    <main>
      <h1>Zähler: {count}</h1>
      <button onClick={() => setCount(count + 1)}>
        Erhöhen
      </button>
    </main>
  );
}

⚡ Server vs Client Components

AspektServer ComponentClient Component
DirectiveKeine'use client'
Hooks❌ Nicht verfügbar✅ Verfügbar
Event Handler❌ Nicht verfügbar✅ Verfügbar
Datenabfrage✅ Direkt (async/await)⚠️ Über API

5.2 Layout-Komponenten (layout.js)

🎯 Was ist ein Layout?

Ein Layout ist eine gemeinsame Struktur, die auf mehreren Seiten angezeigt wird (z.B. Header, Footer, Sidebar).

📝 Root Layout (app/layout.js)

Datei: app/layout.js

jsx
export const metadata = {
  title: 'Meine Next.js Seite',
  description: 'Eine tolle Webseite mit Next.js',
};

export default function RootLayout({ children }) {
  return (
    <html lang="de">
      <body>
        <header style={{ padding: '20px', background: '#f0f0f0' }}>
          <nav>
            <Link href="/">Start</Link> |{' '}
            <Link href="/about">Über uns</Link>
          </nav>
        </header>
        
        <main style={{ padding: '20px' }}>
          {children}
        </main>
        
        <footer style={{ padding: '20px', background: '#333', color: 'white' }}>
          <p>&copy; 2024 Meine Firma</p>
        </footer>
      </body>
    </html>
  );
}

🌐 Verschachtelte Layouts

Szenario: Dashboard mit eigenem Layout.

Datei: app/dashboard/layout.js

jsx
export default function DashboardLayout({ children }) {
  return (
    <div style={{ display: 'flex' }}>
      {/* Sidebar */}
      <aside style={{ width: '250px', background: '#f8f9fa', padding: '20px' }}>
        <h3>Dashboard</h3>
        <nav>
          <ul>
            <li><a href="/dashboard">Übersicht</a></li>
            <li><a href="/dashboard/settings">Einstellungen</a></li>
          </ul>
        </nav>
      </aside>
      
      {/* Inhalt */}
      <section style={{ flex: 1, padding: '20px' }}>
        {children}
      </section>
    </div>
  );
}

5.3 Komponentenentwicklung

🎯 Eigene Komponenten erstellen

Datei: components/Button.js

jsx
export default function Button({ children, onClick, variant = 'primary' }) {
  const styles = {
    primary: { background: 'blue', color: 'white' },
    secondary: { background: 'gray', color: 'white' },
  };
  
  return (
    <button
      onClick={onClick}
      style={{
        padding: '10px 20px',
        border: 'none',
        borderRadius: '5px',
        ...styles[variant],
      }}
    >
      {children}
    </button>
  );
}

📝 Verwendung der Komponente

In einer Seite:

jsx
import Button from '@/components/Button';

export default function HomePage() {
  return (
    <div>
      <Button onClick={() => alert('Geklickt!')}>
        Klick mich
      </Button>
      
      <Button variant="secondary" onClick={() => console.log('Secondary')}>
        Abbrechen
      </Button>
    </div>
  );
}

🔗 Props übergeben

Elternkomponente:

jsx
<Card 
  title="Mein Titel" 
  description="Beschreibung..." 
  onAction={() => console.log('Aktion!')} 
/>

Kindkomponente (Card.js):

jsx
export default function Card({ title, description, onAction }) {
  return (
    <div className="card">
      <h2>{title}</h2>
      <p>{description}</p>
      <button onClick={onAction}>Aktion</button>
    </div>
  );
}

5.4 Automatischer Import & globale Registrierung

🎯 Next.js Komponenten-Auto-Import

Next.js unterstützt keinen automatischen Import von Komponenten. Sie müssen sie manuell importieren:

jsx
// ❌ Funktioniert nicht (muss importiert werden)
<Button>Klick</Button>

// ✅ Korrekt
import Button from '@/components/Button';
<Button>Klick</Button>

📝 Import-Aliase konfigurieren

In jsconfig.json oder tsconfig.json:

json
{
  "compilerOptions": {
    "baseUrl": ".",
    "paths": {
      "@/*": ["./*"]
    }
  }
}

Verwendung:

jsx
import Button from '@/components/Button'; // ✅ Alias
import Button from '../../components/Button'; // ❌ Relativer Pfad (unschön)

5.5 Statische Assets verarbeiten

🖼️ Bilder (Image-Komponente)

Import: next/image

Beispiel:

jsx
import Image from 'next/image';
import logo from '@/public/logo.png';

export default function HomePage() {
  return (
    <div>
      {/* Lokales Bild */}
      <Image 
        src={logo} 
        alt="Logo" 
        width={200} 
        height={100} 
      />
      
      {/* Remote Bild */}
      <Image 
        src="https://example.com/bild.jpg" 
        alt="Remote Bild" 
        width={500} 
        height={300} 
      />
    </div>
  );
}

⚙️ next.config.js für Bilder

javascript
/** @type {import('next').NextConfig} */
const nextConfig = {
  images: {
    domains: ['example.com', 'via.placeholder.com'],
  },
};

module.exports = nextConfig;

🔤 Fonts (next/font)

Google Fonts verwenden:

jsx
import { Inter } from 'next/font/google';

const inter = Inter({ subsets: ['latin'] });

export default function RootLayout({ children }) {
  return (
    <html lang="de" className={inter.className}>
      <body>{children}</body>
    </html>
  );
}

Lokale Fonts:

javascript
import localFont from 'next/font/local';

const myFont = localFont({ src: './my-font.woff2' });

5.6 Metadaten konfigurieren (SEO)

🎯 Metadata-Objekt (Static)

In layout.js oder page.js:

jsx
export const metadata = {
  title: 'Meine Next.js Seite',
  description: 'Eine tolle Webseite mit Next.js 14',
  keywords: ['Next.js', 'React', 'Webentwicklung'],
};

export default function RootLayout({ children }) {
  return (
    <html lang="de">
      <body>{children}</body>
    </html>
  );
}

🎯 Generate Metadata (Dynamic)

Für dynamische Seiten (z.B. Blog-Artikel):

jsx
import { generateMetadata } from 'next/dist/lib/metadata';

export async function generateMetadata({ params }) {
  const post = await getBlogPost(params.slug);
  
  return {
    title: post.title,
    description: post.excerpt,
    openGraph: {
      images: [post.coverImage],
    },
  };
}

export default function BlogPost({ params }) {
  // ...
}

📝 SEO-Optimierung

Meta-TagZweckBeispiel
titleBrowser-Tab Titel"Meine Seite - Home"
descriptionSuchergebnis-Beschreibung"Die beste Seite für..."
keywordsSuchbegriffe["Next.js", "Tutorial"]
openGraphSocial Media VorschauBild, Titel, Beschreibung

📝 Zusammenfassung

In diesem Kapitel haben Sie gelernt:

KonzeptErklärung
Seitenkomponentenpage.js für Routen-Inhalt
Layoutslayout.js für gemeinsame Struktur
KomponentenWiederverwendbare UI-Elemente
Statische AssetsBilder (next/image), Fonts (next/font)
MetadatenSEO-Optimierung (metadata Objekt)

✅ Nächste Schritte

  1. Übung: Erstellen Sie eine wiederverwendbare Button-Komponente
  2. Übung: Konfigurieren Sie Metadaten für besseres SEO
  3. Weiter geht's: Kapitel 6 - Server & Client Components

🎯 Selbsttest

Frage 1: Welche Datei definiert den Inhalt einer Route?

Antwort anzeigen `page.js`

Frage 2: Wie aktiviert man Client-Side-Funktionalität in einer Komponente?

Antwort anzeigen Durch Hinzufügen der Directive `'use client';` am Anfang der Datei.

Frage 3: Welche Komponente wird für optimierte Bilder verwendet?

Antwort anzeigen `next/image` (Image-Komponente)

🚀 Weiter zu Kapitel 6: Server & Client Components

Frei für alle Anfänger