All templates
SaaS

AI Content Studio — Next.js AI SaaS Template

AI-powered content generation SaaS template built with Next.js 15, Tailwind v4, shadcn/ui, and Motion. Includes editor, AI panel, dashboard, prompt history, and export UI.

Secure checkout via Gumroad
AI Content Studio — Next.js AI SaaS Template preview

Overview

AI Content Studio is a production-ready Next.js SaaS template designed for makers, indie developers, and agencies who want to launch an AI-powered content generation product fast. It ships with a complete marketing landing page — hero, features, how-it-works, content types, pricing, testimonials, and CTA — alongside four fully built internal app pages: a workspace dashboard, a Notion-style block editor with an integrated AI generation panel, a prompt history browser, and an export and share interface. Every section is dark-mode only, built on an oklch color system with electric violet and amber accents that give it a distinctive, premium feel without looking like every other SaaS template on the market.

The template is engineered with a strict separation between content and UI. A single config.tsx file holds every piece of copy, every nav link, every pricing plan, every testimonial, and every quick action — no hunting through component files to update text. Pair that with a metadata.tsx that covers global OpenGraph tags, per-page static metadata, and a buildMetadata() helper for dynamic routes, and the SEO foundation is ready to go before you write a single line of custom code. The design system lives entirely in globals.css, with reusable gradient and glow variables that cascade through every component automatically.

Under the hood, every interactive element uses components from shadcn/ui — Button, Badge, Tabs, DropdownMenu, Switch, Avatar, Progress, Tooltip, Textarea, and more — so there are no custom primitives to maintain. Animations are handled by Motion (formerly Framer Motion) with a consistent fadeUp pattern, useInView scroll triggers, and AnimatePresence for route and panel transitions. The result is a template that looks polished on day one and is easy to extend, rebrand, or hand off to a client.


Stack

| Layer | Technology | |---|---| | Framework | Next.js 15 (App Router) | | Styling | Tailwind CSS v4 | | Components | shadcn/ui | | Animation | Motion (motion/react) | | Icons | lucide-react | | Language | TypeScript | | Color system | oklch, dark mode only |


Project Structure

├── app/
│   ├── layout.tsx               # Global layout — add Navbar + Footer here
│   ├── page.tsx                 # Landing page — compose all sections here
│   ├── dashboard/
│   │   └── page.tsx             # Workspace dashboard
│   ├── editor/
│   │   └── [id]/
│   │       └── page.tsx         # Notion-style editor + AI panel
│   ├── history/
│   │   └── page.tsx             # Prompt history browser
│   └── export/
│       └── page.tsx             # Export & share UI
│
├── components/
│   ├── layout/
│   │   ├── navbar.tsx           # Fixed navbar with mobile drawer
│   │   └── footer.tsx           # Footer with columns and socials
│   └── sections/                # Landing page sections
│       ├── hero.tsx
│       ├── features.tsx
│       ├── how-it-works.tsx
│       ├── content-types.tsx
│       ├── pricing.tsx
│       ├── testimonials.tsx
│       └── cta.tsx
│
├── config.tsx                   # All site content and copy
├── metadata.tsx                 # Global, per-page, and dynamic metadata
└── app/globals.css              # Design tokens, gradients, glows

Quick Start

1. Install dependencies

npm install
# or
pnpm install

2. Install shadcn/ui components

This template uses the following shadcn/ui components. Add them all before running the dev server:

npx shadcn@latest add button badge input textarea separator tabs
npx shadcn@latest add dropdown-menu tooltip avatar progress switch label

3. Install Motion

npm install motion

4. Run the dev server

npm run dev

Open http://localhost:3000.

5. Build the landing page

Open app/page.tsx and compose the sections:

import { Hero }         from "@/components/sections/hero";
import { Features }     from "@/components/sections/features";
import { HowItWorks }   from "@/components/sections/how-it-works";
import { ContentTypes } from "@/components/sections/content-types";
import { Pricing }      from "@/components/sections/pricing";
import { Testimonials } from "@/components/sections/testimonials";
import { CTA }          from "@/components/sections/cta";

export default function HomePage() {
  return (
    <main>
      <Hero />
      <Features />
      <HowItWorks />
      <ContentTypes />
      <Pricing />
      <Testimonials />
      <CTA />
    </main>
  );
}

6. Add the layout

Open app/layout.tsx and add the global metadata, fonts, Navbar, and Footer:

import type { Metadata } from "next";
import { Geist, Geist_Mono } from "next/font/google";
import { Navbar } from "@/components/layout/navbar";
import { Footer } from "@/components/layout/footer";
import { globalMetadata } from "@/metadata";
import "./globals.css";

const fontSans = Geist({ subsets: ["latin"], variable: "--font-sans" });
const fontMono = Geist_Mono({ subsets: ["latin"], variable: "--font-geist-mono" });

export const metadata: Metadata = globalMetadata;

export default function RootLayout({ children }: { children: React.ReactNode }) {
  return (
    <html lang="en" className="dark">
      <body className={`${fontSans.variable} ${fontMono.variable} antialiased`}>
        <Navbar />
        {children}
        <Footer />
      </body>
    </html>
  );
}

Note: The Navbar and Footer are intentionally excluded from internal app pages (/dashboard, /editor, /history, /export). Use a nested layout inside app/(app)/layout.tsx if you want a separate shell for the app section.


Customization

Branding and copy

All content lives in config.tsx. Update the following objects to rebrand the template:

// Site identity
export const siteConfig = {
  name: "Your Product Name",
  tagline: "Your tagline here.",
  url: "https://yourproduct.com",
  ogImage: "https://yourproduct.com/og.png",
  twitterHandle: "@yourhandle",
  // ...
};

// Hero section
export const heroContent = {
  eyebrow: "Powered by GPT-4o",
  headline: "Your headline\nhere.",
  // ...
};

Every other section — features, pricing plans, testimonials, footer columns — follows the same pattern. Never hardcode copy directly in component files.

Color palette

The entire color system is defined in app/globals.css under :root. To change the primary color, update the oklch value once and every component updates automatically:

:root {
  --primary: oklch(0.62 0.26 290);   /* Electric violet — change this */
  --accent:  oklch(0.78 0.19 68);    /* Amber — change this */
}

To adjust or add reusable gradients and glows, edit the @theme inline block:

@theme inline {
  --background-image-gradient-hero: radial-gradient(...);
  --shadow-glow-primary: 0 0 40px oklch(...);
}

Pricing plans

Edit pricingContent.plans in config.tsx to add, remove, or modify plans. Each plan accepts:

{
  name: string;
  monthlyPrice: number;       // 0 for free tier
  annualPrice: number;
  description: string;
  highlight: boolean;         // true = featured card with glow
  badge?: string;             // e.g. "Most popular"
  cta: { label: string; href: string };
  features: string[];
}

Navigation

Update navLinks and ctaButton in config.tsx:

export const navLinks = [
  { label: "Features", href: "/#features" },
  { label: "Pricing",  href: "/#pricing"  },
  // add more...
];

export const ctaButton = {
  label: "Get started",
  href:  "/dashboard",
};

SEO Setup

Global metadata

metadata.tsx exports globalMetadata — import it directly in your root layout.tsx:

import { globalMetadata } from "@/metadata";
export const metadata: Metadata = globalMetadata;

It covers: title template, description, keywords, robots, openGraph, twitter, icons, and manifest.

Per-page metadata

Each internal page exports its own static metadata from pageMetadata:

// app/dashboard/page.tsx
import { pageMetadata } from "@/metadata";
export const metadata = pageMetadata.dashboard;

Available keys: home, dashboard, editor, history, export.

Dynamic routes

Use buildMetadata() for pages with dynamic data:

// app/editor/[id]/page.tsx
import { buildMetadata } from "@/metadata";

export async function generateMetadata({ params }: { params: { id: string } }) {
  const doc = await getDocument(params.id);

  return buildMetadata({
    title:       doc.title ?? "Untitled document",
    description: doc.excerpt ?? "Edit and generate content with AI Content Studio.",
    slug:        `editor/${params.id}`,
    noIndex:     true,   // keeps private documents out of search
  });
}

OG image

Replace /public/og.png with a 1200 × 630 image. For dynamic OG images per document, use Next.js opengraph-image.tsx in the relevant route segment.


Final Notes

Dark mode only. The template is intentionally single-mode. The oklch palette is tuned specifically for dark backgrounds — adding a light mode would require a complete re-audit of all color variables.

Mock data. The dashboard, editor, history, and export pages use local mock data. Wire them to your real API or database by replacing the mock arrays (HISTORY, RECENT_PROJECTS, DOC) with fetch calls or server components as needed.

No real AI calls. The AI generation panel in /editor simulates generation with a setTimeout. To connect a real model, replace the handleGenerate function with a call to your backend or directly to the Anthropic / OpenAI API:

const res  = await fetch("/api/generate", { method: "POST", body: JSON.stringify({ prompt, tone, type }) });
const data = await res.json();
setResults(data.variants);

Editor is a textarea. The current editor is a styled <Textarea> with a formatting toolbar. For a true block editor, swap it for Tiptap or Novel — both integrate cleanly with this component structure.

Fonts. The template uses Geist and Geist Mono from next/font/google. Swap the font import in layout.tsx and update --font-sans and --font-geist-mono in globals.css to match your brand.

shadcn/ui registry. All component imports use the @/components/ui/ path alias. If your project has a different alias setup, update tsconfig.json and components.json accordingly before adding components.

Tech stack

NextjsTailwindShadcnMotion

Tags

nextjstailwindstudioaisaascontentdashboard

Highlights

Full Next.js 15 source codeAll sections as isolated componentsglobals.css design systemconfig.tsx for easy content editingSEO: metadata, Open Graph, JSON-LDSitemap & robots.txtLifetime updates via GumroadMIT license
$49USD
Secure checkout via Gumroad
Get template