Zum Inhalt springen
    WingoUI
    KomponentenVorlagenDokuPreiseKI-AgentenBlogChangelog
    1. Startseite
    2. Blog
    3. Praxis: Screens und Apps
    4. Shadcn Login Page: Magic Link, Codes, 2FA und Reset
    Read in English
    1. Blog
    2. Praxis: Screens und Apps
    3. Shadcn Login Page: Magic Link, Codes, 2FA und Reset
    Praxis: Screens und Apps
    Praxis: Screens und Apps

    Shadcn Login Page: Magic Link, Codes, 2FA und Reset

    SR
    Serban Rusu · Gründer von Wingo UI
    16. Okt. 2026 · 12 Min. Lesezeit

    Auf dieser Seite

    0%
    1. Was braucht eine Login-Seite für die Produktion?
    2. Wie installierst du den Login-Page-Block?
    3. Wie verbindest du den Block mit Next.js Server Actions?
      1. Die Server Actions
      2. Der Client-Screen
    4. Welche Fehlerzustände muss eine Login-Seite abdecken?
    5. Magic Link oder Einmalcode: Was ist besser für passwortlosen Login?
    6. Wie baust du „Passwort vergessen“ und den Passwort-Reset?
    7. Wie passt du Layout und Texte der Login-Seite an?
    8. Was ist der nächste Schritt?
    9. Häufige Fragen
      1. Hat shadcn/ui eine Login-Seite?
      2. Soll ein Login-Fehler verraten, ob die E-Mail existiert?
      3. Magic Link oder Einmalcode: Was ist besser für passwortlosen Login?
      4. Wie zeige ich Fehler aus Server Actions in einem Client-Login-Formular an?
      5. Ist die Login-Seite von Wingo UI kostenlos?

    Kurz gesagt

    Eine produktionsreife shadcn login page braucht acht Ansichten (Anmelden, Link gesendet, E-Mail-Code, Zwei-Faktor-Code, Passwort vergessen, Reset-Link gesendet, neues Passwort, fertig) und für jede einen Fehlerzustand. Die fünf offiziellen Login-Blöcke von shadcn/ui sind Stand Oktober 2026 statische Formulare ohne Submit-Handler, diese Logik schreibst du also selbst. Dieses Tutorial verbindet den Block Login Page von Wingo UI, der alle acht Ansichten samt Fehlern mitbringt, mit Next.js Server Actions, die Fehler als Daten zurückgeben.

    Veröffentlicht am 16. Okt. 2026

    Eine shadcn login page fängt meist als Block an, den du an einem Nachmittag kopierst, und endet als Woche voller Sonderfälle. Das Formular schickt ab, dann vertippt sich jemand zweimal beim Passwort, fordert einen Reset-Link an, öffnet ihn auf dem Handy oder meldet sich mit Google an und sieht einen Spinner, der sich endlos dreht. shadcn/ui liefert fünf Login-Blöcke (login-01 bis login-05), aber das sind Layouts. Das Formular in login-03 hat zum Beispiel kein onSubmit, keine Fehlermeldung und einen Link „Forgot your password?“, der auf # zeigt (geprüft am 16. Oktober 2026 in den Login-Blöcken von shadcn/ui (opens in a new tab)). Galerien wie shadcnblocks (opens in a new tab) und Tailark (opens in a new tab) bieten weitere Layouts, und die Anbindung an dein Auth-Backend bleibt trotzdem deine Aufgabe.

    In diesem Tutorial baust du eine komplette Login-Seite für Next.js mit dem Block Login Page von Wingo UI, der Bibliothek, die wir bauen. Wir sind also nicht neutral. Es geht um Anmeldung mit Passwort, Magic Links, E-Mail-Codes, Social Login, Zwei-Faktor-Authentifizierung und Passwort-Wiederherstellung, dazu die Fehlerzustände, die die meisten Vorlagen weglassen.

    Was braucht eine Login-Seite für die Produktion?

    Acht Screens, auch wenn eine Person meist nur zwei davon zu sehen bekommt:

    1. Anmelden. E-Mail, Passwort, „Angemeldet bleiben“, ein Link für ein vergessenes Passwort, Social Buttons und ein Wechsel zu einer passwortlosen Methode.
    2. Schau in dein Postfach. Nach einem Magic Link: die Adresse, ein Button zum erneuten Senden, der wartet, und ein Weg zu einer anderen Adresse.
    3. Code eingeben. Ein 6-stelliger Code per E-Mail für die passwortlose Anmeldung.
    4. Zwei-Faktor-Bestätigung. Der Code aus der Authenticator-App nach einem richtigen Passwort, mit Backup-Code als Ausweg.
    5. Passwort vergessen. Ein E-Mail-Feld.
    6. Reset-Link gesendet. Eine Meldung, die nie bestätigt, ob das Konto existiert.
    7. Neues Passwort festlegen. Regeln für die Passwortstärke und ein Feld zur Bestätigung.
    8. Passwort geändert. Ein klarer Weg zurück zur Anmeldung.

    Der Block Login Page rendert alle acht als Ansichten einer einzigen Komponente (sign-in, magic-link-sent, otp, two-factor, forgot, forgot-sent, reset, reset-done). Jede Ansicht gleitet von der Seite herein, zu der sie gehört, der Fokus springt auf ihren Titel, und Screenreader sagen den Wechsel an. Auf Smartphones füllt das Formular den Bildschirm mit 48px hohen Feldern, und der Hero-Bereich fällt weg.

    Melde dich mit einer beliebigen E-Mail und einem Passwort unter 8 Zeichen an, dann siehst du den Fehler-Alert. Drück danach auf Forgot password: Die eingegebene E-Mail wird mitgenommen
    $ npx wingo-ui@latest add login-page
    ProDoku: Login Page

    Wie installierst du den Login-Page-Block?

    Ein CLI-Befehl kopiert den Block und alles, was er importiert, in dein Projekt:

    bash
    npx wingo-ui@latest add login-page

    Das CLI kopiert components/blocks/login-page.tsx, seinen Layout-Rahmen und seine Beispieldaten als Quellcode in dein Projekt, dazu die Teile, die er importiert: Password Input für den Umschalter zum Einblenden des Passworts und die Warnung bei aktiver Feststelltaste, OTP Input für die Code-Felder, Input, Button, Alert, Checkbox, Toast, den Hook use-cooldown und die Helfer für die Formularvalidierung. Außerdem installiert es motion, lucide-react, react-hook-form und zod. Die Datei beginnt mit "use client". Eine Server Component kann sie rendern, aber die Props, die du in der Produktion brauchst (onSuccess mit dem Router, linkComponent={Link}, Handler, die zurückgegebene Fehler in Ablehnungen verwandeln), sind Funktionen, die im Browser definiert werden. In der Praxis packst du den Block deshalb in eine kleine Client-Komponente.

    Anders als eine statische Tailwind Login Page hält der Block Zustand, Validierung und Ansichtswechsel in der Datei selbst, und jede Klasse nutzt die Theme-Tokens (bg-background, text-muted-foreground, border-border). So passt er sich ohne Änderungen an dein helles und dunkles Theme an. Weil er Quellcode ist, kannst du trotzdem jede Zeile anpassen.

    Wie verbindest du den Block mit Next.js Server Actions?

    Jeder Handler (onSignIn, onMagicLink, onSendCode, onVerifyCode, onForgot, onReset, onProvider) darf ein Promise zurückgeben. Solange es aussteht, zeigt der Button einen Spinner. Wird es abgelehnt, landet die Meldung des Fehlers im roten Alert über dem Formular. Wird es erfüllt, wechselt der Block zur nächsten Ansicht oder ruft onSuccess auf.

    Der Authentifizierungs-Guide von Next.js (opens in a new tab) nutzt Server Actions für die Anmeldelogik, weil sie immer auf dem Server laufen. Der Guide zur Fehlerbehandlung (opens in a new tab) rät, erwartbare Fehler wie ein falsches Passwort als Rückgabewerte zu modellieren, statt sie zu werfen (beide geprüft am 16. Oktober 2026). Dazu kommt ein praktischer Grund: In einem Produktions-Build ersetzt Next.js die Meldung eines Fehlers, der vom Server weitergereicht wird, durch eine allgemeine Meldung plus eine digest-ID, damit keine sensiblen Details nach außen dringen (Referenz zu error.js (opens in a new tab)). Ein „Falscher Code“, der in der Action geworfen wird, käme im Alert nur als Rauschen an. Die Action gibt ihren Fehler also als Daten zurück, und der Client macht daraus eine Ablehnung für den Block.

    Die Server Actions

    ts
    // app/(auth)/actions.ts
    "use server";
    import type { SignInValues, VerifyCodeContext } from "@/components/blocks/login-page";
    // ein dünner Wrapper, den du um Better Auth, Auth.js oder deinen eigenen Session-Code schreibst
    import { auth } from "@/lib/auth";
    export type ActionResult = { error?: string; twoFactor?: boolean };
    export async function signIn(values: SignInValues): Promise<ActionResult> {
    const result = await auth.signInWithPassword(values);
    if (!result.ok) return { error: "E-Mail oder Passwort ist nicht korrekt" };
    return { twoFactor: result.needsSecondFactor };
    }
    export async function sendMagicLink(email: string): Promise<ActionResult> {
    await auth.sendMagicLink(email);
    return {};
    }
    export async function sendCode(email: string): Promise<ActionResult> {
    await auth.sendEmailCode(email);
    return {};
    }
    export async function verifyCode(code: string, context: VerifyCodeContext): Promise<ActionResult> {
    // two-factor prüft die offene Anmeldung, die signIn gestartet hat; otp prüft den per E-Mail geschickten Code
    const result = await auth.verifyCode({ code, kind: context.view, backup: context.backup });
    if (!result.ok) return { error: `Falscher Code. Noch ${result.attemptsLeft} Versuche.` };
    return {};
    }
    export async function requestReset(email: string): Promise<ActionResult> {
    // dieselbe Antwort, egal ob das Konto existiert
    await auth.requestPasswordReset(email);
    return {};
    }
    export async function resetPassword(token: string, password: string): Promise<ActionResult> {
    const result = await auth.resetPassword({ token, password });
    if (!result.ok) return { error: "Dieser Reset-Link ist abgelaufen. Fordere einen neuen an." };
    return {};
    }

    Das Objekt auth steht für die Bibliothek, die du nutzt. Dem Block ist egal, wie die Session entsteht. Ihn interessiert nur, ob das Promise erfüllt oder abgelehnt wird.

    Der Client-Screen

    tsx
    // app/(auth)/login/login-screen.tsx
    "use client";
    import { useRef, useState } from "react";
    import Link from "next/link";
    import { useRouter } from "next/navigation";
    import { AUTH_PROVIDERS, LoginPage, type LoginView } from "@/components/blocks/login-page";
    import { requestReset, sendCode, sendMagicLink, signIn, verifyCode, type ActionResult } from "../actions";
    // ein zurückgegebener Fehler wird zur Ablehnung, also zeigt der Block ihn in seinem Alert
    function check(result: ActionResult) {
    if (result.error) throw new Error(result.error);
    return result;
    }
    export function LoginScreen() {
    const router = useRouter();
    const [view, setView] = useState<LoginView>("sign-in");
    // von signIn gesetzt, wenn dieses Konto die Zwei-Faktor-Bestätigung aktiviert hat
    const needsCode = useRef(false);
    return (
    <LoginPage
    view={view}
    onViewChange={setView}
    methods={["password", "magic-link", "otp"]}
    providers={AUTH_PROVIDERS.filter((provider) => provider.id !== "microsoft")}
    linkComponent={Link}
    registerHref="/signup"
    onSignIn={async (values) => {
    needsCode.current = check(await signIn(values)).twoFactor === true;
    }}
    onMagicLink={async (email) => check(await sendMagicLink(email))}
    onSendCode={async (email) => check(await sendCode(email))}
    onVerifyCode={async (code, context) => check(await verifyCode(code, context))}
    onForgot={async (email) => check(await requestReset(email))}
    onProvider={(id) => {
    window.location.assign(`/api/auth/${id}`);
    // wird nie abgeschlossen: Der Spinner dreht sich weiter, während der Browser die Seite verlässt
    return new Promise(() => {});
    }}
    onSuccess={() => {
    if (needsCode.current) {
    needsCode.current = false;
    setView("two-factor");
    return;
    }
    router.push("/dashboard");
    }}
    />
    );
    }
    tsx
    // app/(auth)/login/page.tsx
    import { LoginScreen } from "./login-screen";
    export default function Page() {
    return <LoginScreen />;
    }

    Diese Server-Datei ist die ganze Route der Next.js Login Page, alles Interaktive steckt im Client-Screen. Drei Entscheidungen in diesem Code verdienen eine Erklärung.

    Zwei-Faktor nur für Konten, die sie aktiviert haben. Die Prop twoFactor ist ein einfacher Boolean, der jede erfolgreiche Anmeldung mit Passwort zur Code-Ansicht schickt. Die meisten Apps wissen das aber erst nach der Passwortprüfung. Darum steuert der Screen view selbst und wechselt in onSuccess zu two-factor, wenn der Server das gemeldet hat. Ist der Code bestätigt, läuft onSuccess noch einmal, und der Router leitet weiter.

    Die Reihenfolge der Methoden. methods={["password", "magic-link", "otp"]} zeigt zuerst das Passwortformular, darunter „Email me a sign-in link“ und „Email me a code“. Setzt du "otp" an den Anfang, startet die Seite als passwortloser Login. Mit methods={[]} und Providern bleiben nur die Provider-Buttons übrig. Das passt zu einer Firma, die Single Sign-on nutzt.

    Echte Links. Mit linkComponent={Link} nutzen der Link „Create one“ und die Links im Footer next/link. „Create one“ führt zu registerHref, wo der Block Register Page die Registrierung im selben Layout übernimmt.

    Welche Fehlerzustände muss eine Login-Seite abdecken?

    Mindestens neun, vom falschen Passwort bis zum gesperrten Code-Feld. Genau diesen Teil lässt ein statischer shadcn login block weg. So reagiert die Login Page auf jeden Fehler, mit den englischen Standardtexten.

    SituationWas die Person sieht
    Falsche E-Mail oder falsches PasswortRoter Alert über dem Formular, die E-Mail bleibt, der Fokus springt zurück aufs Passwort; beim zweiten Fehlschlag wird auch das Passwort geleert
    Feststelltaste beim Tippen des Passworts aktivEine bernsteinfarbene Warnung „Caps Lock is on“ unter dem Feld
    Netzwerk- oder unbekannter Fehler„Something went wrong. Try again.“, wenn der Fehler keine Meldung hat
    Anmeldung beim Provider scheitertDer Spinner auf dem gedrückten Button stoppt, und der Alert zeigt die Meldung
    Falscher CodeDie Felder schütteln sich, und der Alert zeigt deine Meldung, etwa „Falscher Code. Noch 2 Versuche.“
    Drei falsche CodesDas Feld sperrt sich für 60 Sekunden und zählt live herunter: „Too many attempts. Try again in 0:59.“
    „Erneut senden“ zu früh gedrücktDer Button wartet mit „Resend in 0:24“, und die Wartezeit übersteht ein Neuladen
    Passwort vergessen für eine unbekannte E-MailDieselbe Meldung „If there is an account for...“ wie für ein echtes Konto
    Schwaches oder abweichendes neues PasswortStärkeanzeige, Checkliste der Regeln und eine Bestätigung, die übereinstimmen muss

    Zwei davon folgen direkt dem OWASP Authentication Cheat Sheet (opens in a new tab): Fehlgeschlagene Anmeldungen bekommen eine einzige allgemeine Meldung, die nie verrät, welcher Teil falsch war, und das Forgot Password Cheat Sheet (opens in a new tab) verlangt eine einheitliche Meldung für existierende und nicht existierende Konten. Halte dieses Versprechen auch auf dem Server: gleiche Antwort, ähnliches Timing.

    Die Sperre für 60 Sekunden und die Wartezeit beim erneuten Senden sind reine Höflichkeit auf dem Client. Sie halten eine Person davon ab, auf den Button einzuhämmern, und helfen nicht gegen ein Skript, das deinen Endpoint aufruft. Setz auf dem Server ein Rate Limit pro Konto für Codes, Anmeldungen und Reset-Anfragen und schick die verbleibenden Versuche in der Fehlermeldung zurück, damit der Alert die Wahrheit sagt.

    Tipp 123456 ein, um durchzukommen, oder dreimal einen falschen Code für das Schütteln, die verbleibenden Versuche und die Sperre für eine Minute. Wechsle danach zu einem Backup-Code
    $ npx wingo-ui@latest add login-page
    ProDoku: Login Page

    Magic Link oder Einmalcode: Was ist besser für passwortlosen Login?

    Biete einen Code an, wenn Leute das Gerät wechseln oder hinter einem Mailfilter in der Firma sitzen, und einen Link, wenn sie sich meist auf dem Gerät anmelden, auf dem sie auch ihre E-Mails lesen. Beide schicken etwas ins Postfach, beide sind also genauso sicher wie das E-Mail-Konto. Der Unterschied liegt darin, was die Person als Nächstes tut.

    Ein Magic Link ist ein Klick, wenn die E-Mail auf demselben Gerät aufgeht. In zwei häufigen Fällen scheitert er: Die Person startet am Laptop und öffnet die E-Mail auf dem Handy, oder ein Mail-Scanner der Firma öffnet den Link zuerst. Scalekit weist in seinem Vergleich von OTPs und Magic Links (opens in a new tab) darauf hin, dass Scanner einen einmaligen Link verbrauchen können, bevor die Person klickt. Ein defensives Muster ist ein Link, der eine Seite mit einem Button „Weiter“ öffnet, und erst der POST dieses Buttons verbraucht das Token.

    Ein Code hält die Person in dem Tab, in dem sie angefangen hat, und funktioniert über Geräte hinweg. Der OTP Input im Block setzt autocomplete="one-time-code", so können iOS und Android den Code aus der Nachricht anbieten, und das Einfügen von „123 456“ füllt alle sechs Felder. Mit der sechsten Ziffer geht der Code automatisch raus.

    Ist eine der beiden E-Mails verschickt, zeigt die Ansicht „Check your email“ die Adresse fett, einen Button „Open Gmail“ oder „Open Outlook“, wenn die Domain passt, einen Hinweis auf den Spam-Ordner und den Button zum erneuten Senden. Die Wartezeit kommt aus resendSeconds (standardmäßig 30) und liegt im Local Storage, ein Neuladen setzt sie also nicht zurück.

    Wie baust du „Passwort vergessen“ und den Passwort-Reset?

    Die Ansicht für ein vergessenes Passwort übernimmt die E-Mail, die im Anmeldeformular eingetippt wurde, so tippt niemand sie zweimal. onForgot verschickt den Link, und sobald das Promise erfüllt ist, wechselt der Block zur Ansicht „forgot-sent“.

    Der Reset-Link öffnet eine eigene Route. Lies das Token auf dem Server und gib es an einen kleinen Client-Screen weiter, der mit der Ansicht reset startet:

    tsx
    // app/(auth)/reset-password/page.tsx
    import { redirect } from "next/navigation";
    import { auth } from "@/lib/auth";
    import { ResetScreen } from "./reset-screen";
    export default async function Page({ searchParams }: { searchParams: Promise<{ token?: string | string[] }> }) {
    const { token } = await searchParams;
    // der Block hat keine Ansicht „Link abgelaufen“, also geht ein ungültiges Token zurück zur Anmeldung
    if (typeof token !== "string" || !(await auth.isResetTokenValid(token))) redirect("/login");
    return <ResetScreen token={token} />;
    }
    tsx
    // app/(auth)/reset-password/reset-screen.tsx
    "use client";
    import { useRouter } from "next/navigation";
    import { LoginPage } from "@/components/blocks/login-page";
    import { resetPassword } from "../actions";
    export function ResetScreen({ token }: { token: string }) {
    const router = useRouter();
    return (
    <LoginPage
    defaultView="reset"
    passwordMinLength={12}
    onReset={async (password) => {
    const result = await resetPassword(token, password);
    if (result.error) throw new Error(result.error);
    }}
    onSuccess={() => router.push("/login")}
    />
    );
    }

    resetPassword ist die letzte Server Action in der Datei mit den Actions oben. Die Seite prüft das Token, bevor sie irgendetwas rendert, weil der Block keine Ansicht für einen abgelaufenen Link hat. Läuft ein Token ab, während die Person tippt, scheitert das trotzdem sicher: Die Action gibt den Fehler zurück, und der Alert zeigt ihn. Die Reset-Ansicht zeigt eine Stärkeanzeige und eine Checkliste (Länge, Kleinbuchstabe, Großbuchstabe, Ziffer, Sonderzeichen), und onReset läuft erst, wenn das Passwort die Mindestlänge hat, mindestens die Stufe Fair erreicht und mit der Bestätigung übereinstimmt. passwordMinLength legt die Regel für die Länge fest (standardmäßig 8). OWASP stuft Passwörter unter 15 Zeichen ohne zweiten Faktor als schwach ein. Bietet deine App keine Zwei-Faktor-Bestätigung an, erhöh den Wert also.

    In der letzten Ansicht steht „We signed you out on your other devices“. Sorg also auf dem Server dafür, dass das stimmt, und schick eine Bestätigung per E-Mail. Ihr Button „Sign in“ ruft onSuccess auf, und das führt zurück zur Anmelderoute, statt die Person automatisch anzumelden, so wie es die OWASP-Empfehlung zum Reset nahelegt.

    Drück Change password mit leeren Feldern, um jeden Fehler zu sehen, und tipp dann ein neues Passwort ein: Stärkeanzeige und Checkliste reagieren sofort
    $ npx wingo-ui@latest add login-page
    ProDoku: Login Page

    Wie passt du Layout und Texte der Login-Seite an?

    Über Props: Für alles Visuelle gibt es eine, jeweils mit Standardwert.

    • layout: "split" (Formular neben einem Hero-Bereich ab 1024px), "centered" oder "card" (eine abgehobene Karte auf einem gepunkteten Hintergrund).
    • heroImage legt ein Foto hinter den Hero-Bereich, hero ersetzt seinen Inhalt. Der Standard-Hero ist Beispieltext für ein erfundenes CRM, ersetz ihn also.
    • logo, tone (standardmäßig neutral, "brand" für deine Akzentfarbe auf den primären Buttons), radius ("sm", "md", "lg") und size ("lg", der Standard, bedeutet 48px hohe Felder; "md" bedeutet 40px, auf Touchscreens 44px).
    • labels nimmt jeden Text an und wird mit DEFAULT_LOGIN_PAGE_LABELS zusammengeführt. So übersetzt du die Seite. locale setzt nur das Attribut lang, übersetzte Texte laufen also trotzdem über labels.
    • providerLayout="row" macht aus den gestapelten Buttons „Continue with Google“ Icon-Buttons nebeneinander.

    Brauchst du nur ein Stück davon in einem eigenen Formular: Die Teile funktionieren auch einzeln. Ein Code-Schritt mit dem kostenlosen OTP Input:

    tsx
    "use client";
    import { useState } from "react";
    import { OtpInput, type OtpStatus } from "@/components/ui/otp-input";
    export function CodeStep({ verify }: { verify: (code: string) => Promise<boolean> }) {
    const [status, setStatus] = useState<OtpStatus>("idle");
    return (
    <OtpInput
    length={6}
    autoFocus
    status={status}
    onChange={() => setStatus("idle")}
    onComplete={async (code) => setStatus((await verify(code)) ? "success" : "error")}
    />
    );
    }

    Unser Guide zum React OTP Input geht genauer auf Einfügen, SMS-Autofill und Resend-Timer ein, und der Guide zu React-Formularkomponenten erklärt den Aufbau aus Feld, Label und Fehler, den der Block nutzt.

    Was ist der nächste Schritt?

    Installier die Blöcke und ersetz die simulierten Standard-Handler durch deine Server Actions:

    bash
    npx wingo-ui@latest add login-page register-page

    Login Page, Register Page und Password Input gehören zu Wingo UI Pro. Das kostet Stand Oktober 2026 $8 im Monat, $80 im Jahr oder einmalig $150 (zu den Preisen); der OTP Input ist kostenlos. Für die Screens nach der Anmeldung baut das Tutorial zum Next.js Admin Dashboard die App-Shell, und die Kategorie „Praxis: Screens und Apps“ sammelt die anderen Tutorials für komplette Screens.

    Komponenten in diesem Artikel

    • Login Page

      A React login page with password, magic link or code sign-in, Google, Apple and Microsoft buttons, two-step codes and password recovery.

      Pro
    • Register Page

      A React register page for a CRM sign-up: an account with password rules, company details from the CUI, an email code and a welcome step.

      Pro
    • OTP Input

      A React OTP input for one-time verification codes and PINs, with digits that roll into place and a caret that slides between slots.

      Free
    • Password Input

      A password field with a caret-safe reveal toggle, a Caps Lock warning and, for sign-up, a strength meter, rules checklist and generator.

      Pro

    Häufige Fragen

    Hat shadcn/ui eine Login-Seite?

    Ja. Die Blocks-Seite von shadcn/ui führt Stand Oktober 2026 fünf Login-Blöcke (login-01 bis login-05) und fünf Signup-Blöcke. Das sind Layouts ohne Submit-Handler, ohne Fehlerzustand und ohne Ansichten für die Wiederherstellung, den Auth-Flow baust du also selbst.

    Soll ein Login-Fehler verraten, ob die E-Mail existiert?

    Nein. OWASP empfiehlt für jede fehlgeschlagene Anmeldung eine einzige allgemeine Meldung, etwa „Invalid user ID or password“, und im Formular für ein vergessenes Passwort dieselbe Antwort, egal ob es ein Konto gibt. Der Block Login Page hält sich standardmäßig an beide Regeln.

    Magic Link oder Einmalcode: Was ist besser für passwortlosen Login?

    Ein Code hält die Person in deinem Tab und übersteht Mail-Scanner in Firmen, die einen Link öffnen und ein einmaliges Token verbrauchen können, bevor die Person klickt. Ein Link ist ein Klick auf demselben Gerät. Die Login Page unterstützt beides über die Prop methods, du kannst also eins zuerst anbieten und das andere als Alternative.

    Wie zeige ich Fehler aus Server Actions in einem Client-Login-Formular an?

    Gib den Fehler aus der Server Action als Daten zurück, zum Beispiel { error: "E-Mail oder Passwort ist nicht korrekt" }, und wirf ihn im Client-Handler. Die Next.js-Doku rät, erwartbare Fehler als Rückgabewerte zu modellieren, und eine auf dem Server geworfene Meldung kommt in der Produktion nicht unverändert im Client an. Die Login Page zeigt die Meldung jeder Ablehnung in ihrem Alert.

    Ist die Login-Seite von Wingo UI kostenlos?

    Der OTP Input für die Codes ist kostenlos. Der Block Login Page, der Block Register Page und der Password Input gehören zu Wingo UI Pro, und alle lassen sich mit npx wingo-ui@latest add installieren.

    • Authentifizierung
    • Next.js
    • shadcn/ui
    • React
    • Tailwind CSS

    Teilen

    SR

    Über den Autor

    Serban Rusu

    Gründer von Wingo UI

    Serban Rusu hat Wingo UI gegründet. Er baut die Komponenten, die CLI und den MCP-Server und schreibt über React-UIs für Smartphones und KI-Agents.

    Mehr von Serban
    Weiterlesen

    Ähnliche Artikel

    Alle Artikel
    Vergleiche und Alternativen

    Shadcn Blocks vs Components: Womit du anfangen solltest

    Shadcn Blocks vs Components vs Vorlagen: was dir jede Ebene abnimmt, was spätere Anpassungen kosten und eine einfache Regel, womit du neue Screens startest.

    SRSerban Rusu·16. Okt. 2026·11 Min. Lesezeit
    UI mit KI-Agenten bauen

    Codex MCP einrichten: React-UI mit echten Komponenten

    Codex MCP für React-UI einrichten, getestet mit Codex CLI 0.162.1: config.toml-Eintrag, Key-Variable, AGENTS.md-Regeln, Prompts für echte Blocks, Fehlerhilfe.

    SRSerban Rusu·13. Okt. 2026·11 Min. Lesezeit
    UI mit KI-Agenten bauen

    Claude Code shadcn Setup: MCP-Server, Skill und CLAUDE.md

    Claude Code shadcn Setup, getestet mit shadcn 4.21.4: MCP-Server, Skill, eine zweite Registry im eigenen Ordner, ein CLAUDE.md-Block und Prompts, die wirken.

    SRSerban Rusu·9. Okt. 2026·12 Min. Lesezeit
    Newsletter

    Neue Artikel per E-Mail

    Neue Guides, Tutorials und Vergleiche aus dem Wingo UI Blog, sobald sie erscheinen. Die meisten Artikel sind auf Englisch.

    Kein Spam. Du kannst dich jederzeit abmelden.

    WingoUI

    Animierte, konfigurierbare, mobile-first React-Komponenten und Blöcke. Kopier den Quellcode, pass ihn an und lass deinen Coding-Agenten damit bauen.

    Produkt

    • Komponenten
    • Vorlagen
    • Preise
    • KI-Agenten
    • Theme-Generator

    Ressourcen

    • Doku
    • Blog
    • Changelog
    • Changelog-RSS
    • llms.txt

    Rechtliches

    • AGB
    • Datenschutz
    • Rückerstattung
    • Lizenz
    • Impressum

    Komponenten-Kategorien

    • Buttons & Actions
    • Inputs
    • Forms
    • Navigation
    • Overlays
    • Feedback
    • Data Display
    • Tables & Lists
    • Charts & Stats
    • Layout
    • Media
    • AI Kit
    • Text & Effects
    • Mobile
    • Commerce
    • Marketing Sections
    • Blocks
    • Hooks & Utilities

    © 2026 Wingo UI

    Wingo UI