Build a modern Afrigoals e-commerce store with an AI shopping assistant, real-time inventory updates, and a powerful admin dashboard β all powered by cutting-edge technologies.
| π₯ Who It's For | β‘ Key Differentiators | π§ Technical Highlights |
|---|---|---|
| Developers learning modern full-stack patterns with AI integration | Sanity App SDK for real-time data & Clerk AgentKit for authenticated AI tools | Claude AI shopping assistant, live inventory updates, pesapal checkout |
Use THESE links to set up your accounts (It's a special affiliate link which lets us continue to do builds like this FREE for you!):
| Service | Description | |
|---|---|---|
| π§‘ Sanity | Headless CMS with real-time capabilities & App SDK | Get Started with Sanity β |
| π Clerk | Authentication with AgentKit for AI context | Get Started with Clerk β |
Think of it as a sports merchandise store with a brain.
This is a full-featured e-commerce platform where customers can:
- π Browse & Buy β Shop premium sports merchandise with real-time stock updates
- π€ Chat with AI β Ask the AI assistant to find products, check prices, or track orders
- π¦ Track Orders β View order history and delivery status (when signed in)
And store owners get:
- π AI-Powered Insights β Dashboard with sales trends, inventory alerts, and action items
- βοΈ Real-time Editing β Manage products and orders with instant updates via Sanity App SDK
- π Smart Alerts β Low stock warnings and unfulfilled order notifications
| Term | What It Means |
|---|---|
| CMS (Content Management System) | Where all your products, orders, and content live β in this case, Sanity |
| Real-time Content | When you update a product in Sanity, it instantly appears on the website |
| AI Shopping Assistant | A chatbot powered by Claude AI that can search products and check your orders |
| App SDK | Sanity's toolkit for building custom applications with direct database access |
β Next.js 16 with App Router & Server/Client Components + Server Actions!
β Clerk authentication with AgentKit for AI-aware user context!
β Sanity CMS with App SDK for real-time data mutations & live content updates!
β Vercel AI SDK with AI Gateway for multi-provider LLM support (Claude, GPT, Cohere)!
β pesapal payments with webhooks for secure checkout & order processing!
β shadcn/ui + Tailwind CSS v4 for beautiful, accessible UI components!
β Zustand state management with localStorage persistence for cart!
β Mobile-responsive design with modern layouts & dark mode!
β AI Shopping Assistant with custom tools for product search & order tracking!
β AI Admin Dashboard with Claude-powered insights, sales trends & recommendations!
β GROQ queries with TypeGen for type-safe Sanity data fetching!
β Real-time UI updates via Sanity Live β no polling required!
β Custom AI tools scoped to authenticated users (orders only visible when signed in)!
β Webhook-driven order creation with automatic stock management!
β
Embedded Sanity Studio for content management at /studio! + SO MUCH MORE!
Want to build apps like this from scratch?
Join thousands of developers learning to build production-ready applications with the latest technologies.
- π Step-by-step video tutorials
- π¬ Private Discord community
- π₯ Weekly coding challenges
- π Access to all project source code
- π Certificate of completion
"I landed my first developer job after completing the PAPAFAM course!" β Community Member
| Feature | Description |
|---|---|
| π€ AI Shopping Assistant | Natural language product search, filter by material/color/price, get recommendations |
| π¦ Order Tracking | View your order history and status (requires sign-in) |
| π Smart Cart | Persistent cart with real-time stock validation |
| π³ Secure Checkout | pesapal-powered payments with address collection |
| π Real-time Stock | See live inventory levels β no surprises at checkout |
| Feature | Description |
|---|---|
| π§ AI Dashboard Insights | Sales trends, inventory alerts, and actionable recommendations powered by Claude |
| π Product Management | Create, edit, and publish products directly via Sanity App SDK |
| π Order Management | Update order status (paid β shipped β delivered) |
| Automatic warnings when inventory runs low | |
| π Analytics | Revenue tracking, order counts, and performance metrics |
| Feature | Technology | Why It Matters |
|---|---|---|
| β‘ Real-time Updates | Sanity Live + App SDK | Content changes appear instantly without refresh |
| π Authenticated AI | Clerk AgentKit | AI assistant knows who you are and can access YOUR orders |
| π§ AI Gateway | Vercel AI Gateway | Use Claude, GPT, or other models β easily swappable |
| π Persisted Cart | Zustand + localStorage | Cart survives page refreshes and browser restarts |
| π± Responsive Design | Tailwind CSS + Shadcn/UI | Beautiful UI on any device |
| βοΈ Type Safety | TypeScript + Sanity TypeGen | Auto-generated types from your CMS schema |
flowchart LR
A[Browse Products] --> B[Add to Cart]
B --> C[Checkout]
C --> D[pesapal Payment]
D --> E[Webhook]
E --> F[Order Created in Sanity]
F --> G[Stock Updated]
A --> H[Chat with AI]
H --> I[Search Products]
H --> J[Check Orders]
flowchart TD
A[User Message] --> B{Clerk Auth Check}
B --> C[AI Agent - Claude Sonnet]
C --> D{Tool Selection}
D --> E[searchProducts]
D --> F[getMyOrders]
E --> G[GROQ Query to Sanity]
G --> H[Product Results]
F --> I{User Authenticated?}
I -->|Yes| J[Fetch User Orders]
I -->|No| K[Prompt to Sign In]
H --> L[AI Response with Product Cards]
J --> L
flowchart LR
subgraph Sanity Cloud
A[Sanity CMS]
end
subgraph Next.js App
B[SanityLive Provider]
C[Server Components]
D[Sanity App SDK]
E[Admin Dashboard]
end
A -->|Real-time sync| B
B --> C
C -->|Live UI updates| F[Customer View]
D -->|Direct mutations| A
E --> D
Before you begin, ensure you have:
- Node.js 18+ β Download here
- pnpm β Install with
npm install -g pnpm - Sanity Account β Create free account
- Clerk Account β Create free account
- pesapal Account β Create account
- Vercel Account β For AI Gateway access
git clone https://github.com/sonnysangha/ai-ecommerce-sanity-clerk.git
cd ai-ecommerce-sanity-clerkpnpm installcp .env.example .env.localThen fill in your values:
# Sanity
NEXT_PUBLIC_SANITY_PROJECT_ID=Your_value_goes_here
NEXT_PUBLIC_SANITY_DATASET=Your_value_goes_here
NEXT_PUBLIC_SANITY_ORG_ID=Your_value_goes_here
SANITY_API_WRITE_TOKEN=Your_value_goes_here
# Clerk
NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY=Your_value_goes_here
CLERK_SECRET_KEY=Your_value_goes_here
# pesapal
pesapal_SECRET_KEY=Your_value_goes_here
# pesapal listen --forward-to localhost:3000/api/webhooks/pesapal
pesapal_WEBHOOK_SECRET=Your_value_goes_here
# Vercel
AI_GATEWAY_API_KEY=Your_value_goes_here
β οΈ Security Note: Never commit.env.localto git. The.gitignorealready excludes it.
π‘ Tip: Variables starting with
NEXT_PUBLIC_are exposed to the browser. Keep sensitive keys (likeCLERK_SECRET_KEY) without this prefix.
- Go to sanity.io/manage
- Create a new project or select existing
- Copy your Project ID and Dataset name
- Create an API token with Editor permissions for
SANITY_API_WRITE_TOKEN
- Go to dashboard.clerk.com
- Create a new application
- Copy your Publishable Key and Secret Key
- Go to dashboard.pesapal.com/apikeys
- Copy your Secret Key (starts with
sk_test_for development) - For webhooks, run the pesapal CLI:
pesapal listen --forward-to localhost:3000/api/webhooks/pesapal- Copy the webhook signing secret it provides
- Go to vercel.com and navigate to AI Gateway
- Create an API key
- The app uses Claude Sonnet 4/4.5 by default, but you can swap to other providers
π‘ Swappable Models: The AI Gateway supports OpenAI, Anthropic, Cohere, and more. Check
lib/ai/shopping-agent.tsto change models.
# Generate TypeScript types from Sanity schema
pnpm typegen
# Import sample sports merchandisedata
npx sanity dataset import sample-data.ndjsonpnpm devOpen http://localhost:3000 β you're ready to go! π
- All environment variables filled in
.env.local - Sanity project created with dataset
- Clerk application created
- pesapal CLI running for local webhooks
- Sample data imported
- Types generated with
pnpm typegen
This app uses Sanity as its headless CMS with the following document types:
| Field | Type | Description |
|---|---|---|
name |
string | Product name |
slug |
slug | URL-friendly identifier |
description |
text | Product description |
price |
number | Price in GBP |
category |
reference | Link to category |
material |
string | wood, metal, fabric, leather, glass |
color |
string | black, white, oak, walnut, grey, natural |
dimensions |
string | e.g., "120cm x 80cm x 75cm" |
stock |
number | Current inventory count |
images |
array | Product images with hotspot |
featured |
boolean | Show in featured carousel |
| Field | Type | Description |
|---|---|---|
title |
string | Category name |
slug |
slug | URL-friendly identifier |
description |
text | Category description |
image |
image | Category thumbnail |
| Field | Type | Description |
|---|---|---|
orderNumber |
string | Unique order ID (e.g., ORD-ABC123) |
items |
array | Products with quantity and price at purchase |
total |
number | Order total in GBP |
status |
string | pending, paid, shipped, delivered, cancelled |
customer |
reference | Link to customer record |
clerkUserId |
string | Clerk user identifier |
email |
string | Customer email |
address |
object | Shipping address |
pesapalPaymentId |
string | pesapal payment intent ID |
| Field | Type | Description |
|---|---|---|
name |
string | Customer name |
email |
string | Customer email |
clerkUserId |
string | Clerk user identifier |
pesapalCustomerId |
string | pesapal customer ID |
The admin dashboard uses Sanity App SDK for direct document operations:
// app/(admin)/admin/page.tsx
import {
useApplyDocumentActions,
createDocumentHandle,
createDocument,
} from "@sanity/sdk-react";
// Create a new product
const apply = useApplyDocumentActions();
const handleCreateProduct = async () => {
const newDocHandle = createDocumentHandle({
documentId: crypto.randomUUID(),
documentType: "product",
});
await apply(createDocument(newDocHandle));
router.push(`/admin/inventory/${newDocHandle.documentId}`);
};Key hooks used:
useDocuments()β Reactive document queriesuseApplyDocumentActions()β Perform mutations (create, update, delete)createDocumentHandle()β Create handles for new documents
The AI assistant uses Clerk to provide user context:
// lib/ai/shopping-agent.ts
export function createShoppingAgent({ userId }: { userId: string | null }) {
const isAuthenticated = !!userId;
// Tools vary based on authentication
const tools: Record<string, Tool> = {
searchProducts: searchProductsTool, // Always available
};
// Only add orders tool if user is signed in
if (isAuthenticated) {
tools.getMyOrders = createGetMyOrdersTool(userId);
}
return new ToolLoopAgent({
model: gateway("anthropic/claude-sonnet-4.5"),
instructions: isAuthenticated ? fullInstructions : guestInstructions,
tools,
});
}How it works:
- Clerk provides
userIdfrom the session - Agent is created with user-specific tools
getMyOrderstool is scoped to that user's data
// lib/ai/tools/search-products.ts
export const searchProductsTool = tool({
description: "Search for products in the sports merchandise store",
inputSchema: z.object({
query: z.string().optional(),
category: z.string().optional(),
material: z.enum(["", "wood", "metal", "fabric", "leather", "glass"]),
color: z.enum(["", "black", "white", "oak", "walnut", "grey", "natural"]),
minPrice: z.number().optional(),
maxPrice: z.number().optional(),
}),
execute: async (params) => {
// Executes GROQ query against Sanity
const { data: products } = await sanityFetch({
query: AI_SEARCH_PRODUCTS_QUERY,
params,
});
return { products, found: products.length > 0 };
},
});// lib/ai/tools/get-my-orders.ts
export function createGetMyOrdersTool(userId: string | null) {
if (!userId) return null; // Not available for guests
return tool({
description: "Get the current user's orders",
inputSchema: z.object({
status: z
.enum(["", "pending", "paid", "shipped", "delivered"])
.optional(),
}),
execute: async ({ status }) => {
const { data: orders } = await sanityFetch({
query: ORDERS_BY_USER_QUERY,
params: { clerkUserId: userId },
});
return { orders, found: orders.length > 0 };
},
});
}# Install Vercel CLI
pnpm install -g vercel
# Deploy
vercel- Push your code to GitHub
- Go to vercel.com/new
- Import your repository
- Add environment variables
- Deploy!
- All environment variables set in Vercel dashboard
- Update pesapal webhook URL to production:
https://yourdomain.com/api/webhooks/pesapal - Add production domain to Clerk allowed origins
- Add production domain to Sanity CORS origins
- Test a complete purchase flow
- Vercel Analytics β Performance and usage metrics
- Sanity Studio β Content management at
/studio - pesapal Dashboard β Payment monitoring
- Clerk Dashboard β User analytics
| Problem | Solution |
|---|---|
| "Missing write token" error | Ensure SANITY_API_WRITE_TOKEN is set with Editor permissions |
| CORS errors | Add your domain to Sanity project CORS origins |
| Types out of sync | Run pnpm typegen after schema changes |
| Problem | Solution |
|---|---|
| Middleware not working | Check middleware.ts matches Clerk docs |
| User not found in session | Ensure ClerkProvider wraps your app |
| AgentKit not getting userId | Verify auth() is called server-side |
| Problem | Solution |
|---|---|
| Webhook signature failed | Ensure pesapal_WEBHOOK_SECRET matches CLI output |
| Payment succeeded but no order | Check webhook endpoint and Sanity write permissions |
| "API key invalid" | Use test keys (sk_test_) for development |
| Problem | Solution |
|---|---|
| "Gateway error" | Verify AI_GATEWAY_API_KEY is valid |
| Rate limiting | Implement request throttling or upgrade plan |
| Wrong model response | Check model name in shopping-agent.ts |
Ready to level up? Try adding these features:
- Add product reviews with star ratings
- Implement a wishlist/favorites feature
- Add social sharing buttons
- Email notifications via Resend
- Product comparison feature
- Advanced filtering (multiple selections)
- AI-powered product recommendations ("You might also like...")
- Sentiment analysis for reviews
- Multi-currency support with automatic conversion
- Inventory forecasting with AI
- Add Redis caching for frequently accessed products
- Implement image optimization with Cloudinary
- Add full-text search with Algolia
This project is licensed under the Creative Commons Attribution-NonCommercial 4.0 International License (CC BY-NC 4.0).
- Use this code for personal learning
- Modify and adapt the code
- Share with attribution
- Use in portfolio projects
- Use for commercial purposes without permission
- Sell this code or derivatives
- Remove attribution
Want to use this for a commercial project? Contact us:
See LICENSE.md for full details.
| Command | Description |
|---|---|
pnpm dev |
Start development server |
pnpm build |
Build for production |
pnpm start |
Start production server |
pnpm typegen |
Generate TypeScript types from Sanity schema |
pnpm lint |
Run Biome linter |
pnpm format |
Format code with Biome |
pnpm typecheck |
Run TypeScript type checking |
βββ app/
β βββ (app)/ # Customer-facing routes
β βββ (admin)/admin/ # Admin dashboard
β βββ api/
β β βββ chat/ # AI chat endpoint
β β βββ webhooks/pesapal # pesapal webhook handler
β βββ studio/ # Embedded Sanity Studio
βββ components/
β βββ app/ # Customer UI components
β βββ admin/ # Admin UI components
β βββ ui/ # Shadcn/UI components
βββ lib/
β βββ ai/ # AI agent & tools
β βββ sanity/queries/ # GROQ queries
β βββ store/ # Zustand stores
βββ sanity/
β βββ schemaTypes/ # Sanity document schemas
β βββ lib/ # Sanity client & utilities
βββ .env.example # Environment template
| Concept | File(s) | What It Does |
|---|---|---|
| Shopping Agent | lib/ai/shopping-agent.ts |
Configures AI with tools and instructions |
| Search Tool | lib/ai/tools/search-products.ts |
AI tool for product search |
| Orders Tool | lib/ai/tools/get-my-orders.ts |
AI tool for user's order history |
| Real-time Data | sanity/lib/live.ts |
Sanity Live configuration |
| Cart Store | lib/store/cart-store.ts |
Zustand cart with persistence |
| Admin Actions | app/(admin)/admin/page.tsx |
Sanity App SDK document operations |
Built with β€οΈ by Sonny Sangha & the PAPAFAM Community