shadcn/ui ?????????????????????
shadcn/ui ???????????? component library ?????????????????? React ???????????????????????????????????? library ?????????????????? ???????????????????????? install ???????????? npm package shadcn/ui ????????? copy component source code ???????????? project ?????????????????? ??????????????? customize ????????? 100% ?????????????????? lock-in ????????? library version ????????????????????? Radix UI (headless components) ????????? Tailwind CSS
?????????????????? SaaS Architecture shadcn/ui ??????????????????????????????????????? Fully customizable ???????????? design system ?????????????????? brand ?????????, Tree-shakeable ???????????????????????? components ?????????????????????????????? bundle size ????????????, Accessible Radix UI ????????? accessibility ??????????????????????????????????????????????????? (WCAG 2.1), TypeScript ????????? component ?????? type safety, Dark mode built-in ?????????????????? light/dark theme
SaaS (Software as a Service) applications ????????????????????? UI components ???????????????????????? Dashboard, Data tables, Forms, Charts, Navigation, Settings panels shadcn/ui ??????????????? component ?????????????????????????????? ??????????????? blocks (pre-built layouts) ?????????????????? SaaS ??????????????????????????????????????????
????????????????????? SaaS Project ???????????? shadcn/ui
Setup Next.js SaaS project
# === shadcn/ui SaaS Project Setup ===
# 1. Create Next.js App
npx create-next-app@latest my-saas --typescript --tailwind --eslint --app --src-dir
cd my-saas
# 2. Initialize shadcn/ui
npx shadcn-ui@latest init
# Configuration options:
# Style: Default
# Base color: Slate
# CSS variables: Yes
# Tailwind config: tailwind.config.ts
# Components alias: @/components
# Utils alias: @/lib/utils
# 3. Install Essential SaaS Components
npx shadcn-ui@latest add button card input label
npx shadcn-ui@latest add dialog dropdown-menu avatar
npx shadcn-ui@latest add table tabs badge separator
npx shadcn-ui@latest add form select textarea checkbox
npx shadcn-ui@latest add sheet sidebar navigation-menu
npx shadcn-ui@latest add chart toast sonner
npx shadcn-ui@latest add command popover calendar
# 4. Install Additional Dependencies
npm install next-auth @prisma/client prisma
npm install stripe @stripe/stripe-js
npm install zustand @tanstack/react-query
npm install lucide-react date-fns zod
# 5. Project Structure
cat > project-structure.txt << 'EOF'
src/
app/
(auth)/
login/page.tsx
register/page.tsx
(dashboard)/
layout.tsx
page.tsx
settings/page.tsx
billing/page.tsx
api/
auth/[...nextauth]/route.ts
stripe/webhook/route.ts
components/
ui/ # shadcn/ui components
dashboard/ # Dashboard-specific components
forms/ # Form components
layout/ # Layout components
lib/
utils.ts
prisma.ts
stripe.ts
auth.ts
hooks/
use-subscription.ts
use-user.ts
EOF
# 6. Tailwind Config for SaaS Theme
cat > tailwind.config.ts << 'EOF'
import type { Config } from "tailwindcss"
const config: Config = {
darkMode: ["class"],
content: ["./src/**/*.{ts,tsx}"],
theme: {
extend: {
colors: {
border: "hsl(var(--border))",
background: "hsl(var(--background))",
foreground: "hsl(var(--foreground))",
primary: {
DEFAULT: "hsl(var(--primary))",
foreground: "hsl(var(--primary-foreground))",
},
muted: {
DEFAULT: "hsl(var(--muted))",
foreground: "hsl(var(--muted-foreground))",
},
},
},
},
plugins: [require("tailwindcss-animate")],
}
export default config
EOF
echo "SaaS project initialized"
??????????????? Dashboard Components
SaaS Dashboard ???????????? shadcn/ui
// === SaaS Dashboard Components ===
// File: src/components/dashboard/stats-cards.tsx
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"
import { ArrowUpRight, ArrowDownRight, Users, DollarSign, Activity, TrendingUp } from "lucide-react"
interface StatCardProps {
title: string
value: string
change: number
icon: React.ReactNode
}
function StatCard({ title, value, change, icon }: StatCardProps) {
const isPositive = change >= 0
return (
<Card>
<CardHeader className="flex flex-row items-center justify-between pb-2">
<CardTitle className="text-sm font-medium text-muted-foreground">
{title}
</CardTitle>
{icon}
</CardHeader>
<CardContent>
<div className="text-2xl font-bold">{value}</div>
<div className={`flex items-center text-xs `}>
{isPositive ? <ArrowUpRight className="h-4 w-4" /> : <ArrowDownRight className="h-4 w-4" />}
{Math.abs(change)}% from last month
</div>
</CardContent>
</Card>
)
}
export function DashboardStats() {
return (
<div className="grid gap-4 md:grid-cols-2 lg:grid-cols-4">
<StatCard title="Total Revenue" value="$45,231" change={20.1} icon={<DollarSign className="h-4 w-4 text-muted-foreground" />} />
<StatCard title="Subscriptions" value="2,350" change={10.5} icon={<Users className="h-4 w-4 text-muted-foreground" />} />
<StatCard title="Active Users" value="12,234" change={-2.3} icon={<Activity className="h-4 w-4 text-muted-foreground" />} />
<StatCard title="Growth Rate" value="15.2%" change={4.1} icon={<TrendingUp className="h-4 w-4 text-muted-foreground" />} />
</div>
)
}
// File: src/components/dashboard/sidebar-nav.tsx
import { cn } from "@/lib/utils"
import { Button } from "@/components/ui/button"
import { LayoutDashboard, Users, Settings, CreditCard, BarChart3, FileText } from "lucide-react"
const navItems = [
{ title: "Dashboard", href: "/dashboard", icon: LayoutDashboard },
{ title: "Customers", href: "/customers", icon: Users },
{ title: "Analytics", href: "/analytics", icon: BarChart3 },
{ title: "Billing", href: "/billing", icon: CreditCard },
{ title: "Documents", href: "/documents", icon: FileText },
{ title: "Settings", href: "/settings", icon: Settings },
]
export function SidebarNav({ currentPath }: { currentPath: string }) {
return (
<nav className="flex flex-col gap-1 p-4">
{navItems.map((item) => (
<Button key={item.href} variant={currentPath === item.href ? "secondary" : "ghost"} className="justify-start" asChild>
<a href={item.href}>
<item.icon className="mr-2 h-4 w-4" />
{item.title}
</a>
</Button>
))}
</nav>
)
}
Authentication ????????? Multi-Tenancy
???????????? Auth ????????? Multi-Tenant ?????????????????? SaaS
// === Authentication & Multi-Tenancy ===
// File: src/lib/auth.ts
import NextAuth from "next-auth"
import GoogleProvider from "next-auth/providers/google"
import CredentialsProvider from "next-auth/providers/credentials"
import { PrismaAdapter } from "@auth/prisma-adapter"
import { prisma } from "@/lib/prisma"
export const { handlers, auth, signIn, signOut } = NextAuth({
adapter: PrismaAdapter(prisma),
providers: [
GoogleProvider({
clientId: process.env.GOOGLE_CLIENT_ID!,
clientSecret: process.env.GOOGLE_CLIENT_SECRET!,
}),
CredentialsProvider({
name: "credentials",
credentials: {
email: { label: "Email", type: "email" },
password: { label: "Password", type: "password" },
},
async authorize(credentials) {
// Validate credentials against database
const user = await prisma.user.findUnique({
where: { email: credentials?.email as string },
})
if (!user) return null
// Verify password (use bcrypt in production)
return { id: user.id, email: user.email, name: user.name }
},
}),
],
callbacks: {
async session({ session, token }) {
if (token.sub) {
session.user.id = token.sub
// Add tenant info
const membership = await prisma.teamMember.findFirst({
where: { userId: token.sub },
include: { team: true },
})
if (membership) {
session.user.teamId = membership.team.id
session.user.role = membership.role
}
}
return session
},
},
})
// File: prisma/schema.prisma (Multi-Tenant Schema)
/*
model Team {
id String @id @default(cuid())
name String
slug String @unique
plan String @default("free") // free, pro, enterprise
members TeamMember[]
createdAt DateTime @default(now())
}
model TeamMember {
id String @id @default(cuid())
role String @default("member") // owner, admin, member
user User @relation(fields: [userId], references: [id])
userId String
team Team @relation(fields: [teamId], references: [id])
teamId String
@@unique([userId, teamId])
}
model User {
id String @id @default(cuid())
email String @unique
name String?
teams TeamMember[]
}
*/
Billing ????????? Subscription
Stripe integration ?????????????????? SaaS billing
# === Stripe Billing Integration ===
# 1. Stripe Configuration
cat > src/lib/stripe.ts << 'TSEOF'
import Stripe from "stripe"
export const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!, {
apiVersion: "2024-04-10",
})
export const PLANS = {
free: {
name: "Free",
price: 0,
features: ["5 projects", "1 team member", "1GB storage"],
limits: { projects: 5, members: 1, storage: 1 },
},
pro: {
name: "Pro",
price: 29,
priceId: "price_pro_monthly",
features: ["Unlimited projects", "10 team members", "50GB storage", "Priority support"],
limits: { projects: -1, members: 10, storage: 50 },
},
enterprise: {
name: "Enterprise",
price: 99,
priceId: "price_enterprise_monthly",
features: ["Unlimited everything", "SSO/SAML", "Custom integrations", "SLA 99.9%"],
limits: { projects: -1, members: -1, storage: -1 },
},
} as const
TSEOF
# 2. Pricing Page Component
cat > src/components/billing/pricing-cards.tsx << 'TSXEOF'
import { Button } from "@/components/ui/button"
import { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle } from "@/components/ui/card"
import { Badge } from "@/components/ui/badge"
import { Check } from "lucide-react"
const plans = [
{ name: "Free", price: "$0", period: "/month", features: ["5 projects", "1 team member", "1GB storage"], cta: "Get Started", popular: false },
{ name: "Pro", price: "$29", period: "/month", features: ["Unlimited projects", "10 team members", "50GB storage", "Priority support"], cta: "Start Trial", popular: true },
{ name: "Enterprise", price: "$99", period: "/month", features: ["Unlimited everything", "SSO/SAML", "Custom integrations", "SLA 99.9%", "Dedicated support"], cta: "Contact Sales", popular: false },
]
export function PricingCards() {
return (
{plans.map((plan) => (
{plan.popular && Most Popular }
{plan.name}
{plan.price}
{plan.period}
{plan.features.map((f) => (
-
{f}
))}
))}
)
}
TSXEOF
echo "Billing integration configured"
Performance ????????? Deployment
Optimize ????????? deploy SaaS application
# === Performance & Deployment ===
# 1. Next.js Configuration for SaaS
cat > next.config.ts << 'EOF'
import type { NextConfig } from "next"
const config: NextConfig = {
// Performance
reactStrictMode: true,
poweredByHeader: false,
// Image optimization
images: {
formats: ["image/avif", "image/webp"],
remotePatterns: [
{ protocol: "https", hostname: "*.googleusercontent.com" },
{ protocol: "https", hostname: "avatars.githubusercontent.com" },
],
},
// Headers (Security)
async headers() {
return [
{
source: "/(.*)",
headers: [
{ key: "X-Frame-Options", value: "DENY" },
{ key: "X-Content-Type-Options", value: "nosniff" },
{ key: "Referrer-Policy", value: "strict-origin-when-cross-origin" },
{ key: "Permissions-Policy", value: "camera=(), microphone=(), geolocation=()" },
],
},
]
},
}
export default config
EOF
# 2. Dockerfile
cat > Dockerfile << 'EOF'
FROM node:20-alpine AS base
FROM base AS deps
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
FROM base AS builder
WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules
COPY . .
RUN npx prisma generate
RUN npm run build
FROM base AS runner
WORKDIR /app
ENV NODE_ENV=production
RUN addgroup --system --gid 1001 nodejs
RUN adduser --system --uid 1001 nextjs
COPY --from=builder /app/public ./public
COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./
COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static
USER nextjs
EXPOSE 3000
ENV PORT=3000
CMD ["node", "server.js"]
EOF
# 3. Vercel Deployment
cat > vercel.json << 'EOF'
{
"framework": "nextjs",
"buildCommand": "npx prisma generate && next build",
"env": {
"DATABASE_URL": "@database-url",
"NEXTAUTH_SECRET": "@nextauth-secret",
"STRIPE_SECRET_KEY": "@stripe-secret-key"
}
}
EOF
echo "Deployment configured"
FAQ ??????????????????????????????????????????
Q: shadcn/ui ????????? Material UI ???????????? Chakra UI ???????????????????????????????????????????
A: shadcn/ui ?????????????????? npm package ????????? install ????????????????????? copy-paste components ???????????? project ?????????????????? ??????????????? customize ????????? 100% ??????????????? dependency lock-in, bundle size ????????????????????? (???????????????????????? component ??????????????????????????????), update ????????? break ??????????????? code ?????????????????? project Material UI (MUI) ???????????? npm package ????????????????????????????????????????????????????????? ?????? components ?????????????????? ????????????????????? bundle size ???????????? customize ????????? (theming complex) Chakra UI ?????? middle ground ????????????????????? MUI ????????? shadcn/ui ???????????????????????? MUI ?????????????????? lock-in ????????? package ?????????????????? SaaS ??????????????? shadcn/ui ??????????????? customizable ?????????????????? bundle ???????????? ????????? design ????????? modern
Q: Multi-Tenancy ?????? SaaS ????????????????????????????????????????
A: ?????? 3 ?????????????????????????????? Shared Database, Shared Schema ????????? tenant ????????? database ??????????????? ????????????????????? tenantId column ????????????????????? ????????????????????? ???????????????????????????????????? data isolation ????????? query ?????????????????? WHERE tenantId = xxx Shared Database, Separate Schema ????????? schema ????????? tenant ?????? database ??????????????? isolation ?????????????????? migration ??????????????????????????? schema Separate Database 1 database ????????? tenant isolation ???????????????????????? ??????????????????????????? ??????????????? enterprise ?????????????????? SaaS ???????????????????????? ??????????????????????????? (shared database, tenantId column) ???????????????????????????????????? ?????????????????????????????? ????????????????????? enterprise customers ???????????????????????????????????? separate database
Q: SaaS ?????????????????? Stripe ???????????? Paddle ?????????????????? billing?
A: Stripe ???????????? payment processor ?????????????????????????????? tax ????????? (????????????????????? Stripe Tax) ?????? API ????????????????????????????????? flexible ????????? ???????????????????????????????????? 2.9% + 30 cents ????????????????????????????????????????????? control ????????????????????? Paddle ???????????? Merchant of Record ?????????????????? tax, compliance, invoicing ?????????????????????????????? ???????????????????????? Stripe ????????? ???????????????????????????????????? 5% + 50 cents (?????????????????????) ??????????????????????????????????????????????????????????????? tax ???????????????????????? ????????? Stripe ??????????????? ecosystem ???????????????????????? documentation ?????? ????????????????????????????????????????????????????????? ?????????????????? globally ???????????????????????????????????????????????? VAT/GST ????????? Paddle ?????????????????????????????????
Q: SaaS architecture ?????????????????? monolith ???????????? microservices?
A: ?????????????????? startup/early-stage SaaS ????????? Modular Monolith (Next.js full-stack) ?????????????????? ??????????????????????????? develop, deploy ????????????, debug ????????????, ????????? infrastructure ?????????, ????????????????????? (1-5 ??????) ??????????????????????????? ??????????????? scale (50K+ users, 10+ developers) ???????????? extract ???????????? microservices ???????????????????????? ???????????????????????? services ????????????????????? scale ????????? (???????????? billing, notifications, file processing) Next.js + shadcn/ui + Prisma + Stripe ???????????? stack ????????????????????????????????? SaaS monolith ??????????????? MVP ???????????????????????? 2-4 ????????????????????? ???????????????????????? optimize
