Most .cursorrules files are generic. "Use TypeScript." "Prefer functional components." "Write clean code."
That's fine for a tutorial project. But when you're building something real, like an e-commerce app with payments, inventory, and SEO requirements, Cursor still doesn't know about Stripe webhook verification, cart race conditions, or product page metadata.
I put together a .cursorrules file specifically for Next.js e-commerce projects. Key rules were tested with Cursor CLI to confirm they actually change the output. The rest are documented best practices that solve real e-commerce problems.
Rule 1: Server Components for Product Pages
The problem: Cursor puts 'use client' on everything. Product pages don't need it. They're read-heavy, SEO-critical, and benefit from server rendering.
The rule:
When building e-commerce product pages, category pages, or search results:
- Default to React Server Components
- Only use 'use client' for interactive elements: cart buttons, quantity selectors, wishlist toggles, search filters
- Product data fetching happens in Server Components, never in useEffect
What Cursor generates with this rule:
The main page is an async Server Component that fetches products directly:
// app/products/page.tsx — Server Component (no 'use client')
export default async function ProductsPage() {
const products = await getProducts();
return (
<div className="grid grid-cols-3 gap-4">
{products.map(product => (
<ProductCard key={product.id} product={product} />
))}
</div>
);
}
The only client component is the interactive button:
// components/AddToCartButton.tsx
'use client';
export function AddToCartButton({ productId }: { productId: string }) {
// cart interaction logic here
}
Without this rule, Cursor tends to make the entire page a client component with useEffect for data fetching. With it, you get proper server rendering where it matters and client interactivity only where you need it.
Rule 2: Stripe Stays on the Server
The problem: Cursor will put Stripe API calls in client components if you're not specific. That means secret keys in the browser bundle.
The rule:
For payment processing with Stripe:
- Never import stripe in client components for server-side operations
- All payment intent creation must happen in Server Actions or API routes
- Always verify webhook signatures using stripe.webhooks.constructEvent()
- Include idempotency keys in payment creation calls
What Cursor generates with this rule:
Webhook handler with proper signature verification:
// app/api/webhooks/stripe/route.ts
import Stripe from 'stripe';
const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!);
export async function POST(request: Request) {
const body = await request.text();
const sig = request.headers.get('stripe-signature')!;
const event = stripe.webhooks.constructEvent(
body,
sig,
process.env.STRIPE_WEBHOOK_SECRET!
);
// Handle events...
}
Without this rule, Cursor sometimes creates checkout flows that import Stripe helpers directly in client components. With it, all payment logic stays server-side.
Rule 3: Prices in Cents, Display with Intl
The problem: Floating point math and money don't mix. $19.99 + $5.00 might equal $24.989999999999998. Cursor doesn't think about this unless you tell it.
The rule:
For all monetary values:
- Store and calculate prices as integers (cents)
- Never use floating point for money calculations
- Display prices using Intl.NumberFormat with the correct currency
- Stripe expects amounts in cents — no conversion needed when stored correctly
Why this matters: This prevents an entire class of rounding bugs. When your cart calculates totals, discounts, and tax, integer math gives you exact results. The Intl.NumberFormat API handles locale-specific formatting (commas, decimal points, currency symbols) so you don't have to.
// Good: integer math + formatted display
const priceInCents = 1999;
const formatted = new Intl.NumberFormat('en-US', {
style: 'currency',
currency: 'USD',
}).format(priceInCents / 100);
// "$19.99" — always correct
Rule 4: Route Groups for Clean Architecture
The rule:
Organize e-commerce routes using Next.js route groups:
- (shop) for browsing: products, categories, search
- (checkout) for purchase flow: cart, shipping, payment, confirmation
- (account) for user: profile, orders, wishlist
Each group gets its own layout. Don't nest checkout UI inside the shop layout.
Why: Without this, Cursor dumps everything into a flat route structure. Route groups give you separate layouts (the shop has a product sidebar, checkout has a progress stepper, account has a settings nav) without affecting URLs.
Rule 5: Inventory Checks are Server-Side
The rule:
For inventory management:
- Always check inventory server-side before confirming a purchase
- Use database transactions for inventory decrement + order creation
- Show "Low stock" warnings when quantity falls below threshold
- Never trust client-side quantity validation alone
Why: The classic "two people buy the last item" problem. Cursor doesn't think about race conditions unless you explicitly tell it to wrap inventory checks and order creation in a transaction.
Rule 6: SEO Metadata on Every Product Page
The rule:
Every product page must include:
- generateMetadata with title, description, Open Graph image, canonical URL
- JSON-LD structured data (Product schema with price, availability, reviews)
- Category pages need proper pagination metadata
- No client-side-only rendering for any content search engines need to index
Why: E-commerce lives and dies by SEO. Cursor skips metadata by default. This rule makes it generate proper Open Graph tags, structured data, and server-rendered content on every product page.
The Point
Generic .cursorrules tell Cursor to "write good code." Domain-specific rules tell Cursor how your specific type of project should work.
An e-commerce app has different requirements than a SaaS dashboard or a content site. The rules should reflect that.
I'm working on more industry-specific setups like this. If "cursorrules for [your project type]" is something you'd actually use, let me know in the comments what you're building.
📝 Previous articles in this series:
- 5 .cursorrules That Actually Changed Cursor's Output
- How to Write .cursorrules That Actually Work
- I Tested Every Way .cursorrules Can Break
💻 Free collection (33 rules): github.com/nedcodes-ok/cursorrules-collection
If your Cursor setup isn't working the way you expect, I do async setup audits — send your config, get a written report with fixes.
📋 I made a free Cursor Safety Checklist — a pre-flight checklist for AI-assisted coding sessions, based on actual experiments.
Top comments (0)