惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

N
News and Events Feed by Topic
酷 壳 – CoolShell
酷 壳 – CoolShell
G
Google Developers Blog
N
Netflix TechBlog - Medium
Recorded Future
Recorded Future
S
Securelist
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
Scott Helme
Scott Helme
C
Check Point Blog
量子位
月光博客
月光博客
Last Week in AI
Last Week in AI
C
Cisco Blogs
G
GRAHAM CLULEY
B
Blog RSS Feed
K
Kaspersky official blog
爱范儿
爱范儿
J
Java Code Geeks
I
Intezer
aimingoo的专栏
aimingoo的专栏
WordPress大学
WordPress大学
云风的 BLOG
云风的 BLOG
C
CXSECURITY Database RSS Feed - CXSecurity.com
H
Hackread – Cybersecurity News, Data Breaches, AI and More
博客园 - 三生石上(FineUI控件)
罗磊的独立博客
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
MongoDB | Blog
MongoDB | Blog
T
Threat Research - Cisco Blogs
N
News | PayPal Newsroom
Exploit-DB.com RSS Feed
Exploit-DB.com RSS Feed
Security Latest
Security Latest
T
Tailwind CSS Blog
人人都是产品经理
人人都是产品经理
P
Privacy International News Feed
The Cloudflare Blog
H
Heimdal Security Blog
宝玉的分享
宝玉的分享
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
腾讯CDC
GbyAI
GbyAI
V
Visual Studio Blog
Recent Commits to openclaw:main
Recent Commits to openclaw:main
博客园 - 聂微东
T
Tenable Blog
The Register - Security
The Register - Security
AI
AI
C
Cybersecurity and Infrastructure Security Agency CISA
S
Schneier on Security
L
LangChain Blog

Clerk Blog

Going to production with Clerk Deploy Clerk Init: The fastest way to start a new project Introducing Clerk CLI Middleware-based route protection bypass Postmortem: Clerk System Outage (March 10, 2026) Clerk for the AI era Add API Key support to your SaaS in minutes Postmortem: Clerk System Outage (February 19, 2026) Using Clerk in a React Native app Postmortem: DNS Provider Outage (February 10, 2026) How do I implement passkeys in Next.js? Clerk ranked #4 fastest-growing software vendor on Ramp’s December 2025 list How do I handle JWT verification in Next.js? Committing to Agent Identity: Clerk raises $50m Series C from Menlo and Anthropic’s Anthology Fund What is the best way to handle authentication in Next.js App Router? Postmortem: Database Incident (September 14–18, 2025) How do I add authentication to a Next.js app? Introducing Free Trials in Clerk Billing Postmortem: August 28, 2025 - elevated API latency and errors Introducing Mosaic: Bring Your Brand to Every Authentication Flow Multi-tenant authentication: What you need to know (and how Clerk helps) What are the risks and challenges of multi-tenancy? Resilience in Practice: Regional Failover at Clerk Build a Cross-Platform B2B App with Clerk, Expo, and Supabase Highlights from the MiduDev/Clerk Hackathon Add multi-tenancy to an app built with Clerk, Lovable, and Supabase How to build an AI coding rules app with Clerk, Lovable, and Supabase How to Build Multi-Tenant Authentication with Clerk Choosing the right SaaS architecture: Multi-Tenant vs. Single-Tenant Postmortem: June 26, 2025 service outage How to Design a Multi-Tenant SaaS Architecture What is multi-tenancy and why it matters for B2B SaaS How OAuth Works Synchronize user data from Clerk to Supabase Add subscriptions to your SaaS with Clerk Billing Getting started with Clerk Billing Multi-tenant analytics with Tinybird and Clerk How Huntr Migrated 250K Users to Clerk: A Scalable Auth Solution for Startups How to take Clerk to Production How to take your Clerk application to production A practical guide to testing Clerk Next.js applications Implementing multi-tenancy into a Supabase app with Clerk How Clerk integrates with a Next.js application using Supabase How Clerk integrates with Supabase Build a blog with tRPC, Prisma, Next.js and Clerk How to enrich PostHog events with Clerk user data How to build a secure project management platform with Next.js, Clerk, and Neon Validate your SaaS idea while building an audience Postmortem: February 6, 2025 service outage Implement Role-Based Access Control in Next.js 15 Build a Next.js login page template How to implement Google authentication in Next.js 15 What is middleware in Next.js? How to customize Next.js metadata How to set environment variables in Node.js Building a React Login Page Template How to implement per-user OAuth scopes with Clerk Using Clerk SSO to access Google Calendar and other service data Streamline enterprise customer onboarding with SAML and Clerk Clerk launches EASIE SSO and eliminates SSO fees How to secure Liveblocks Rooms with Clerk in Next.js Securing Node.js Express APIs with Clerk and React Combining the benefits of session tokens and JWTs Build a task manager with Next.js, Supabase, and Clerk Comparing Clerk Webhooks vs Backend API Automate Neon schema changes with Drizzle and GitHub Actions A guide to reading authenticated user data from Clerk Role based access control with Clerk Organizations Mitigating OAuth’s recently discovered Open Response Type vulnerability Per-user B2B monetization with Stripe and Clerk Organizations Build a team-based task manager with Next.js, Neon, and Clerk Building a Hybrid Sign-Up/Subscribe Form with Stripe Elements Welcoming Colin from Zod as our inaugural Open Source Fellow Build a modern authenticated chat application with Next.js, Ably, and Clerk Build a waitlist with Clerk user metadata How to use Clerk with PostHog Identify in Next.js How to secure API Gateway using JWT and Lambda Authorizers with Clerk What are passkeys and how do they work? Comparing Authentication in React.js vs. Next.js How to Add an Onboarding Flow for your Application with Clerk Create Your Own Custom User Menu with Radix - Part 2 Introducing Webhook Workflows with Inngest & Svix Clerk raises $30M Series B from CRV and Stripe Clerk in 2023: A Year in Review Build a Movie Emoji Quiz App with Remix, Fauna, and Clerk Ultimate Guide to Magic Link Authentication Create Your Own Custom User Menu with Radix Introducing has(), protect(), and <Protect> Updated Pricing: 10,000 MAUs Free, and a new “Pro Plan” Next.js Authentication with Clerk: Streamlined SSR Handling Clerk Webhooks: Data Sync with Convex Exploring Clerk Metadata with Stripe Webhooks The Ultimate Guide to Next.js Authentication Empower Your Support Team With User Impersonation Clerk Webhooks: Getting Started A Complete Guide to Session Management in Next.js The Advanced Guide to Passwordless Authentication in Next.js How We Roll – Chapter 10: Roundup How We Roll – Chapter 9: Infrastructure Password-Based Authentication in Next.js
Build a Next.js sign-up form with React Hook Form
Alex Booker · 2025-02-04 · via Clerk Blog

In this post, you will learn how to build a sign-up form using the Next.js App Router and the following technologies:

  • Argon2 - Secure password hashing algorithm that provides strong protection against attacks.
  • Drizzle - ORM (Object-Relational Mapping) tool used to define the database schema and perform database operations, such as inserting or querying users.
  • Zod - TypeScript-first schema declaration and validation library.
  • shadcn/ui - An assortment of beautifully-designed components you can copy into your app.
  • React Hook Form - Library to simplify React form management and validation.

By the end of this guide, you will have a fully functional and secure sign-up form with the following features:

  1. Dynamic form validation - Users receive feedback on the validity of their input when they type.
  2. Password strength feedback - Input validation ensures users follow password best practices to create strong passwords.
  3. Secure password storage - Passwords are hashed using Argon2 before being stored.

We won't be building the sign-up form step-by-step. Instead, you'll find the complete source code for the post on GitHub. I will guide you through the key parts of the code, explaining how each section functions and contributes to the final product.

Database schema

Let's begin with the database schema, as it defines the structure of the sign-up form and serves as its foundation.

@/db/schema.ts

I'm using Drizzle with Postgres, but one of the advantages of using an ORM like Drizzle is its flexibility - you can adapt it to work with almost any database with minimal adjustments.

The table includes these columns: id, createdAt, email, and passwordHash.

An important aspect often overlooked is ensuring emails are stored as unique and case-insensitive. While PostgreSQL offers the citext module for this purpose, I've opted for an index using the lower function. This approach keeps everything within the application code and avoids the need to run additional PostgreSQL queries.

While basic constraints like length are useful at the database level, validations like email format are best handled in the application layer. In the next section, we'll explore using Zod to define and validate the email and other inputs before storing them in the database.

Zod validation

@/definitions/sign-up.ts

It's important to validate user input on both the frontend (in the client component) and the backend (in the server function):

  • Client-side validation provides instant feedback to users and improves the user experience by catching errors before submitting the form. However, client-side validation can be bypassed or may fail if JavaScript doesn't load correctly.
  • Server-side validation acts as a crucial security layer, ensuring that invalid and potentially malicious data is caught and handled properly before it reaches the database, even if the client-side validation is circumvented.

Instead of duplicating validation code on the server and client, we use Zod to define the "shape" of a valid form in one place. By exporting the schema, it can be referenced on both the server and client and we keep the code nice and DRY.

Server action

@/actions/sign-up.ts

A server action is a server-side function that can be called directly from client components. This allows you to run backend code, such as database queries and mutations, without needing to create separate API endpoints.

A common security oversight with server functions is assuming client-side validation is sufficient. However, server functions are essentially HTTP endpoints and a malicious actor could send invalid data directly using a tool like cURL. This may lead to inconsistencies in your database and could even pose a security risk. For this reason, we use Zod to validate all incoming data on the server, even though we already have client-side validation in place.

The server function checks for existing users by querying the database with the provided email. If a user is found, it returns a field-level error message stating: "The email you entered has already been taken".

Password hashing

In the server action above, we first hash the password before storing it in the database:

@/actions/sign-up.ts

What is hashing and why is it important?

Storing passwords in plain text creates a significant security vulnerability. If an attacker gains access to your database through a data breach, they immediately have access to every user's account. Worse yet, since many people reuse passwords across services, compromised credentials could lead to breaches of users' accounts on other platforms.

This is where password hashing becomes crucial. A hash function transforms a password into an irreversible string of characters. When a user attempts to sign in, the system hashes their input password and compares it with the stored hash. If they match, you know the credentials are valid. This all happens without ever storing or exposing the actual password.

The code uses Argon2 for password hashing, which is considered one of the most secure hashing algorithms available today. While older algorithms like MD5 were once common, they've proven vulnerable to reverse-engineering attacks. Other popular options like Bcrypt are still secure, but Argon2 offers additional benefits - it's memory-hard (making it resistant to specialized hardware attacks) and was specifically designed to be future-proof against advances in password cracking technology.

Creating the form with React Hook Form

@/components/sign-up-form.tsx

While server-side validation is essential for security, relying on it alone creates a suboptimal user experience. Without client-side validation, users would need to submit the form to see if their input was valid - an experience that feels clunky and outdated.

The SignUpForm component uses React Hook Form to provide immediate, dynamic feedback as users type.

By passing the same Zod schema we use on the server to React Hook Form's zodResolver, we get automatic validation of password strength requirements and email format.

This creates a layered validation approach - immediate client-side feedback for a smooth user experience, backed by robust server-side validation for security.

As an added benefit, if JavaScript is disabled, the form gracefully falls back to server-side validation, displaying errors returned from the server function via useActionState.

Advanced sign-up form features

This concludes our guide to building a secure sign-up form with Next.js, React Hook Form, and Argon2. You now have a solid foundation with robust form validation and proper password hashing. Additionally, the form is built using progressive enhancement, meaning it works even without JavaScript. This means you'll never miss a potential sign-up, even if JavaScript fails to load due to network issues, browser settings, or extensions.

While this is a good start, production-ready sign-up forms usually require more sophisticated features. Here are some advanced capabilities to consider for your implementation:

User experience improvements:

  • Social Sign-In Options - Improve conversion rate by enabling your users to sign up quickly by authenticating with Google and other SSO providers.
  • Biometric Authentication with Passkeys - Enable users to sign-up using fingerprint or facial recognition.
  • Web3 Authentication Options - Enables users to authenticate using blockchain-based methods.

Security measures:

  • Email Verification - Ensure user authenticity and prevent spam accounts by confirming the user's email address.
  • Bot Detection - Utilize CAPTCHA or similar technologies to prevent automated and spam sign-ups.
  • Rate Limiting - Protect against abuse by limiting the number of sign-up attempts from a single source.
  • Blocklist - Block specific account identifiers, such as accounts with your competitor's email domain, from signing up.
  • Block Email Subaddresses - Prevent sign-ups using email addresses with characters like +, =, or #.
  • Block High-Risk Disposable Email Addresses - Reject sign-ups using email addresses from disposable email domains.

So why Clerk then?

Clerk is a user management and authentication platform, so it might surprise you that we're publishing an article that explains how to implement user registration in Next.js yourself.

While implementing the sophisticated features listed above from scratch is possible, it requires significant development effort and security expertise. If these advanced features are important for your application but you don't want to build them yourself, consider using a complete user management and authentication platform like Clerk that provides these capabilities out of the box.

In addition to sign-up, Clerk provides sign-in and manages the entire session, allowing you to authenticate access to pages and access information about the current user wherever you need it.

Learn how to add not only a sign-up form but complete sign-in and session management in minutes:

The best part? Clerk uses components as the API.

Instead of building your own form component and manually building all the necessary logic, you can just drop a Next.js <SignUp /> component in your page like so:

app/sign-up/[[...sign-up]]/page.tsx

Clerk's component-driven approach makes setup incredibly easy. You can further customise your sign-up process and manage advanced features directly from the Clerk dashboard once you create a free application following the link below.