An artisan dessert shop e-commerce platform specializing in handcrafted brownies, brookies, cupcakes, and cookies with delivery across Tamil Nadu, India.
- Features
- Tech Stack
- Prerequisites
- Installation
- Environment Variables
- Development
- On-Demand Revalidation with Sanity Webhooks
- Available Scripts
- Project Structure
- Deployment
- License
- Contact
- Menu Management: Browse categorized menu items with detailed information including ingredients, allergens, and pricing
- E-commerce Functionality: Shopping cart, item details, and product categorization
- Content Management: Fully integrated Sanity CMS for managing products, content, and site configuration
- Responsive Design: Mobile-first approach with modern UI components
- Email Templates: Integrated React Email for transactional emails
- Type Safety: Full TypeScript support with auto-generated types from Sanity schema
- Framework: Next.js 16 with App Router
- UI Library: React 19
- CMS: Sanity
- Styling: Tailwind CSS
- Component Library: Radix UI
- State Management: Zustand
- Forms: React Hook Form + Zod
- Email: React Email
- Animations: Motion
- Language: TypeScript
Before you begin, ensure you have the following installed:
- Node.js: v22.x or higher (LTS recommended)
- pnpm: v10.x or higher
You can install pnpm globally using:
npm install -g pnpm@latest
# or using Corepack (recommended)
corepack enable
corepack use pnpm@latest- Clone the repository
git clone https://github.com/kunalkeshan/hzel-brown.git
cd hzel-brown- Install dependencies
pnpm install- Set up environment variables
Copy the .env.sample file to .env.local and fill in your environment variables:
cp .env.sample .env.localSee Environment Variables section for details.
- Set up Sanity
If you haven't already set up a Sanity project, you can initialize one using:
npx sanity@latest initThis will guide you through creating a new Sanity project or connecting to an existing one.
For detailed setup instructions, refer to the next-sanity documentation.
- Generate Sanity types
pnpm run generate:typesCreate a .env.local file in the root directory with the following variables:
# Sanity Configuration
NEXT_PUBLIC_SANITY_PROJECT_ID=your_project_id
NEXT_PUBLIC_SANITY_DATASET=your_dataset_name
# Environment
NODE_ENV=development
# Site Configuration
SITE_URL=http://localhost:3000
# Sanity Webhook Secret (required for on-demand revalidation)
# Generate a strong secret and add the same value to your Sanity webhook configuration
SANITY_WEBHOOK_SECRET=your_webhook_secretNote: For actual values and additional configuration details, refer to the .env.sample file or contact the repository owner.
To start the development server:
pnpm devThis will concurrently start:
- Next.js development server at http://localhost:3000
- Email preview server at http://localhost:3001
You can also run them individually:
# Next.js only
pnpm dev:app
# Email preview only
pnpm dev:emailThe Sanity CMS admin panel is available at:
http://localhost:3000/cms
This application uses on-demand revalidation instead of time-based revalidation for optimal performance and reduced API calls. When content is updated in Sanity CMS, webhooks automatically trigger cache invalidation for affected pages.
- Content Update: You create, update, or delete content in Sanity Studio
- Webhook Trigger: Sanity sends a webhook to your Next.js application
- Cache Invalidation: The webhook handler identifies affected pages and revalidates specific cache tags
- Fresh Content: Users immediately see updated content without waiting for a time-based revalidation interval
This approach provides:
- ✅ Instant updates: Content changes are reflected immediately
- ✅ Reduced API usage: No periodic polling or unnecessary revalidation
- ✅ Granular control: Only affected pages are revalidated
- ✅ Better performance: Cache stays fresh without rebuilding unaffected pages
To enable on-demand revalidation, configure a webhook in your Sanity project:
-
Navigate to Webhook Settings
- Go to sanity.io/manage
- Select your project → API → Webhooks
-
Create a New Webhook
- Name:
Next.js On-Demand Revalidation - URL:
https://your-production-domain.com/api/revalidate - HTTP Method:
POST - Trigger on: ✅ Create, ✅ Update, ✅ Delete
- Name:
-
Configure Filter (GROQ)
_type in ['menuItem', 'menuCategory', 'siteConfig', 'legal', 'faqs']This ensures the webhook only fires for relevant content types.
-
Configure Projection (GROQ)
{ _id, _type, "slug": slug.current, "categorySlug": category->slug.current }This sends just enough data to determine which cache tags to invalidate.
-
Add Secret
- Generate a strong secret (use a password generator)
- Add it to the webhook configuration in Sanity
- Add the same value as
SANITY_WEBHOOK_SECRETin your.env.localand production environment variables
-
Save and Test
- Save the webhook configuration
- Test it by updating any content in Sanity Studio
- Check your deployment logs to verify the webhook is being received
The webhook endpoint (/app/api/revalidate/route.ts) uses Next.js's revalidateTag function to invalidate cache entries based on document type and slug.
Cache Tag Patterns:
The application uses a type-safe tagging system defined in /sanity/lib/cache-tags.ts:
-
Collection-level tags:
collection:{type}- Used to invalidate all documents of a specific typecollection:menuItem- All menu itemscollection:menuCategory- All menu categoriescollection:siteConfig- Site configurationcollection:legal- All legal documentscollection:faqs- All FAQs
-
Document-level tags:
{type}:{slug}- Used to invalidate a specific documentmenuItem:chocolate-brownie- Specific menu itemmenuCategory:brownies- Specific categorylegal:privacy-policy- Specific legal document
Example:
When updating a menu item with slug chocolate-brownie in the brownies category, the webhook will revalidate:
collection:menuItem- All menu pages showing menu itemsmenuItem:chocolate-brownie- The specific item detail pagemenuCategory:brownies- The brownies category page
This type-safe approach ensures:
- Compile-time validation - TypeScript enforces valid tag patterns
- Consistency - Helper functions (
createCollectionTag,createDocumentTag) ensure uniform tag creation - Maintainability - Tags are derived from Sanity schema types automatically
- Sanity Webhooks: sanity.io/docs/webhooks
- Next.js On-Demand Revalidation: nextjs.org/docs/app/guides/incremental-static-regeneration#on-demand-revalidation-with-revalidatetag
- revalidateTag API: nextjs.org/docs/app/api-reference/functions/revalidateTag
| Script | Description |
|---|---|
pnpm dev |
Start both Next.js and Email preview servers concurrently |
pnpm dev:app |
Start Next.js development server with Turbopack |
pnpm dev:email |
Start Email preview server on port 3001 |
pnpm build |
Build the Next.js application for production |
pnpm start |
Start the production server |
pnpm lint |
Run ESLint to check code quality |
pnpm generate:types |
Generate TypeScript types from Sanity schema |
hzel-brown/
├── app/ # Next.js App Router
│ ├── (static)/ # Static pages (menu, cart, contact, etc.)
│ ├── cms/ # Sanity Studio admin interface
│ └── api/ # API routes
│ └── revalidate/ # Webhook endpoint for on-demand revalidation
├── components/ # React components
│ ├── landing/ # Landing page components
│ ├── menu/ # Menu-related components
│ ├── cart/ # Shopping cart components
│ └── ui/ # Reusable UI components
├── sanity/ # Sanity CMS configuration
│ ├── schemaTypes/ # Content type definitions
│ ├── queries/ # GROQ queries
│ └── lib/ # Sanity client and utilities
├── stores/ # Zustand state management
├── hooks/ # Custom React hooks
├── lib/ # Utility functions
├── types/ # TypeScript type definitions
└── public/ # Static assets
This application is designed to be deployed on Vercel.
- Push your code to a Git repository (GitHub, GitLab, or Bitbucket)
- Import your repository to Vercel
- Configure your environment variables in the Vercel dashboard (including
SANITY_WEBHOOK_SECRET) - Deploy!
- Important: After deployment, configure the Sanity webhook with your production URL (see On-Demand Revalidation)
For more information, see the Next.js deployment documentation.
This project is licensed under the GNU General Public License v3.0 - see the LICENSE file for details.
For questions, issues, or contributions, please contact the repository owner or open an issue on GitHub.
Made with ❤️ for artisan dessert lovers