Index: app/lib/actions.ts
===================================================================
--- app/lib/actions.ts	(revision 2e0a1389aa2e0d7b46373f6b174d549e2eedb893)
+++ app/lib/actions.ts	(revision bd7f7a743898148bd6afd7fd149ddc7773d49b1d)
@@ -5,4 +5,5 @@
 import postgres from 'postgres';
 import { signIn } from '@/auth';
+import bcrypt from "bcryptjs";
 import { AuthError } from 'next-auth';
 import { redirect } from 'next/navigation';
@@ -35,5 +36,48 @@
 }
 
-export async function register() {
+export async function register(
+    prevState: string | undefined,
+    formData: FormData,
+) {
+    const schema = z.object({
+        name: z.string().min(1),
+        email: z.string().email(),
+        password: z.string().min(6),
+        redirectTo: z.string().optional(),
+    });
+
+    const parsed = schema.safeParse({
+        name: formData.get('name'),
+        email: formData.get('email'),
+        password: formData.get('password'),
+        redirectTo: formData.get('redirectTo'),
+    });
+
+    if (!parsed.success) {
+        return 'Invalid form data.';
+    }
+
+    const { name, email, password, redirectTo } = parsed.data;
+
+    const existing =
+        await sql`SELECT id FROM users WHERE email=${email}`;
+
+    if (existing.length > 0) {
+        return 'User already exists.';
+    }
+
+    const hashed = await bcrypt.hash(password, 10);
+
+    await sql`
+    INSERT INTO users (name, email, password)
+    VALUES (${name}, ${email}, ${hashed})
+  `;
+
+    // auto-login
+    await signIn('credentials', {
+        email,
+        password,
+        redirectTo: redirectTo || '/home',
+    });
 }
 
Index: app/ui/register-form.tsx
===================================================================
--- app/ui/register-form.tsx	(revision 2e0a1389aa2e0d7b46373f6b174d549e2eedb893)
+++ app/ui/register-form.tsx	(revision bd7f7a743898148bd6afd7fd149ddc7773d49b1d)
@@ -147,5 +147,5 @@
       </p>
 
-      {typeof errorMessage === 'string' && errorMessage && (
+      {errorMessage && (
         <div
           className="flex h-8 items-end space-x-1"
Index: package.json
===================================================================
--- package.json	(revision 2e0a1389aa2e0d7b46373f6b174d549e2eedb893)
+++ package.json	(revision bd7f7a743898148bd6afd7fd149ddc7773d49b1d)
@@ -12,4 +12,5 @@
     "autoprefixer": "10.4.23",
     "bcrypt": "^6.0.0",
+    "bcryptjs": "^3.0.3",
     "clsx": "^2.1.1",
     "next": "15.5.9",
Index: pnpm-lock.yaml
===================================================================
--- pnpm-lock.yaml	(revision 2e0a1389aa2e0d7b46373f6b174d549e2eedb893)
+++ pnpm-lock.yaml	(revision bd7f7a743898148bd6afd7fd149ddc7773d49b1d)
@@ -21,4 +21,7 @@
         specifier: ^6.0.0
         version: 6.0.0
+      bcryptjs:
+        specifier: ^3.0.3
+        version: 3.0.3
       clsx:
         specifier: ^2.1.1
@@ -833,4 +836,8 @@
     engines: {node: '>= 18'}
 
+  bcryptjs@3.0.3:
+    resolution: {integrity: sha512-GlF5wPWnSa/X5LKM1o0wz0suXIINz1iHRLvTS+sLyi7XPbe5ycmYI3DlZqVGZZtDgl4DmasFg7gOB3JYbphV5g==}
+    hasBin: true
+
   brace-expansion@1.1.12:
     resolution: {integrity: sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==}
@@ -2774,4 +2781,6 @@
       node-addon-api: 8.5.0
       node-gyp-build: 4.8.4
+
+  bcryptjs@3.0.3: {}
 
   brace-expansion@1.1.12:
