Auth, database, payments, AI chat, background jobs — fully wired.
Clone it. Configure it. Ship it.
One repo. Everything wired. Change
config.tsand you have a different app.
| Feature | What's Inside |
|---|---|
| Auth | Better Auth — GitHub, Google, Discord OAuth, sessions, protected routes |
| Payments | Polar — one-time & subscriptions, webhooks, customer portal |
| AI Chat | Vercel AI SDK — streaming, tool calling, multi-step agents |
| Database | Prisma + PostgreSQL (Neon-ready), typed queries |
| API | tRPC — end-to-end type safety, no REST boilerplate |
| Security | Arcjet — Shield WAF, bot protection, route-level rate limiting |
| UI | Shadcn/ui — accessible, themeable component system |
| UX | Next.js App Router streaming + layout-matching loading skeletons |
| Dashboard Onboarding | Config-driven starter checklist with per-user progress, hide/show state, and automatic completion hooks |
| Landing Page | Hero, Features, Pricing, Comparison, FAQ, Footer — all config-driven |
| Legal | /privacy + /terms — editable from config |
| Config-Driven | One config.ts to rename, reprice, retheme, and rebrand everything |
# 1. Clone
git clone https://github.com/AdityaKodez/gridly.git my-app
cd my-app
npm install
# 2. Environment
cp .env.example .env
# Fill in your keys (see Environment Variables below)
# 3. Database
npx prisma migrate dev
# 4. Run
npm run devOpen localhost:3000 — you're live.
Everything that changes between projects lives in one file — config.ts:
export const appConfig = {
name: "Your App", // app name everywhere
description: "Your tagline",
url: "https://yourapp.com",
theme: "blue", // "orange" | "blue" | "violet" | "rose" | "emerald" | "amber"
radius: "lg", // "sm" | "md" | "lg" | "xl"
arcjet: {
enabled: true,
shield: { enabled: true, mode: "LIVE" },
botProtection: { enabled: true, mode: "LIVE" },
rateLimit: {
api: { enabled: true, mode: "LIVE", max: 100, window: "60s" },
auth: { enabled: true, mode: "LIVE", max: 10, window: "60s" },
ai: { enabled: true, mode: "LIVE", max: 20, window: "1h" },
},
},
};Landing page copy, pricing plans, auth providers, dashboard nav, onboarding, and legal pages — all driven from this file. No hunting through 20 files to rebrand.
Dashboard onboarding is controlled from config.ts under dashboardConfig.onboarding.
export const dashboardConfig = {
onboarding: {
enabled: true,
badgeLabel: "Onboarding",
title: "Quick starter checklist",
description: "Keep setup in one place, then hide it once the app feels familiar.",
hiddenTitle: "Onboarding hidden",
hiddenDescription: "Bring it back whenever you want the starter checklist again.",
hideActionLabel: "Hide for now",
showActionLabel: "Show onboarding",
steps: [
{
id: "learn-layout",
title: "Learn the workspace layout",
contextRoute: "/dashboard",
},
],
},
};What it does:
- Shows a compact onboarding checklist on the dashboard only
- Stores completion and dismiss state per user in the database
- Lets you disable the whole section by setting
dashboardConfig.onboarding.enabledtofalse - Lets you change the onboarding copy and steps from
config.ts
| Variable | Required | Where to Get It |
|---|---|---|
DATABASE_URL |
Yes | Any PostgreSQL — Neon free tier works great |
GOOGLE_GENERATIVE_AI_API_KEY |
Yes | Google AI Studio |
BETTER_AUTH_SECRET |
Yes | Run openssl rand -base64 32 |
BETTER_AUTH_URL |
Yes | Your app URL (http://localhost:3000) |
GITHUB_CLIENT_ID/SECRET |
OAuth | GitHub Developer Settings |
GOOGLE_CLIENT_ID/SECRET |
OAuth | Google Cloud Console |
DISCORD_CLIENT_ID/SECRET |
OAuth | Discord Developer Portal |
POLAR_ACCESS_TOKEN |
Payments | Polar → Settings → API Keys |
POLAR_WEBHOOK_SECRET |
Payments | Polar webhook settings |
ARCJET_KEY |
Security (recommended) | Arcjet Dashboard |
Only enable the OAuth providers you add keys for — the sign-in page adapts automatically.
Arcjet is pre-wired and controlled entirely from config.ts + one env var:
- Set
ARCJET_KEYin.env - Toggle
appConfig.arcjet.enabledto enable/disable globally - Fine-tune
shield,botProtection, andrateLimitinappConfig.arcjet
Current route coverage:
authRulesonPOST /api/auth/[...all]apiRuleson/api/trpc/[trpc]aiRuleson/api/ai/chat
When a rule denies a request, routes return 429 Too many requests.
- Create a product at polar.sh
- Copy the Product ID into
config.ts→plansConfig[n].productId - Add
POLAR_ACCESS_TOKEN+POLAR_WEBHOOK_SECRETto.env - Point webhook to
https://yourdomain.com/api/auth/polar/webhooks
Extend the AI assistant with your own tools in app/api/ai/chat/route.ts:
tools: {
getProjectStats: tool({
description: "Get stats for the current user's projects",
inputSchema: z.object({}),
execute: async () => {
return { totalProjects: 12, activeUsers: 48 };
},
}),
}The model calls tools automatically when relevant. Return plain JSON only.
app/
├── (dashboard)/ # Protected app pages (dashboard, settings, AI chat)
├── (marketing)/ # Public landing page
└── api/ # API routes (AI, auth, tRPC)
features/ # Feature modules (ai, auth, dashboard, landing)
lib/ # Server utilities (auth, db, ai, billing, arcjet)
trpc/ # tRPC routers + init
prisma/ # Schema + migrations
config.ts # Single config for the entire app
types/ # Shared TypeScript types
Recent onboarding files:
features/onboarding/— checklist UI, auto-step helper, and actionslib/onboarding.ts— onboarding snapshot + persistence helpersprisma/schema.prisma—UserOnboardingmodel
For a complete, step-by-step walkthrough of deploying Gridly to Vercel with a Neon Postgres database and all API keys, 👉 read the Deployment Guide.
| Tech | Purpose |
|---|---|
| Next.js 16 | App Router, Server Components, Server Actions |
| TypeScript | Strict mode, end-to-end type safety |
| tRPC | Type-safe API, zero codegen |
| Prisma | Database ORM with migrations |
| Better Auth | OAuth, sessions, protected routes |
| Polar | Payments, subscriptions, webhooks |
| Vercel AI SDK | Streaming AI, tool calling |
| Arcjet | WAF, bot detection, and rate limiting |
| Shadcn/ui | Accessible, customizable components |
If Gridly saved you time, consider buying me a coffee.
MIT — free to use, modify, and ship. See LICENSE.
Made by Aditya
Star this repo if it helped you.
