Best for
SaaS founders who want a login page that doubles as marketing — hero image or value prop on the left, form on the right
Stack
A ready-made Two-Column Login UI you can fork, run, and customize with the prompt pack below.
What's actually inside
The honest engineer's breakdown — what the Two-Column Logintemplate does, how it's wired, and where it's opinionated.
The Two-Column Login splits the screen into two equal halves. The Left Column Panel is a marketing surface — it holds a hero image, a tagline headline, and a feature bullets list (or a testimonial quote). The intent is that visitors read the value proposition while their eye moves to the form. This is the same layout pattern used by Linear, Vercel, and most high-converting SaaS login pages.
The Right Column Form wraps a shadcn/ui Card with Email Input (controlled by react-hook-form), Password Input with a show/hide Eye toggle, and a Submit Button that enters a loading state on click. Zod handles email format and password minimum length validation, though error messages are not inline by default — they only surface on submission unless you prompt V0 to switch to 'onBlur' mode.
Honest caveats: the left panel image does not fill the column height on first fork — the img tag lacks explicit height constraints, causing the two-column layout to break on smaller viewports. You'll want to add h-screen to the left column and hidden lg:block to hide it on mobile as a first step. The template also has no mobile-optimized single-column fallback out of the box, and the 'Forgot password' link goes nowhere until you build that page.
Key UI components
Left Column PanelMarketing panel with hero image, tagline, and feature bullets or testimonial
Right Column Formshadcn/ui Card with email and password inputs and submit button
Email Inputshadcn/ui Input with Label, registered with react-hook-form
Password Inputshadcn/ui Input type='password' with show/hide Eye icon toggle
Submit Buttonshadcn/ui Button with loading spinner state during form submission
Social Proof / Feature ListStatic list of benefits or a testimonial quote with avatar in the left panel
Libraries it leans on
react-hook-formForm field registration, isSubmitting state for the loading button
zodEmail format and password minimum-length validation schema via zodResolver
Fork it and get it running
Forking and branding this template takes about 5 minutes. The left panel image replacement is the highest-impact change — do it first.
Open and Fork
Go to https://v0.dev/chat/community/dP3UBdVxREK. You'll see the two-column layout with a placeholder hero on the left and the auth form on the right. Click the 'Fork' button in the top-right corner to copy the project into your V0 account. V0 creates a new chat project with all source files.
Tip: You need a free V0 account. Sign up at v0.dev if you don't have one.
You should see: A new V0 project opens with the Two-Column Login source code and preview loaded.
Replace the Left Panel in Design Mode
Press Option+D to open Design Mode. Click the left panel area and replace the placeholder hero image URL with your own. Update the tagline headline and the feature bullet list text to describe your product's value. Change brand colors on the right form card border or Submit Button. All Design Mode edits are free — no credits used.
Tip: Use a high-quality dark image for the left panel — a semi-transparent overlay (from-black/60 to transparent) on top makes the white tagline text readable regardless of image content.
You should see: The preview shows your hero image and product copy in the left panel with updated brand colors in the right form.
Add Zod Validation and Inline Errors
Type this prompt: 'Add Zod validation to the email and password fields: email must be valid email format, password minimum 8 characters; show inline errors via shadcn/ui FormMessage below each field; set mode: onBlur on useForm so errors appear as users tab between fields.' V0 updates the schema, adds zodResolver, and wires FormMessage components.
You should see: Both fields display inline validation errors below them as the user moves between fields, before any submit attempt.
Add Auth Credentials in the Vars Panel
Click the Vars panel in the V0 toolbar. Add your auth provider credentials here: CLERK_SECRET_KEY and NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY for Clerk; or NEXT_PUBLIC_SUPABASE_URL and NEXT_PUBLIC_SUPABASE_ANON_KEY for Supabase. Never paste secrets into the V0 chat. Then use one of the auth prompts below to wire the right-column form to your provider.
You should see: Your auth credentials are securely stored as env vars, available in route handlers and Server Actions without appearing in source code.
Publish to Production
Click Share → Publish → 'Publish to Production.' Vercel deploys your Next.js project in under 60 seconds and provides a live Vercel subdomain URL. Test the full sign-in flow from the deployed URL — OAuth redirects must be tested on the real domain, not the V0 sandbox preview.
You should see: A live URL loads the two-column login. Signing in with valid credentials redirects to /dashboard.
Update Callback URLs and Attach Custom Domain
If you wired Google OAuth or any social login, update your auth provider's allowed callback URLs to include the Vercel production domain. For Supabase: Supabase Dashboard → Auth → URL Configuration → Redirect URLs. For Clerk: Clerk Dashboard → Domains. If you're attaching a custom domain, go to Vercel Dashboard → Settings → Domains, add the domain, and follow the DNS instructions.
Tip: Remove any manually set NEXTAUTH_URL if you're using NextAuth — Vercel auto-injects the correct URL per deployment. Manually setting it breaks preview deployments.
You should see: OAuth logins work on the production domain. No redirect URI mismatch errors.
The prompt pack
Copy-paste these straight into v0's chat to customize the Two-Column Logintemplate. Each one names this template's own components — no generic filler.
Replace left panel with a brand image overlay
Fixes the left panel height collapse and creates a full-height hero image layout that hides on mobile.
Replace the placeholder left-column background with an img tag using object-cover with w-full h-full. Add h-screen to the left column container div so the image fills the full viewport height. Add a semi-transparent gradient overlay (from-black/60 to transparent) over the image. Move the tagline and feature bullets on top of the image in white text. Hide the left panel on mobile with the Tailwind class hidden lg:block.
Add a 'Don't have an account? Sign up' link
Links the Two-Column Login to a sign-up page so users who land on login can navigate to registration without hunting for the link.
Add a text-sm line at the bottom of the right-column Card below the Submit Button: 'Don't have an account? ' followed by a shadcn/ui Button variant='link' with href='/signup'. On the /signup page (create it too), add a matching 'Already have an account? Sign in' link pointing back to /login. Both links should be the same text-primary style as the Forgot Password link.
Add Google OAuth button to the right column
Adds a one-click Google Sign-In path to the right-column auth form alongside the existing email and password fields.
Add a shadcn/ui Button variant='outline' with an inline Google SVG icon and label 'Continue with Google' at the top of the right-column Card, above the email input. Add a horizontal 'or' divider with text-muted-foreground between the OAuth button and the email form. Wire the Google button to supabase.auth.signInWithOAuth({ provider: 'google', options: { redirectTo: process.env.NEXT_PUBLIC_SITE_URL + '/auth/callback' } }) using NEXT_PUBLIC_SUPABASE_URL and NEXT_PUBLIC_SUPABASE_ANON_KEY from the Vars panel.Wire the form to Clerk
Wires the right-column auth form to Clerk's sign-in flow so users can authenticate with email and password.
Install @clerk/nextjs. Add ClerkProvider to app/layout.tsx using NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY from the Vars panel. In the right-column form component (marked 'use client'), use the useSignIn hook: const { signIn, setActive } = useSignIn(). On submit, call signIn.create({ identifier: email, password }). On the returned createdSessionId, call setActive then redirect to /dashboard using useRouter. Show the Clerk error.message string in a shadcn/ui Alert below the form on failure.Wire the form to Supabase Auth with a Server Action
Connects the right-column form to Supabase Auth using a secure Server Action with cookie-based session and route protection.
Create app/actions/login.ts as a Server Action with 'use server' at the top. Import createServerClient from @supabase/ssr and create a Supabase client using request cookies. Call supabase.auth.signInWithPassword({ email, password }) and set the session via the response cookies. Return { error: error.message } on failure. In the right-column form client component, call the Server Action on submit and display the returned error string in a shadcn/ui Alert under the form. Add middleware.ts using createServerClient to redirect unauthenticated users from /dashboard to /login.Animate the left panel on first load
Adds a polished entry animation to the left panel copy that reinforces the marketing message as users arrive on the page.
Install framer-motion. Wrap the left-column panel content (tagline, feature bullets list) in a motion.div with initial={{ opacity: 0, x: -40 }} animate={{ opacity: 1, x: 0 }} transition={{ duration: 0.6, ease: 'easeOut' }}. Apply motion.li to each feature bullet item with transition={{ delay: index * 0.1 }} so they stagger in sequentially. Keep the hero image non-animated — only the text content fades and slides in.Gotchas when you extend it
The failures people actually hit when they push this template past its defaults — and the exact fix for each.
Left panel image does not fill the column height — two-column layout breaks on smaller viewportsWhy: An img tag without explicit height constraints causes the left column to collapse to the image's natural height; at certain viewport widths the two-column grid breaks into unequal rows.
Fix: Add h-screen to the left column container div. Set the img inside to object-cover with w-full h-full. Hide the left panel on mobile with hidden lg:block.
Add h-screen to the left column container and ensure the hero img uses object-cover with w-full h-full; hide the left panel on mobile with the Tailwind class hidden lg:block.
`NEXTAUTH_URL misconfiguration` on Vercel previewWhy: Manually setting NEXTAUTH_URL to a specific preview URL causes auth to break on every other preview deployment because each branch gets a different URL.
Fix: Remove the manually set NEXTAUTH_URL from the Vercel Dashboard environment variables for preview environments — Vercel auto-populates the correct URL for each deployment.
Remove the manually set NEXTAUTH_URL environment variable from the Vercel Dashboard for preview environments — Vercel auto-populates it correctly for each deployment.
OAuth redirect URI mismatch on preview deploymentsWhy: Vercel preview URLs are dynamic per branch (project-abc123.vercel.app); auth providers only allow the production URL by default, so OAuth fails on previews.
Fix: Register the production domain in your auth provider. For Supabase, add the Vercel deployment URL pattern to Additional Redirect URLs in Auth → URL Configuration.
Add your production Vercel domain to Supabase Auth → URL Configuration → Redirect URLs; for preview testing, also add the specific preview URL to the Additional Redirect URLs list.
Right column overflows on mobile when left panel hidesWhy: When hidden lg:block hides the left panel on mobile, the right column form card may expand with wrong padding, or the Card exceeds the viewport width on small screens.
Fix: Add px-4 sm:px-8 to the right column wrapper and max-w-md mx-auto to the Card to constrain form width on all screen sizes.
Add max-w-md mx-auto to the login Card and px-4 to the right column wrapper for correct mobile layout when the left panel is hidden on small screens.
Template vs. custom — the honest call
A forked template gets you far, fast. Here's where it holds up, and where you'll outgrow it.
The template is enough when
- SaaS login page that needs to sell and authenticate in one screen
- Early-access beta with a compelling left-panel headline driving signups
- Brand-forward login for a consumer app with strong visual identity
- Design reference for a developer briefing showing the desired layout
Go custom when
- You need magic link or passwordless login with email delivery
- SAML SSO for enterprise customers via Okta or Azure Active Directory
- Complex multi-step onboarding flow after sign-in with multiple screens
- Localized login pages for multiple regions with different hero images per locale
RapidDev connects your V0 two-column login to Clerk or Supabase, adds protected dashboard routes, and launches your auth flow — typically in 1–2 days.
Frequently asked questions
Is the Two-Column Login v0 template free?
Yes. Fork it for free with a free V0 account. The generated code is yours with no licensing fees. shadcn/ui, react-hook-form, and Zod are all MIT-licensed.
Can I use this template commercially in a paid SaaS product?
Yes. The generated Next.js code is yours to use commercially without restriction. Don't republish it on v0.dev community as your own original template.
Why does my fork break in preview after I add Supabase?
V0's preview sandbox resolves npm packages through esm.sh. @supabase/ssr sometimes fails there, showing 'Import Error | Failed to load from blob...'. This is a sandbox-only limitation — the auth flow works correctly after deploying to Vercel via Share → Publish to Production.
Why does the left panel image not fill the full column height?
The img tag in the template defaults to its natural image height, causing the left column to collapse below the form height. Fix: add h-screen to the left column container, then set the img to object-cover w-full h-full. Hide the left panel on mobile with hidden lg:block.
How do I make the left panel disappear on mobile?
Add the Tailwind class hidden lg:block to the left column container. On screens smaller than 1024px, the left panel hides and the right-column form takes the full width. Add max-w-md mx-auto to the Card so the form doesn't stretch too wide on tablets.
What's the difference between this and the Two-Column Login Card template?
This template uses a full-height image panel on the left with marketing copy overlaid on it — more visual impact. Two-Column Login Card uses a shadcn/ui Card on the left with a testimonial quote or illustration — more structured, card-based layout. Both share the same right-column form.
Can RapidDev customize the left panel and wire the auth for me?
Yes. RapidDev connects your Two-Column Login to Clerk or Supabase, adds protected dashboard routes, configures OAuth callbacks, and ships your auth flow — typically in 1–2 days.
How do I add a sign-up route that shares the same two-column layout?
Prompt V0: 'Add a /signup page that uses the same two-column layout as the login page but with email, password, and confirm-password fields. Wire it to the same auth provider and add links between /login and /signup.' V0 generates the page and reuses the layout wrapper.
Outgrowing the template?
RapidDev turns v0 prototypes into production apps — real auth, database, and payments — at $13K–$25K.
Book a free consultation30-min call. No commitment.