A production-ready Next.js starter kit for building multi-tenant SaaS applications with dynamic theming powered by Dheme.
- Multi-Tenant Architecture — Subdomain-based tenant routing with a single codebase
- Dynamic Theming — Each tenant gets unique colors via Dheme's AI-powered theme generation
- Server-Side Rendering — Theme CSS injected at request time, zero flash of unstyled content
- shadcn/ui Components — Beautiful, accessible components out of the box
- Dark Mode — Automatic light/dark theme support per tenant
- Live Theme Customizer — Real-time theme preview with Dheme API integration
- TypeScript — Full type safety throughout
- Tailwind CSS v4 — Latest Tailwind with CSS variable theming
npx create-next-multi-tenant my-saas-appThis will:
- Scaffold a new multi-tenant Next.js project
- Optionally connect your Dheme account for AI theme generation
- Set up three demo tenants to showcase different industries
cd my-saas-app
npm run devVisit your tenants:
- http://acme.localhost:3000 — Fintech dashboard
- http://bloom.localhost:3000 — Health CRM
- http://nova.localhost:3000 — Creative ERP
npx create-next-multi-tenant [project-name] [options]
Options:
-y, --yes Skip prompts and use defaults
--no-shadcn Create without shadcn/ui (minimal Tailwind setup)
--no-dheme Skip Dheme OAuth setup
--no-git Skip git initialization
--use-npm Use npm as package manager
--use-yarn Use yarn as package manager
--use-pnpm Use pnpm as package manager
--use-bun Use bun as package manager
--dheme-key <key> Provide Dheme API key directly (skip OAuth)
-h, --help Display help
-V, --version Display versionThe proxy extracts the tenant slug from the subdomain and rewrites the URL:
acme.localhost:3000/dashboard → /acme/dashboard
// src/proxy.ts
export function proxy(request: NextRequest) {
const host = request.headers.get("host");
const subdomain = host?.split(".")[0];
// Rewrite to /[tenant]/path
}Each tenant's theme is resolved server-side in the layout:
// src/app/[tenant]/layout.tsx
export default async function TenantLayout({ params }) {
const theme = await getTheme(params.tenant);
const css = themeToCss(theme);
return (
<>
<style dangerouslySetInnerHTML={{ __html: css }} />
{children}
</>
);
}With a Dheme API key, themes are generated dynamically:
// With API key → Live AI-generated themes
const client = new DhemeClient({ apiKey: process.env.DHEME_API_KEY });
const { data } = await client.generateTheme({
theme: "#2563EB",
radius: 0.5,
});
// Without API key → Falls back to mock themessrc/
├── app/
│ ├── [tenant]/
│ │ ├── layout.tsx # Theme injection
│ │ ├── page.tsx # Tenant landing page
│ │ └── demo/page.tsx # Industry-specific demo
│ ├── api/
│ │ └── dheme/
│ │ └── callback/ # OAuth callback handler
│ ├── layout.tsx # Root layout
│ └── globals.css # Tailwind + CSS variables
├── components/
│ ├── demos/ # Industry demo components
│ ├── ui/ # shadcn/ui components
│ ├── tenant-nav.tsx # Navigation with tenant switcher
│ ├── theme-customizer.tsx # Live theme editor
│ └── theme-mode-toggle.tsx
├── config/
│ └── tenants.ts # Tenant configuration
├── lib/
│ ├── tenant/
│ │ └── resolve-tenant.ts
│ └── theme/
│ ├── get-theme.ts # Theme fetching logic
│ ├── mock-themes.ts # Fallback themes
│ └── theme-to-css.ts # CSS generation
├── providers/
│ └── theme-provider.tsx # next-themes wrapper
└── proxy.ts # Subdomain routing
- Add the tenant to
src/config/tenants.ts:
export const TENANTS: TenantConfig[] = [
// ... existing tenants
{
slug: "startup",
name: "Startup Inc",
description: "A fast-growing tech startup",
themeRequest: {
theme: "#F59E0B", // Primary color (HEX)
radius: 0.75, // Border radius (0-2)
},
},
];-
(Optional) Add a mock theme to
src/lib/theme/mock-themes.tsfor offline development. -
Access at
http://startup.localhost:3000
# Server-side: Theme generation via Dheme API
DHEME_API_KEY=your_api_key
# Client-side: Theme customizer widget
NEXT_PUBLIC_DHEME_API_KEY=your_api_key
# Optional: Custom Dheme server (for self-hosted)
DHEME_BASE_URL=https://dheme.com
NEXT_PUBLIC_DHEME_BASE_URL=https://dheme.comThe floating theme customizer lets users preview theme changes in real-time:
- Click the paintbrush button in the bottom-right corner
- If not connected, click "Connect to Dheme" to authenticate
- Adjust primary color, border radius, saturation, and contrast
- Click "Generate Theme" to see live changes
The customizer uses the Dheme API to generate complete color palettes from a single primary color.
- Push your project to GitHub
- Import to Vercel
- Add environment variables in Vercel dashboard
- Configure wildcard domain for subdomains:
- Add
*.yourdomain.comto your domains - Point DNS with a wildcard CNAME record
- Add
The app works on any platform that supports Next.js. Ensure:
- Wildcard subdomain routing is configured
- Environment variables are set
- Node.js 18+ is available
| Technology | Version | Purpose |
|---|---|---|
| Next.js | 15+ | React framework with App Router |
| React | 19 | UI library |
| Tailwind CSS | v4 | Styling with CSS variables |
| shadcn/ui | latest | UI component library |
| @dheme/sdk | ^1.1.0 | Theme generation API |
| next-themes | 0.4+ | Dark mode support |
| TypeScript | 5.x | Type safety |
Contributions are welcome! Please read our contributing guidelines before submitting a PR.
- Fork the repository
- Create your feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'Add amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
MIT License - see LICENSE for details.
- Dheme — AI-powered theme generation
- shadcn/ui — UI components
- Next.js Documentation
- Tailwind CSS
Built with Dheme — Intelligent Theme Generation