DEV Community

Ned C
Ned C

Posted on • Edited on

The .cursorrules I'd Actually Use for a Next.js E-Commerce Project

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
Enter fullscreen mode Exit fullscreen mode

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>
  );
}
Enter fullscreen mode Exit fullscreen mode

The only client component is the interactive button:

// components/AddToCartButton.tsx
'use client';
export function AddToCartButton({ productId }: { productId: string }) {
  // cart interaction logic here
}
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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...
}
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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.
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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:

💻 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.

Get it free →

Top comments (0)