AI-powered weekly meal planning that feels like a beautiful recipe journal β not a clinical SaaS dashboard.
Plan your week, get a smart consolidated grocery list, and see your nutrition at a glance. No sign-up required to start.
- No sign-up wall β start planning immediately as a guest; results are fully usable without an account
- AI generation β one click produces a full week of meals, a nutrition breakdown, and a consolidated grocery list (via Claude or GPT-4o)
- Duplicate meals & quick-add β copy any meal to another slot; add Leftovers in one hover click
- Guest β account import β sign up after planning and your week is automatically saved
- Dietary preferences β set vegetarian, vegan, gluten-free, dairy-free, low-carb, or high-protein; passed to the AI on every generation
- Saved recipes β bookmark meals with tag filtering (4 free, Pro unlimited)
- Subscriptions β embedded Stripe checkout (no redirect); monthly ($4.99) and annual ($49) Pro plans
- Mobile-friendly β responsive grid; iOS & Android install-to-home-screen prompt
| Layer | Choice |
|---|---|
| Framework | Next.js 16 (App Router, Server Actions, Turbopack) |
| Styling | Tailwind CSS v4 with custom design tokens |
| Auth | Clerk (optional β only required to persist data) |
| Database | Neon (PostgreSQL) + Drizzle ORM |
| AI | Anthropic Claude (claude-sonnet-4-6) or OpenAI (gpt-4o) |
| Payments | Stripe (embedded Elements β no hosted redirect) |
| Toasts | Sonner |
| Icons | Lucide React |
| Validation | Zod |
- Node.js 20.9+ (recommend 22 via
nvm use 22) - A Neon database
- An Anthropic or OpenAI API key
- A Clerk app (or use keyless dev mode β Clerk starts automatically without keys in local dev)
- A Stripe account (test mode is fine for local dev)
git clone https://github.com/jesshas/shef.git
cd shef
npm installcp .env.local.example .env.localEdit .env.local:
| Variable | Required | Description |
|---|---|---|
DATABASE_URL |
Yes | Neon connection string (postgres://...) |
NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY |
No* | Clerk publishable key |
CLERK_SECRET_KEY |
No* | Clerk secret key |
CLERK_WEBHOOK_SECRET |
No* | Clerk webhook signing secret |
AI_PROVIDER |
No | anthropic (default) or openai |
ANTHROPIC_API_KEY |
No** | Required if AI_PROVIDER=anthropic |
OPENAI_API_KEY |
No** | Required if AI_PROVIDER=openai |
OPENAI_MODEL |
No | Override OpenAI model (default: gpt-4o) |
ANTHROPIC_MODEL |
No | Override Claude model (default: claude-sonnet-4-6) |
NEXT_PUBLIC_APP_URL |
Yes | Full origin URL e.g. http://localhost:3000 |
STRIPE_SECRET_KEY |
Yes*** | Stripe secret key (sk_test_... or sk_live_...) |
NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY |
Yes*** | Stripe publishable key (pk_test_...) |
STRIPE_WEBHOOK_SECRET |
Yes*** | Stripe webhook signing secret (whsec_...) |
STRIPE_MONTHLY_PRICE_ID |
Yes*** | Stripe Price ID for the $4.99/month plan |
STRIPE_ANNUAL_PRICE_ID |
Yes*** | Stripe Price ID for the $49/year plan |
* Clerk runs in keyless dev mode locally if omitted β auth is fully functional without API keys in development.
** AI generation returns a friendly error toast if no key is configured.
*** Required for subscriptions to work. Payments are disabled if omitted.
npx drizzle-kit pushnvm use 22 # ensure Node 20.9+
npm run devOpen http://localhost:3000.
Switch between Anthropic and OpenAI with one env var:
# Default β Anthropic Claude
AI_PROVIDER=anthropic
ANTHROPIC_API_KEY=sk-ant-...
# OpenAI
AI_PROVIDER=openai
OPENAI_API_KEY=sk-...
OPENAI_MODEL=gpt-4o-mini # optional, defaults to gpt-4oBoth providers run nutrition and grocery list generation in parallel for speed.
npx drizzle-kit push # Push schema directly (dev)
npx drizzle-kit generate # Generate migration files
npx drizzle-kit migrate # Run pending migrations
npx drizzle-kit studio # Open Drizzle Studio UI| Route | Access | Description |
|---|---|---|
/ |
Public | Landing page |
/plan/new |
Public | Weekly meal planner (guests welcome) |
/plan/[weekId] |
Auth | Saved week view |
/dashboard |
Auth | User home β stats and past weeks |
/recipes |
Auth | Saved recipe bookmarks |
/settings |
Auth | Account profile + dietary preferences + billing |
/pricing |
Public | Plan comparison β Free vs Pro |
/upgrade |
Auth | Embedded Stripe checkout (?plan=monthly or ?plan=annual) |
In your Clerk dashboard, create a webhook pointing to:
https://your-domain.com/api/webhooks/clerk
Subscribe to: user.created, user.updated, user.deleted
Copy the signing secret into CLERK_WEBHOOK_SECRET in your production environment.
In the Stripe Dashboard, create one product called "Shef Pro" with two recurring prices:
| Price | Amount | Interval |
|---|---|---|
| Monthly | $4.99 | Monthly |
| Annual | $49.00 | Yearly |
Copy each Price ID (price_...) into STRIPE_MONTHLY_PRICE_ID and STRIPE_ANNUAL_PRICE_ID.
In production, add a webhook endpoint in your Stripe dashboard pointing to:
https://your-domain.com/api/webhooks/stripe
Subscribe to:
invoice.paidβ grants Pro access after successful paymentcustomer.subscription.deletedβ revokes Pro access on cancellationinvoice.payment_failedβ logged for observability; Stripe retries automatically
Copy the signing secret into STRIPE_WEBHOOK_SECRET.
Install the Stripe CLI and forward events to your local server:
stripe listen --forward-to localhost:3000/api/webhooks/stripeThe CLI prints a webhook signing secret β use that as STRIPE_WEBHOOK_SECRET in .env.local while developing.
Use Stripe's test card 4242 4242 4242 4242 (any future expiry, any CVC) to complete a test payment.
Enable the Customer Portal in your Stripe dashboard so users can manage and cancel their subscriptions from the Settings page.
app/
page.tsx # Landing page
plan/new/page.tsx # Planner (guests + signed-in)
plan/[weekId]/page.tsx # Saved week view
(auth)/dashboard/ # Dashboard
(auth)/recipes/ # Saved recipes
(auth)/settings/ # Settings + billing
pricing/page.tsx # Pricing page
upgrade/page.tsx # Embedded Stripe checkout
api/webhooks/clerk/route.ts # Clerk β Neon user sync
api/webhooks/stripe/route.ts # Stripe β plan upgrade/downgrade
api/stripe/create-subscription/route.ts # Creates subscription + returns clientSecret
components/
ui/ # Button, Card, SlideOver, Badge, Input, Accordion
meal-grid/ # WeekGrid, MealCell, MealSlideOver
results/ # NutritionSummary, GroceryList, DayByDayBreakdown
recipes/ # RecipeCard, SaveRecipePanel, UpgradePrompt
layout/ # Navbar, Footer, MobileInstallBanner
lib/
db/ # Drizzle schema + Neon client
ai/ # AI generation (Anthropic + OpenAI)
actions/ # Server actions (billing portal)
stripe/ # Stripe client singleton
guest/ # localStorage helpers
utils/ # Macros, week helpers, limit checks
validations/ # Zod schemas
hooks/
useWeekPlan.ts # Meal state (guest + signed-in)
useMealSlideOver.ts # Slide-over open/close
useGuestPlan.ts # Auto-import guest plan on sign-in
vercel deploySet all environment variables in Vercel β Project Settings β Environment Variables.
Make sure DATABASE_URL ends with ?sslmode=require for Neon.
.env.localand.clerk/are gitignored and will never be committed- No API keys or secrets are hardcoded anywhere in the source
- See
.env.local.examplefor a full list of required variables with placeholder values only
MIT