Loading...
Next.js 14 - header footer contentful tailwindcss - full example - part3
Author Cloudapp
E.G.

Next.js 14 - Komplettes Beispiel - Header / Footer / Tailwindcss / Contentful - Teil 3

18. März 2024
Inhaltsverzeichnis

In Teil 1 haben wir das Fundament unseres Next.js 14 Projekts erstellt. In Teil 2 haben wir das Frontend mit Contentful als Headless CMS, TailwindCSS für das Styling und TypeScript für die Programmierung erstellt. Jetzt haben wir Teil 3 erreicht, und wir werden eine Kopf- und Fußzeilenkomponente hinzufügen, sowie die sogenannte Entwurfs-/Vorschaufunktionalität, die von Contentful angeboten wird. Nicht zu vergessen, der heutzutage obligatorische Umschalter für den Dunkelmodus. Hier ist bereits ein Spoiler zum Endergebnis-> Create Cloudapp.dev Example App

Neue Contentful Inhaltsklassen (Content Types)

Wir beginnen mit der Erstellung unserer neuen Inhaltstypen: Nav Item NavitemGroup FooterItemGroup

Contentful Contenttypes
Contentful Contenttypes

Da wir neue Inhaltsarten haben, müssen wir die entsprechenden GraphQL-Dateien unter src/lib/graphql erstellen.

New Graphql Files Contentful Part3
New Graphql Files Contentful Part3

Sobald die neuen GraphQL-Dateien bereitstehen, können wir den Synchronisationsbefehl starten.

npm run graphql-codegen:generate

Welcher drei Dateien im Ordner src/lib/__generated aktualisiert

sdk.ts graphql.schema.json graphql.schema.graphql

Werfen wir einen Blick auf navItemFields.graphql. Wie Sie im folgenden Screenshot sehen können, haben wir zwei Felder im Inhalts-Typ „Nav Item“, und diese beiden Felder sind in der GraphQL-Datei dargestellt.

fragment NavItemFields on NavItem {
  name
  href
}

Wir verwenden das Fragment in mehreren Dateien (navitemgroup.graphql)

fragment NavItemGroupFields on NavItemGroup {
  __typename
  sys {
    id
    spaceId
  }
  name
  navItemsCollection {
    __typename
    items {
      ...NavItemFields
    }
  }
}
query navItemGroup($locale: String, $preview: Boolean) {
  navItemGroupCollection(
    limit: 1
    locale: $locale
    preview: $preview
    where: { mainNav: "Yes" }
  ) {
    items {
      ...NavItemGroupFields
    }
  }
}

Videotutorial für Erstellung FooterItemGroup Inhaltstyp

Neue NPM Pakete

Jetzt sind wir bereit für die Installation von drei neuen NPM-Paketen

npm install @headlessui/react @heroicons/react next-themes

Jetzt können wir die neuen Komponenten hinzufügen

New Components Part3
New Components Part3
import Navbar from "@/components/header/navbar.component";
export default function Header({ menuItems }: any) {
  return (
    <header>
      {/* <DarkModeButton /> */}
      {/* <NavbarBanner /> */}
      <Navbar menuItems={menuItems} />
    </header>
  );
}

In der header.component.tsx erhalten wir die "menuItems" als Eigenschaften von layout.tsx und senden sie dann an die Komponente "Navbar".

Layout.tsx im Detail

Schauen wir uns die Date layout.tsx (src/app/layout.tsx) etwas genauer an

import type { Metadata } from "next";
import { Urbanist } from "next/font/google";
import { draftMode } from "next/headers";
import "./globals.css";
import Header from "@/components/header/header.component";
import Footer from "@/components/footer/footer.component";
import { Providers } from "@/components/header/providers";
import getAllNavitemsForHome from "@/components/header/navbar.menuitems.component";
import getAllFooteritemsForHome from "@/components/footer/footer.menuitems.component";
import ExitDraftModeLink from "@/components/header/draftmode/ExitDraftModeLink.component";

const urbanist = Urbanist({ subsets: ["latin"], variable: "--font-urbanist" });

export const metadata: Metadata = {
  title: "Create Cloudapp.dev Example App",
  description: "Generated by create next app",
};

export default async function RootLayout({
  children,
}: {
  children: React.ReactNode;
}) {
  const headerdata = await getAllNavitemsForHome();
  const footerdata = await getAllFooteritemsForHome();

  return (
    <html lang="en" suppressHydrationWarning>
      <body>
        <main className={`${urbanist.variable} font-sans dark:bg-gray-900`}>
          <Providers>
            <Header menuItems={headerdata} />
            {draftMode().isEnabled && (
              <p className="bg-emerald-400 py-4 px-[6vw]">
                Draft mode is on! <ExitDraftModeLink className="underline" />
              </p>
            )}
            {children}
            <Footer footerItems={footerdata} />
          </Providers>
        </main>
      </body>
    </html>
  );
}  

Am Dateinanfang importieren wir diese neuen Komponenten

import Header from "@/components/header/header.component";
import Footer from "@/components/footer/footer.component";
import { Providers } from "@/components/header/providers";
import getAllNavitemsForHome from "@/components/header/navbar.menuitems.component";
import getAllFooteritemsForHome from "@/components/footer/footer.menuitems.component";

und die Komponente "Providers" für die Handhabung des Dunkelmodus.

"use client";
import { ThemeProvider } from "next-themes";
export function Providers({ children }: { children: React.ReactNode }) {
  return (
    <ThemeProvider enableSystem={true} attribute="class">
      {children}
    </ThemeProvider>
  );
}

Im Folgenden weise ich auf den Rückgabeteil von src/app/layout.tsx hin, wo wir sehen können, dass wir den Header, Footer und {children} in die Komponente <Providers></Providers> eingewickelt haben, was es uns ermöglicht, den Umschalter für den Dunkelmodus auf jeder Seite zu verwenden.

return (
    <html lang="en" suppressHydrationWarning>
      <body>
        <main className={`${urbanist.variable} font-sans dark:bg-gray-900`}>
          <Providers>
            <Header menuItems={headerdata} />
            {children}
            <Footer footerItems={footerdata} />
          </Providers>
        </main>
      </body>
    </html>
  );

Unter {Providers} importieren wir die Daten für Header/Footer.

import getAllNavitemsForHome from "@/components/header/navbar.menuitems.component";
import getAllFooteritemsForHome from "@/components/footer/footer.menuitems.component";

getAllNavitemsForHome im Detail

import { client } from "@/lib/client";
import { draftMode } from "next/headers";
async function getMenuItems() {
  const { isEnabled } = draftMode();
  const entries = await client.navItemGroup({
    preview: isEnabled,
  });
  return extractNavItemEntries(entries) || [];
}
function extractNavItemEntries(fetchResponse: any) {
  return fetchResponse?.navItemGroupCollection?.items?.[0].navItemsCollection
    ?.items;
}
export default async function getAllNavitemsForHome() {
  const navitems = await getMenuItems();
  return navitems || [];
}

welche die Daten über eine GraphQL-Abfrage von Contentful abruft. client.navItemGroup verwendet die in src/lib/graphql/navitemgroup.graphql spezifizierte GraphQL-Abfrage.

fragment NavItemGroupFields on NavItemGroup {
  __typename
  sys {
    id
    spaceId
  }
  name
  navItemsCollection {
    __typename
    items {
      ...NavItemFields
    }
  }
}
query navItemGroup($locale: String, $preview: Boolean) {
  navItemGroupCollection(
    limit: 1
    locale: $locale
    preview: $preview
    where: { mainNav: "Yes" }
  ) {
    items {
      ...NavItemGroupFields
    }
  }
}

Da wir dieselben "Navitems" im Header und im Footer wiederverwenden, haben wir sie in GraphQL nur einmal definiert.

Auflistung aller geänderten & hinzugefügten Daten in Part 3

Hier ist eine vollständige Liste aller geänderten/hinzugefügten Dateien in Teil 3 des Tutorials. Alle Dateien im Ordner src/lib/__generated werden durch den Befehl „npm run graphql-codegen:generate“ generiert, der zu Beginn erwähnt wurde.

Changed Added Files Part3
Changed Added Files Part3

Änderungen in der Datei tailwind.config.ts

Die tailwind.config.ts wurde ebenfalls geringfügig modifiziert. Wir haben Zeile 4 und Zeile 27 hinzugefügt

# Line 4
const extraColors = require("tailwindcss/colors"); //for extra colors e.g. in the Arctile TOC -> emerald
# Line 27
colors: { emerald: extraColors.emerald },
import type { Config } from "tailwindcss";
import tokens from "@contentful/f36-tokens";
const { fontFamily } = require("tailwindcss/defaultTheme");
const extraColors = require("tailwindcss/colors"); //for extra colors e.g. in the Arctile TOC -> emerald

const colors = Object.entries(tokens).reduce(
  (acc: Record<string, any>, [key, value]) => {
    // Filter Hex colors from the f36-tokens
    if (/^#[0-9A-F]{6}$/i.test(value as any)) {
      acc[key] = value;
    }

    return acc;
  },
  {} as Record<string, string>
);

const config: Config = {
  darkMode: "class",
  content: [
    "./src/components/**/*.{js,ts,jsx,tsx,mdx}",
    "./src/app/**/*.{js,ts,jsx,tsx,mdx}",
  ],
  theme: {
    colors,
    extend: {
      colors: { emerald: extraColors.emerald },
      maxWidth: {
        "8xl": "90rem",
      },
      letterSpacing: {
        snug: "-0.011em",
      },
      boxShadow: {
        // light
        "tremor-input": "0 1px 2px 0 rgb(0 0 0 / 0.05)",
        "tremor-card":
          "0 1px 3px 0 rgb(0 0 0 / 0.1), 0 1px 2px -1px rgb(0 0 0 / 0.1)",
        "tremor-dropdown":
          "0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1)",
      },
      borderRadius: {
        "tremor-small": "0.375rem",
        "tremor-default": "0.5rem",
        "tremor-full": "9999px",
      },
      fontSize: {
        "2xs": "0.625rem",
        "3xl": "1.75rem",
        "4xl": "2.5rem",
        "tremor-label": "0.75rem",
        "tremor-default": ["1.0rem", { lineHeight: "1.25rem" }],
        "tremor-title": ["1.125rem", { lineHeight: "1.75rem" }],
        "tremor-metric": ["1.875rem", { lineHeight: "2.25rem" }],
      },
      lineHeight: {
        tighter: "1.1",
      },
      fontFamily: {
        sans: ["var(--font-urbanist)", ...fontFamily.sans],
      },
    },
  },
  plugins: [require("@tailwindcss/typography")],
};
export default config;

Mit dieser Änderung haben wir die zusätzliche Farbe „Emerald“ hinzugefügt, die wir in Zeile 34 (layout.tsx) innerhalb unseres Entwurfsmodus-Banners verwendet haben.

Aktivierung Vorschau - Entwurfmodus für Contentful

{draftMode().isEnabled && (
   <p className="bg-emerald-400 py-4 px-[6vw]">
     Draft mode is on! <ExitDraftModeLink className="underline" />
   </p>
 )}
DraftMode Banner Header
DraftMode Banner Header

Um den Entwurfs-/Vorschaumodus zu aktivieren, haben wir diese Seiten und Komponenten modifiziert.

Draftmode Files
Draftmode Files

sowie diese zwei Route

DraftMode Apis
DraftMode Apis

Ich habe diesen Import hinzugefügt

import { draftMode } from "next/headers";

und diese Konstante, und ich habe die Konstante für die GraphQL-Abfrage wiederverwendet (Beispiel zeigt ein Code-Stück von src/app/page.tsx)

 
sync function BlogPostPage() {
  const { isEnabled } = draftMode();

  const [blogPagedata] = await Promise.all([
    client.pageBlogPost({
      slug: "testblogpost",
      preview: isEnabled,
    }),
  ]);

oben habe ich „client.pageBlogPost“ mit zwei Parametern „slug“ und „preview“ verwendet. „Slug“ für die Filterung („Slug“ ist in Contentful als einzigartiges Feld definiert), während „preview“ verwendet wird, um die Daten für Inhaltsdatensätze im „Entwurf“-Status zu erhalten, die wir für die Vorschau benötigen.

query pageBlogPost($slug: String!, $locale: String, $preview: Boolean) {
  pageBlogPostCollection(
    limit: 1
    where: { slug: $slug }
    locale: $locale
    preview: $preview
  ) {
    items {
      ...PageBlogPostFields
    }
  }
}

Konfiguration Inhaltsvorschau in Contentful

Als letzten Schritt haben wir die „Inhaltsvorschau“ innerhalb von Contentful eingerichtet.

Content Preview Menu
Content Preview Menu
Content Preview Menu 2
Content Preview Menu 2

Wir definieren drei Felder

  1. Name -> Ich habe “Preview” verwendet

  2. Bei Inhaltstyp -> “page — Blog post”

  3. Url -> https://nextjs14-full-example-header-footer.vercel.app/api/draft?previewSecret=xxxxxxxxxxxxxx&redirect=/{entry.fields.slug} (wir verwenden den Wert der entsprechenden Umgebungsvariablen -> CONTENTFUL_PREVIEW_SECRET)

Content Preview Menu 3
Content Preview Menu 3

Lassen Sie uns jetzt „TestBlogPost3“ in Contentful öffnen und auf den Button „Live-Vorschau öffnen“ auf der rechten Seite klicken.

Content Preview Menu 4
Content Preview Menu 4

Und hier ist die Vorschau (um sie zu testen, ändern Sie das Titelfeld auf der linken Seite (1) und klicken Sie dann auf Aktualisieren (2) und Sie werden das Banner des Entwurfsmodus und die Änderung sehen (3))

Content Preview Menu 5
Content Preview Menu 5

GitHub Repo

Hier der Link zum GitHub Repo -> GitHub - cloudapp-dev/nextjs14-typescript-app-router-contentful at nextjs14-part3

dort liegt auch die aktualiserte Version der Datei export.json (Ordner "contentfulsync"), welche wir für den Contentfulimport nutzen können.

npm run cf-import

Bleiben Sie dran für die nächsten Beiträge, in denen wir neue Funktionen hinzufügen werden, wie eine Azure Entra ID (früher Azure AD B2C) für das Identitätsmanagement, Algolia als On-Site-Suchmaschine und vieles mehr.

Wenn Ihnen gefällt, was Sie sehen, dann unterstützen Sie mich bitte mit einem "Clap" oder folgen Sie mir auf medium.com.

Verwandte Artikel