Loading...
Loading...
Build complete demo projects from scratch. Takes a project description (presentation website, shop, dashboard, SaaS, portfolio, etc.) and scaffolds a full working Next.js + Tailwind CSS 3.4 app ready for Vercel deployment. Supports optional database integration when a DATABASE_URL is provided. Use when the user wants to build a demo, create a project, scaffold an app, prototype something, or spin up a quick site.
npx skill4agent add apetcu/skills demo-builder| Layer | Technology | Version |
|---|---|---|
| Framework | Next.js (App Router) | 14.x |
| Styling | Tailwind CSS | 3.4.19 |
| Language | TypeScript | 5.x |
| Deployment | Vercel | latest |
| Database ORM | Prisma | latest (when DB provided) |
| Auth (optional) | NextAuth.js | 4.x |
| Icons | Lucide React | latest |
npx create-next-app@14 <project-name> \
--typescript \
--tailwind \
--eslint \
--app \
--src-dir \
--import-alias "@/*" \
--no-turbocd <project-name>
npm install tailwindcss@3.4.1 postcss autoprefixertailwind.config.tsimport type { Config } from "tailwindcss";
const config: Config = {
content: [
"./src/pages/**/*.{js,ts,jsx,tsx,mdx}",
"./src/components/**/*.{js,ts,jsx,tsx,mdx}",
"./src/app/**/*.{js,ts,jsx,tsx,mdx}",
],
theme: {
extend: {
colors: {
primary: {
50: "#f0f9ff",
100: "#e0f2fe",
200: "#bae6fd",
300: "#7dd3fc",
400: "#38bdf8",
500: "#0ea5e9",
600: "#0284c7",
700: "#0369a1",
800: "#075985",
900: "#0c4a6e",
950: "#082f49",
},
accent: {
50: "#fdf4ff",
100: "#fae8ff",
200: "#f5d0fe",
300: "#f0abfc",
400: "#e879f9",
500: "#d946ef",
600: "#c026d3",
700: "#a21caf",
800: "#86198f",
900: "#701a75",
950: "#4a044e",
},
},
fontFamily: {
sans: ["var(--font-geist-sans)", "system-ui", "sans-serif"],
mono: ["var(--font-geist-mono)", "monospace"],
},
animation: {
"fade-in": "fadeIn 0.5s ease-out",
"slide-up": "slideUp 0.5s ease-out",
"slide-in-right": "slideInRight 0.3s ease-out",
},
keyframes: {
fadeIn: {
"0%": { opacity: "0" },
"100%": { opacity: "1" },
},
slideUp: {
"0%": { opacity: "0", transform: "translateY(20px)" },
"100%": { opacity: "1", transform: "translateY(0)" },
},
slideInRight: {
"0%": { opacity: "0", transform: "translateX(20px)" },
"100%": { opacity: "1", transform: "translateX(0)" },
},
},
},
},
plugins: [],
};
export default config;src/
app/
layout.tsx # Root layout with fonts, metadata
page.tsx # Home/landing page
globals.css # Tailwind directives + custom styles
favicon.ico
components/
ui/ # Reusable UI primitives
button.tsx
card.tsx
input.tsx
badge.tsx
container.tsx
layout/ # Layout components
navbar.tsx
footer.tsx
sidebar.tsx # (if dashboard-style)
lib/
utils.ts # cn() helper, formatters
constants.ts # Site config, nav links
types/
index.ts # Shared TypeScript types// src/lib/utils.ts
import { clsx, type ClassValue } from "clsx";
import { twMerge } from "tailwind-merge";
export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs));
}npm install clsx tailwind-merge lucide-react// src/app/layout.tsx
import type { Metadata } from "next";
import { Inter } from "next/font/google";
import "./globals.css";
import { Navbar } from "@/components/layout/navbar";
import { Footer } from "@/components/layout/footer";
const inter = Inter({ subsets: ["latin"], variable: "--font-geist-sans" });
export const metadata: Metadata = {
title: "Project Name",
description: "Project description",
};
export default function RootLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
<html lang="en">
<body className={`${inter.variable} font-sans antialiased`}>
<Navbar />
<main className="min-h-screen">{children}</main>
<Footer />
</body>
</html>
);
}/* src/app/globals.css */
@tailwind base;
@tailwind components;
@tailwind utilities;
@layer base {
* {
@apply border-gray-200;
}
body {
@apply bg-white text-gray-900;
}
}
@layer components {
.container-default {
@apply mx-auto max-w-7xl px-4 sm:px-6 lg:px-8;
}
.section-padding {
@apply py-16 sm:py-20 lg:py-24;
}
}npm install prisma @prisma/client
npx prisma init// src/lib/db.ts
import { PrismaClient } from "@prisma/client";
const globalForPrisma = globalThis as unknown as {
prisma: PrismaClient | undefined;
};
export const db = globalForPrisma.prisma ?? new PrismaClient();
if (process.env.NODE_ENV !== "production") globalForPrisma.prisma = db;DATABASE_URL.envDATABASE_URL="<user-provided-url>"model Product {
id String @id @default(cuid())
name String
description String?
price Float
image String?
category String?
inStock Boolean @default(true)
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
}
model Order {
id String @id @default(cuid())
total Float
status String @default("pending")
items OrderItem[]
createdAt DateTime @default(now())
}
model OrderItem {
id String @id @default(cuid())
quantity Int
price Float
productId String
orderId String
order Order @relation(fields: [orderId], references: [id])
}model User {
id String @id @default(cuid())
name String
email String @unique
role String @default("user")
createdAt DateTime @default(now())
}
model Metric {
id String @id @default(cuid())
name String
value Float
category String
date DateTime @default(now())
}npx prisma db push
npx prisma generate// src/app/api/<resource>/route.ts
import { NextResponse } from "next/server";
import { db } from "@/lib/db";
export async function GET() {
const items = await db.<model>.findMany({
orderBy: { createdAt: "desc" },
});
return NextResponse.json(items);
}
export async function POST(request: Request) {
const body = await request.json();
const item = await db.<model>.create({ data: body });
return NextResponse.json(item, { status: 201 });
}vercel.json{
"framework": "nextjs"
}## Environment Variables
| Variable | Required | Description |
|----------|----------|-------------|
| DATABASE_URL | No | Database connection string |next.config.mjs/** @type {import('next').NextConfig} */
const nextConfig = {
images: {
remotePatterns: [
{
protocol: "https",
hostname: "**",
},
],
},
};
export default nextConfig;npm installnpm run devany"use client"npm run buildnpm run devhttp://localhost:3000