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:
- "next": "^14.2+"
- "tailwindcss": "^3.4+"
- "react-hook-form: 7.52.2" - Do not use higher versions for now, as they contain rendering bugs (issues: https://github.com/react-hook-form/react-hook-form/issues/12518)
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 validationmotion
-- Animation library@zxcvbn-*
-- Password strength detection libraryembla-carousel-*
-- Carousel library (Note:embla-carousel
must be installed indevDependencies
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>
}
Parameter | Default Value | Description |
---|---|---|
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) |
email | Initial 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. |
socialHandler | Callback 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) |
serverAction | All forms (except social login) submit to the server via Server Actions | |
serverStateSuccessHandler | Typically, server-side authentication completes with a server-side redirect. If you need client-side redirect logic, use this callback. |
Notes:
socials
-- Each template includes aconfig/social.tsx
configuration file. You can edit this file to add additional social login methods. Modify thekey
to match your authentication framework or replace the icon SVG as desired.email
-- Theverify-otp
form UI includes a button to edit the email. When clicked, the user should return to the previous page to correct the email. Theemail
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>
}
Parameter | Default Value | Description |
---|---|---|
email | Initial email, also used for pre-filling when returning from the OTP verification form after editing | |
serverAction | Submits form data to the server via Server Actions | |
serverStateSuccessHandler | Typically, 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>
}
Parameter | Default Value | Description |
---|---|---|
email | Initial 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. |
serverAction | Submits form data to the server via Server Actions | |
serverStateSuccessHandler | Typically, 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>
}
Parameter | Default Value | Description |
---|---|---|
email | Email used only for password strength testing (recommended to improve password strength input) | |
resetToken | Token generated by the server to track form submission and state | |
serverAction | Submits form data to the server via Server Actions | |
serverStateSuccessHandler | Typically, 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 acookie
, which the browser automatically includes when submitting the form.For the former, use
searchParams
oruseSearchParams()
to retrieve theresetToken
from the query parameters and pass it to the form. The form will automatically include thisresetToken
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>
}
Parameter | Default Value | Description |
---|---|---|
email | Initial email displayed in the UI for user editing | |
verifyToken | Token generated by the server to track form submission and state | |
editEmailHandler | Callback when clicking the "Edit Email" button | |
codeResendAction | Callback for resending the code (resend interval can be configured via the code_resend_max_seconds constant in verify-otp-form.tsx ) | |
serverAction | Submits form data to the server via Server Actions | |
serverStateSuccessHandler | Typically, 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 acookie
, which the browser automatically includes when submitting the form.For the former, use
searchParams
oruseSearchParams()
to retrieve theverifyToken
from the query parameters and pass it to the form. The form will automatically include thisverifyToken
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 }
}