How to Build a Multi-Page SaaS Dashboard in v0 with Shadcn UI, API Routes & Vercel Deployment
How to Build a Multi-Page SaaS Dashboard in v0 with Shadcn UI, API Routes & Vercel Deployment
v0 by Vercel is an AI-powered code generation tool that produces production-ready React components built on Next.js and Shadcn UI. In this guide, you will build a complete multi-page SaaS dashboard from scratch—using v0 prompts for UI generation, Next.js API route handlers for backend logic, and one-click Vercel deployment to go live.
Prerequisites
- A v0.dev account (free tier works for getting started)
- Node.js 18+ and npm installed locally
- A GitHub account
- A Vercel account linked to your GitHub
Step 1: Scaffold the Dashboard Layout in v0
Open v0.dev and enter a detailed prompt to generate your dashboard shell. The more specific your prompt, the better the output.
Prompt in v0:
“Create a SaaS dashboard layout with a collapsible sidebar navigation
containing links for Dashboard, Analytics, Users, Billing, and Settings.
Include a top header bar with a user avatar dropdown, notification bell,
and search input. Use Shadcn UI components: Sheet for mobile sidebar,
DropdownMenu for the avatar, and Input for search. The main content area
should accept children for page routing.”
v0 will generate a fully functional layout component. Click **"Add to Codebase"** or copy the code. The output uses the App Router structure by default.
Step 2: Set Up Your Local Project
Initialize your Next.js project locally and install Shadcn UI:
npx create-next-app@latest saas-dashboard —typescript —tailwind —eslint —app —src-dir
cd saas-dashboard
npx shadcn@latest init
When prompted during Shadcn init, choose your preferred style and color scheme. Then install the specific components your dashboard needs:
npx shadcn@latest add button card table tabs input dropdown-menu sheet avatar badge dialog select separator chart
Paste the layout code from v0 into src/app/layout.tsx or create a dedicated src/components/dashboard-layout.tsx component.
Step 3: Generate Individual Dashboard Pages
Return to v0 and generate each page. Here are effective prompts for key pages:
Analytics Page
Prompt in v0:
“Build an analytics dashboard page with: a row of 4 KPI stat cards
(Total Revenue, Active Users, Conversion Rate, MRR) using Shadcn Card,
a large area chart showing revenue over 12 months using Recharts,
and a data table of recent transactions with columns for ID, Customer,
Amount, Status, and Date. Use Shadcn Table with sortable headers.”
Users Management Page
Prompt in v0:
"Create a user management page with a Shadcn DataTable showing columns:
Name, Email, Role (Admin/Member/Viewer), Status (Active/Inactive), and
Joined Date. Add a search/filter bar at the top, pagination at the bottom,
and an 'Add User' button that opens a Shadcn Dialog with a form."
Save each generated page into the App Router structure:
src/app/(dashboard)/analytics/page.tsx
src/app/(dashboard)/users/page.tsx
src/app/(dashboard)/billing/page.tsx
src/app/(dashboard)/settings/page.tsx
Step 4: Create API Route Handlers
Next.js App Router uses route.ts files for API endpoints. Create handlers that your dashboard pages will consume:
// src/app/api/analytics/route.ts
import { NextResponse } from ‘next/server’;
export async function GET() {
// Replace with your actual database query
const stats = {
totalRevenue: 48250,
activeUsers: 2340,
conversionRate: 3.2,
mrr: 12400,
revenueHistory: [
{ month: ‘Jan’, revenue: 8200 },
{ month: ‘Feb’, revenue: 9100 },
{ month: ‘Mar’, revenue: 12400 },
],
};
return NextResponse.json(stats);
}
// src/app/api/users/route.ts
import { NextRequest, NextResponse } from 'next/server';
export async function GET(request: NextRequest) {
const { searchParams } = new URL(request.url);
const page = parseInt(searchParams.get('page') || '1');
const limit = parseInt(searchParams.get('limit') || '10');
// Replace with your database call
const users = [
{ id: '1', name: 'Jane Cooper', email: 'jane@example.com', role: 'Admin', status: 'Active' },
{ id: '2', name: 'Alex Morgan', email: 'alex@example.com', role: 'Member', status: 'Active' },
];
return NextResponse.json({
users,
pagination: { page, limit, total: 47 },
});
}
export async function POST(request: NextRequest) {
const body = await request.json();
// Validate and save to database
return NextResponse.json({ success: true, user: body }, { status: 201 });
}
Step 5: Connect Frontend to API Routes
Use React hooks or server components to fetch data from your API routes:
// src/app/(dashboard)/analytics/page.tsx
export default async function AnalyticsPage() {
const res = await fetch(${process.env.NEXT_PUBLIC_APP_URL}/api/analytics, {
cache: ‘no-store’,
});
const data = await res.json();
return (
Analytics
<StatCard title=“Total Revenue” value={$${data.totalRevenue.toLocaleString()}} />
<StatCard title=“Conversion Rate” value={${data.conversionRate}%} />
<StatCard title=“MRR” value={$${data.mrr.toLocaleString()}} />
);
}
For environment variables, create a .env.local file:
NEXT_PUBLIC_APP_URL=http://localhost:3000
DATABASE_URL=your_database_connection_string
API_SECRET_KEY=YOUR_API_KEY
Step 6: One-Click Vercel Deployment
Push your project to GitHub, then deploy:
git init
git add .
git commit -m “SaaS dashboard with Shadcn UI”
git remote add origin https://github.com/yourusername/saas-dashboard.git
git push -u origin main
- Go to vercel.com/new
- Import your GitHub repository
- Vercel auto-detects the Next.js framework
- Add your environment variables (
DATABASE_URL,API_SECRET_KEY) in the Vercel dashboard - Click Deploy
Alternatively, use the Vercel CLI:
npm i -g vercel
vercel —prod
Pro Tips for Power Users
- Iterate with v0 Chat: After the initial generation, use v0’s chat to refine components. For example: “Add a dark mode toggle to the header” or “Make the sidebar persist collapsed state in localStorage.”
- Use Route Groups: Wrap your dashboard pages in an
(dashboard)route group with a shared layout so the sidebar and header render once, not per page. - Parallel Routes for Modals: Use Next.js parallel routes (
@modal) so dialogs like “Add User” get their own URL, enabling shareable deep links. - Middleware for Auth: Add a
middleware.tsat your project root to protect all/dashboardroutes before they render. - Streaming with Suspense: Wrap slow data-fetching sections in
with ShadcnSkeletoncomponents as fallbacks for instant perceived performance. - Prompt Chaining: Generate a base component in v0, then immediately follow up: “Now add loading states, error handling, and empty states to this table.”
Troubleshooting Common Errors
| Error | Cause | Solution |
|---|---|---|
Module not found: @/components/ui/button | Shadcn component not installed | Run npx shadcn@latest add button for the missing component |
TypeError: fetch failed in server components | NEXT_PUBLIC_APP_URL not set or incorrect | Verify the env variable in .env.local and Vercel dashboard. On Vercel, use VERCEL_URL dynamically. |
| Sidebar navigation does not update active state | Not using usePathname() hook | Import usePathname from next/navigation and compare against each nav link's href |
| API route returns 405 Method Not Allowed | Wrong HTTP method or file named page.tsx instead of route.ts | Ensure API files are named route.ts and export the correct method (GET, POST, etc.) |
| Hydration mismatch warnings | Client-only code (localStorage, window) running on server | Wrap client-dependent logic in useEffect or add 'use client' directive |
| Vercel build fails on type errors | v0-generated code may have minor type issues | Run npx tsc --noEmit locally before pushing; fix flagged types |
Frequently Asked Questions
Can I use v0 to generate entire multi-page applications or only single components?
v0 generates individual components and pages, not full multi-page apps in a single prompt. The recommended workflow is to generate each page separately—dashboard overview, analytics, users, settings—then assemble them into your Next.js App Router structure locally. Use a shared layout component (also generated via v0) to ensure consistent navigation and styling across all pages. This modular approach actually produces better results because each prompt can be specific and detailed.
How do I add authentication to protect the dashboard routes?
Create a middleware.ts file in your project root that checks for a valid session or JWT token on all /dashboard routes. Popular options include NextAuth.js, Clerk, or Supabase Auth. In your middleware.ts, redirect unauthenticated users to a login page. You can also prompt v0 to generate a login page: “Create a login page with email and password fields, a Google OAuth button, and a link to sign up, using Shadcn Card, Input, Button, and Separator.” Then wire the form to your chosen auth provider’s API.
Is the code generated by v0 production-ready, or does it need significant refactoring?
v0 output is high-quality and uses well-structured React with Shadcn UI and Tailwind CSS, making it a strong starting point. However, for production you should review and adjust several things: replace hardcoded mock data with real API calls, add proper error handling and loading states, implement input validation, verify accessibility (keyboard navigation, ARIA labels), and ensure TypeScript types are strict. The generated code typically needs 15–25% refinement for production use—far less than building from scratch, but not zero.