LoginGen Documentation

Introduction

LoginGen is a login page template website. The reason for developing this project is that while there are many template websites available on the market, most lack support for login pages or only provide one or two simple templates, often without complete frontend logic. Although login pages may not carry as much interaction weight as Landing Pages, an aesthetically pleasing login page can significantly enhance users' first impression of a website.

The tech stack is primarily based on Next.js and Shadcn/ui. In addition to providing template designs, it also offers complete frontend logic and utilizes Next.js's Server Actions capability for unified server-side authentication processing (except for social login, which is implemented in client-side events for convenience).

All code is open-source, allowing you to copy and paste it directly for use, just like Shadcn. You are encouraged to modify the code according to your project's needs. For convenience, common properties have been extracted from form components to enable out-of-the-box usage.

More templates will be continuously added, improved, and optimized in the future. Stay tuned.

Installation

The templates are developed based on Next.js and Shadcn, so you first need to have the project environment set up:

shadcn Components

If you are using other package managers or prefer manual installation, refer to Shadcn's documentation.

pnpm dlx shadcn@latest add card tabs form button input input-otp separator sonner

Add the following CSS animations to your tailwind.config.js file:

// for input-otp
/** @type {import('tailwindcss').Config} */
module.exports = {
  theme: {
    extend: {
      keyframes: {
        'caret-blink': {
          '0%,70%,100%': { opacity: '1' },
          '20%,50%': { opacity: '0' }
        }
      },
      animation: {
        'caret-blink': 'caret-blink 1.25s ease-out infinite'
      }
    }
  }
}

For sonner, you need to add the following component:

import { Toaster } from '@/components/ui/sonner'
 
export default function RootLayout({ children }) {
  return (
    <html lang='en'>
      <head />
      <body>
        <main>{children}</main>
        <Toaster />
      </body>
    </html>
  )
}

Third-Party Dependencies

pnpm add react-hook-form@7.52.2 zod motion @zxcvbn-ts/core @zxcvbn-ts/language-common @zxcvbn-ts/language-en embla-carousel-react embla-carousel-autoplay embla-carousel-fade lucide-react
pnpm add -D embla-carousel
  • react-hook-form + zod -- Form client-side validation
  • motion -- Animation library
  • @zxcvbn-* -- Password strength detection library
  • embla-carousel-* -- Carousel library (Note: embla-carousel must be installed in devDependencies to correctly obtain TypeScript types)
  • lucide-react -- Icon library

Copy-Paste Code

Each template's preview page displays complete code, which you can copy and paste into the corresponding location in your project. Images included in the templates can be downloaded directly, and videos are provided via CDN.

Currently, there is no support for one-click installation (e.g., via npx), but this may be considered in the future.

Themes

Each template includes a styles/theme.module.css file containing all theme variables, styles, and animations. You can modify this file to adjust the theme or align it with your website's UI aesthetics.

Dark Mode

For dark mode, follow Shadcn's documentation. In each template's theme.module.css file, dark theme styles are prefixed with :global(.dark) to respond to the website's global dark mode.

I18n (TODO)

Multi-language configuration is not yet supported, but you can manually add it (though it may be slightly cumbersome). This feature may be added in the future.

Form Properties

There are five forms: Sign In, Sign Up, Forget Password, Reset Password, and OTP (One-Time Password) verification. Each form includes parameters for out-of-the-box usage, most of which are common across forms.

Although the template code defaults to separate routes for each form page with server-side redirection for navigation between them, all components are designed to work with prop passing, meaning you can also use client-side routing.

Sign In

The interface is defined as follows:

const _auths = ['social', 'email', 'password'] as const
type Auth = (typeof _auths)[number]
 
export interface SignInFormProps {
  auths?: Readonly<Auth[]>
  socials?: SocialKey[]
  email?: string
  emailStrategy?: 'magic-link' | 'one-time-pwd'
  socialHandler?: (event: MouseEvent<HTMLButtonElement>, key: SocialKey) => void | Promise<void>
  forgetHandler?: string | ((event: React.MouseEvent<HTMLAnchorElement>) => void | Promise<void>) // for more convenient use
  serverAction?: (
    formType: 'email' | 'password',
    currentState: SignInActionState,
    formData: FormData
  ) => Promise<SignInActionState>
  serverStateSuccessHandler?: (formType: 'email' | 'password', email: string) => void | Promise<void>
}
ParameterDefault ValueDescription
auths['social', 'password', 'email']Authentication methods: social login, email login, password login
socials['google', 'github']Types of social login (layout adjusts automatically based on the number selected and form width)
emailInitial email, also used for pre-filling when returning from the OTP verification form after editing
emailStrategy'magic-link'Type of email sent. If 'magic-link', no page redirection occurs, and a Sonner toast is triggered. Otherwise, redirection to the OTP verification form occurs.
socialHandlerCallback function when clicking a social login button (e.g., using libraries like Auth.js)
forgetHandler'#'Callback when clicking the "Forgot Password" link (included for convenience)
serverActionAll forms (except social login) submit to the server via Server Actions
serverStateSuccessHandlerTypically, server-side authentication completes with a server-side redirect. If you need client-side redirect logic, use this callback.

Notes:

  1. socials -- Each template includes a config/social.tsx configuration file. You can edit this file to add additional social login methods. Modify the key to match your authentication framework or replace the icon SVG as desired.
  2. email -- The verify-otp form UI includes a button to edit the email. When clicked, the user should return to the previous page to correct the email. The email property is designed to pre-fill the email field in this scenario.

Sign Up

The interface is defined as follows:

export interface SignUpFormProps {
  email?: string
  serverAction: (currentState: SignUpActionState, formData: FormData) => Promise<SignUpActionState>
  serverStateSuccessHandler?: (email: string) => void | Promise<void>
}
ParameterDefault ValueDescription
emailInitial email, also used for pre-filling when returning from the OTP verification form after editing
serverActionSubmits form data to the server via Server Actions
serverStateSuccessHandlerTypically, server-side authentication completes with a server-side redirect. If you need client-side redirect logic, use this callback.

Forget Password

The interface is defined as follows:

export interface ForgetPasswordProps {
  email?: string
  emailStrategy?: 'magic-link' | 'one-time-pwd'
  serverAction: (
    currentState: ForgetPasswordActionState,
    formData: FormData
  ) => Promise<ForgetPasswordActionState>
  serverStateSuccessHandler?: (email: string) => void | Promise<void>
}
ParameterDefault ValueDescription
emailInitial email, also used for pre-filling when returning from the OTP verification form after editing
emailStrategy'magic-link'Type of email sent. If 'magic-link', no page redirection occurs, and a Sonner toast is triggered. Otherwise, redirection to the OTP verification form occurs.
serverActionSubmits form data to the server via Server Actions
serverStateSuccessHandlerTypically, server-side authentication completes with a server-side redirect. If you need client-side redirect logic, use this callback.

Reset Password

The interface is defined as follows:

export interface ResetPasswordFormProps {
  email?: string // Only used to test password strength
  resetToken?: string
  serverAction: (
    resetToken: string,
    currentState: ResetPasswordActionState,
    formData: FormData
  ) => Promise<ResetPasswordActionState>
  serverStateSuccessHandler?: () => void | Promise<void>
}
ParameterDefault ValueDescription
emailEmail used only for password strength testing (recommended to improve password strength input)
resetTokenToken generated by the server to track form submission and state
serverActionSubmits form data to the server via Server Actions
serverStateSuccessHandlerTypically, server-side authentication completes with a server-side redirect. If you need client-side redirect logic, use this callback.

resetToken In the password reset flow, the server typically generates a token to securely track and confirm the form submitter and state.

The server usually passes the token in two ways: either in the URL query parameters or in a cookie, which the browser automatically includes when submitting the form.

For the former, use searchParams or useSearchParams() to retrieve the resetToken from the query parameters and pass it to the form. The form will automatically include this resetToken when submitting to the server via Server Action.

For the latter, you can ignore this parameter.

OTP Verification

The interface is defined as follows:

export interface VerifyOTPFormProps {
  email: string
  verifyToken?: string
  editEmailHandler?: (event: MouseEvent<HTMLButtonElement>) => void
  serverAction: (
    email: string,
    verifyToken: string,
    currentState: VerifyOTPActionState,
    formData: FormData
  ) => Promise<VerifyOTPActionState>
  codeResendAction: (email: string, verifyToken: string) => Promise<VerifyOTPActionState>
  serverStateSuccessHandler?: () => void | Promise<void>
}
ParameterDefault ValueDescription
emailInitial email displayed in the UI for user editing
verifyTokenToken generated by the server to track form submission and state
editEmailHandlerCallback when clicking the "Edit Email" button
codeResendActionCallback for resending the code (resend interval can be configured via the code_resend_max_seconds constant in verify-otp-form.tsx)
serverActionSubmits form data to the server via Server Actions
serverStateSuccessHandlerTypically, server-side authentication completes with a server-side redirect. If you need client-side redirect logic, use this callback.

verifyToken In the one-time password verification flow, the server typically generates a token to securely track and confirm the form submitter and state.

The server usually passes the token in two ways: either in the URL query parameters or in a cookie, which the browser automatically includes when submitting the form.

For the former, use searchParams or useSearchParams() to retrieve the verifyToken from the query parameters and pass it to the form. The form will automatically include this verifyToken when submitting to the server via Server Action.

For the latter, you can ignore this parameter.

Server Side

Except for social login, all other login options submit to Server Actions for server-side processing after client-side validation. The submission uses React's useFormState and useFormStatus.

The types/action-state.ts file in the template code defines a simple data format for Server Actions. You can extend this as needed.

Below is an example of server action operations:

sign-in-action.ts

'use server'
 
import type { FormType, SignInActionState } from '@/types/server'
import { redirect } from 'next/navigation'
import { revalidatePath } from 'next/cache'
 
export async function signInAction(
  formType: FormType,
  currentState: SignInActionState,
  formData: FormData
): Promise<SignInActionState> {
  switch (formType) {
    case 'email':
      return await emailAction(formData)
    case 'password':
      return await passwordAction(formData)
  }
}
 
async function emailAction(formData: FormData): Promise<SignInActionState> {
  const data = Object.fromEntries(formData)
  const parsed = signInSchemas.email.safeParse(data)
 
  // Form validation is also required on the server side
  if (!parsed.success) {
    const issues: Record<string, string> = {}
    parsed.error.issues.map((issue) => (issues[issue.path[0]] = issue.message))
    return {
      success: false,
      formIssues: issues
    }
  }
 
  // Perform some server-side operations, such as sending emails, operating databases, etc.
 
  // Redirection can be performed on the server side
  revalidatePath('/auth/sign-in')
  redirect(`/auth/verify-otp`)
 
  // Or return it directly to the client
  // return { success: true }
}
 
async function passwordAction(formData: FormData): Promise<SignInActionState> {
  const data = Object.fromEntries(formData)
  // mock delay
  await new Promise((resolve) => setTimeout(resolve, 2000))
  console.log('Sign in password server action form data : ' + JSON.stringify(data, null, 2))
 
  return { success: true }
}