RuleStack

Configs

Stacks

Compare

Diff

RuleStack

Configs

Stacks

Compare

Diff

Read API

RuleStack

Configs

Stacks

Compare

Diff

Read API

Configs/Cursor rules/tugkanboz/awesome-cursorrules

Cursor rule

example-structures/next-js/.cursor/rules/app-router-patterns.mdc

Next.js 14+ App Router patterns — Server Components, Client Components, Route Handlers, Server Actions, and metadata API

Cursor rules

Quality

65/100

Scores the file, not the repository.

Length

1,273 words

9 headings · 9 code blocks

Repository

18

— · pushed 0 days ago

Last changed

2 days ago

First indexed 2 days ago.
tugkanboz/awesome-cursorrules/example-structures/next-js/.cursor/rules/app-router-patterns.mdcRawGitHub
1---
2description: Next.js 14+ App Router patterns — Server Components, Client Components, Route Handlers, Server Actions, and metadata API
3globs: **/app/**/*.tsx,**/app/**/*.ts,**/app/**/*.jsx,**/app/**/*.js,next.config.*
4alwaysApply: false
5---
6# Next.js App Router Excellence
7 
8## Server Components vs Client Components
9 
10- **Default is Server Component** — every file in `app/` is a Server Component unless marked otherwise
11- Add `"use client"` only at the boundary where you need browser APIs, event handlers, or React state
12- Keep `"use client"` components as leaf nodes; wrap them in Server Components for data fetching
13- Never fetch data inside a Client Component when a Server Component parent can pass it as a prop
14 
15```tsx
16// ✅ Server Component — async, direct DB/API access, no "use client"
17// app/products/page.tsx
18import { db } from '@/lib/db'
19 
20export default async function ProductsPage() {
21 const products = await db.product.findMany({ orderBy: { createdAt: 'desc' } })
22 
23 return (
24 <main>
25 <h1>Products</h1>
26 {products.map((product) => (
27 <ProductCard key={product.id} product={product} />
28 ))}
29 </main>
30 )
31}
32 
33// ✅ Client Component — only for interactivity
34// components/add-to-cart-button.tsx
35'use client'
36 
37import { useState } from 'react'
38 
39interface AddToCartButtonProps {
40 productId: string
41}
42 
43export function AddToCartButton({ productId }: AddToCartButtonProps) {
44 const [loading, setLoading] = useState(false)
45 
46 const handleAdd = async () => {
47 setLoading(true)
48 await addToCart(productId)
49 setLoading(false)
50 }
51 
52 return (
53 <button onClick={handleAdd} disabled={loading}>
54 {loading ? 'Adding…' : 'Add to Cart'}
55 </button>
56 )
57}
58```
59 
60## App Router File Conventions
61 
62```
63app/
64├── layout.tsx # Root layout — wraps all routes, runs once
65├── page.tsx # Route UI rendered at /
66├── loading.tsx # Automatic Suspense boundary for this segment
67├── error.tsx # Error boundary ("use client" required)
68├── not-found.tsx # Rendered by notFound() or unmatched routes
69├── global-error.tsx # Error boundary for root layout
70├── route.ts # API Route Handler (no page.tsx in same segment)
71├── template.tsx # Like layout but re-mounts on navigation
72└── products/
73 ├── page.tsx # Renders at /products
74 ├── [id]/
75 │ └── page.tsx # Renders at /products/:id
76 └── (marketing)/ # Route group — ignored in URL path
77 └── page.tsx
78```
79 
80```tsx
81// ✅ Root layout — must return <html> and <body>
82// app/layout.tsx
83import type { Metadata } from 'next'
84import { Inter } from 'next/font/google'
85 
86const inter = Inter({ subsets: ['latin'] })
87 
88export const metadata: Metadata = {
89 title: { template: '%s | My App', default: 'My App' },
90 description: 'My application description',
91}
92 
93export default function RootLayout({ children }: { children: React.ReactNode }) {
94 return (
95 <html lang="en">
96 <body className={inter.className}>{children}</body>
97 </html>
98 )
99}
100 
101// ✅ Error boundary — must be a Client Component
102// app/products/error.tsx
103'use client'
104 
105export default function Error({
106 error,
107 reset,
108}: {
109 error: Error & { digest?: string }
110 reset: () => void
111}) {
112 return (
113 <div>
114 <h2>Something went wrong</h2>
115 <button onClick={reset}>Try again</button>
116 </div>
117 )
118}
119```
120 
121## Route Handlers
122 
123Use `route.ts` for API endpoints. Export named functions for each HTTP method.
124 
125```typescript
126// ✅ app/api/products/route.ts
127import { NextRequest, NextResponse } from 'next/server'
128import { z } from 'zod'
129import { db } from '@/lib/db'
130 
131export async function GET(request: NextRequest) {
132 const { searchParams } = new URL(request.url)
133 const page = Number(searchParams.get('page') ?? '1')
134 
135 const products = await db.product.findMany({
136 skip: (page - 1) * 20,
137 take: 20,
138 })
139 
140 return NextResponse.json({ products })
141}
142 
143const createProductSchema = z.object({
144 name: z.string().min(1),
145 price: z.number().positive(),
146})
147 
148export async function POST(request: NextRequest) {
149 const body = await request.json()
150 const parsed = createProductSchema.safeParse(body)
151 
152 if (!parsed.success) {
153 return NextResponse.json({ error: parsed.error.flatten() }, { status: 400 })
154 }
155 
156 const product = await db.product.create({ data: parsed.data })
157 return NextResponse.json(product, { status: 201 })
158}
159 
160// ✅ Dynamic route handler: app/api/products/[id]/route.ts
161export async function GET(
162 request: NextRequest,
163 { params }: { params: Promise<{ id: string }> }
164) {
165 const { id } = await params
166 const product = await db.product.findUnique({ where: { id } })
167 
168 if (!product) {
169 return NextResponse.json({ error: 'Not found' }, { status: 404 })
170 }
171 
172 return NextResponse.json(product)
173}
174```
175 
176## Server Actions
177 
178Use Server Actions for mutations triggered from forms or Client Components.
179 
180```typescript
181// ✅ lib/actions/product.ts
182'use server'
183 
184import { revalidatePath } from 'next/cache'
185import { redirect } from 'next/navigation'
186import { z } from 'zod'
187import { db } from '@/lib/db'
188 
189const schema = z.object({
190 name: z.string().min(1, 'Name is required'),
191 price: z.coerce.number().positive('Price must be positive'),
192})
193 
194export async function createProduct(prevState: unknown, formData: FormData) {
195 const parsed = schema.safeParse({
196 name: formData.get('name'),
197 price: formData.get('price'),
198 })
199 
200 if (!parsed.success) {
201 return { errors: parsed.error.flatten().fieldErrors }
202 }
203 
204 await db.product.create({ data: parsed.data })
205 
206 revalidatePath('/products')
207 redirect('/products')
208}
209```
210 
211```tsx
212// ✅ Using Server Action with useActionState
213// app/products/new/page.tsx
214'use client'
215 
216import { useActionState } from 'react'
217import { createProduct } from '@/lib/actions/product'
218 
219export default function NewProductPage() {
220 const [state, action, pending] = useActionState(createProduct, null)
221 
222 return (
223 <form action={action}>
224 <div>
225 <label htmlFor="name">Name</label>
226 <input id="name" name="name" type="text" required />
227 {state?.errors?.name && <p>{state.errors.name[0]}</p>}
228 </div>
229 <div>
230 <label htmlFor="price">Price</label>
231 <input id="price" name="price" type="number" step="0.01" required />
232 {state?.errors?.price && <p>{state.errors.price[0]}</p>}
233 </div>
234 <button type="submit" disabled={pending}>
235 {pending ? 'Creating…' : 'Create Product'}
236 </button>
237 </form>
238 )
239}
240```
241 
242## Metadata API
243 
244```tsx
245// ✅ Static metadata
246// app/about/page.tsx
247import type { Metadata } from 'next'
248 
249export const metadata: Metadata = {
250 title: 'About Us',
251 description: 'Learn more about our company',
252 openGraph: {
253 title: 'About Us',
254 description: 'Learn more about our company',
255 images: ['/og-about.png'],
256 },
257}
258 
259// ✅ Dynamic metadata from route params
260// app/products/[id]/page.tsx
261import type { Metadata } from 'next'
262import { db } from '@/lib/db'
263 
264interface Props {
265 params: Promise<{ id: string }>
266}
267 
268export async function generateMetadata({ params }: Props): Promise<Metadata> {
269 const { id } = await params
270 const product = await db.product.findUnique({ where: { id } })
271 
272 if (!product) return { title: 'Product Not Found' }
273 
274 return {
275 title: product.name,
276 description: product.description,
277 openGraph: { images: [product.imageUrl] },
278 }
279}
280 
281export default async function ProductPage({ params }: Props) {
282 const { id } = await params
283 const product = await db.product.findUnique({ where: { id } })
284 // ...
285}
286```
287 
288## Data Fetching Patterns
289 
290```tsx
291// ✅ Parallel data fetching in Server Components
292export default async function DashboardPage() {
293 const [user, stats, notifications] = await Promise.all([
294 fetchUser(),
295 fetchStats(),
296 fetchNotifications(),
297 ])
298 
299 return <Dashboard user={user} stats={stats} notifications={notifications} />
300}
301 
302// ✅ Streaming with Suspense
303import { Suspense } from 'react'
304 
305export default function Page() {
306 return (
307 <main>
308 <h1>Dashboard</h1>
309 <Suspense fallback={<StatsSkeleton />}>
310 <StatsPanel /> {/* async Server Component */}
311 </Suspense>
312 <Suspense fallback={<FeedSkeleton />}>
313 <ActivityFeed /> {/* async Server Component */}
314 </Suspense>
315 </main>
316 )
317}
318 
319// ✅ fetch() with Next.js cache options
320async function getProduct(id: string) {
321 const res = await fetch(`https://api.example.com/products/${id}`, {
322 next: { revalidate: 3600 }, // ISR: revalidate every hour
323 })
324 
325 if (!res.ok) throw new Error('Failed to fetch product')
326 return res.json()
327}
328 
329// Force dynamic (no cache)
330async function getLivePrice(symbol: string) {
331 const res = await fetch(`https://api.example.com/price/${symbol}`, {
332 cache: 'no-store',
333 })
334 return res.json()
335}
336```
337 
338## Image and Font Optimization
339 
340```tsx
341// ✅ next/image — always specify width/height or fill
342import Image from 'next/image'
343 
344// Fixed dimensions
345<Image src="/hero.png" alt="Hero banner" width={1200} height={600} priority />
346 
347// Fill parent container
348<div className="relative h-64 w-full">
349 <Image src={product.imageUrl} alt={product.name} fill className="object-cover" />
350</div>
351 
352// ✅ next/font — load at module level, not inside components
353import { Inter, Roboto_Mono } from 'next/font/google'
354 
355const inter = Inter({ subsets: ['latin'], variable: '--font-inter' })
356const robotoMono = Roboto_Mono({ subsets: ['latin'], variable: '--font-mono' })
357 
358export default function RootLayout({ children }: { children: React.ReactNode }) {
359 return (
360 <html lang="en" className={`${inter.variable} ${robotoMono.variable}`}>
361 <body>{children}</body>
362 </html>
363 )
364}
365```
366 
367## Key Rules
368 
369- Params are now `Promise<{ ... }>` in Next.js 15 — always `await params`
370- Never use `pages/` and `app/` for the same routes — pick one per segment
371- `revalidatePath()` and `revalidateTag()` only work in Server Actions and Route Handlers
372- Use `next/navigation` (`useRouter`, `redirect`, `notFound`) in App Router, not `next/router`
373- Co-locate non-route files (components, utils) inside `app/` using `_` prefix or route groups `(group)` to prevent them from becoming routes
374 

Sections

  • Next.js App Router Excellence
  • Server Components vs Client Components
  • App Router File Conventions
  • Route Handlers
  • Server Actions
  • Metadata API
  • Data Fetching Patterns
  • Image and Font Optimization
  • Key Rules

What it covers

code-styleapido-not

Glob targeting

  • **/app/**/*.tsx
  • **/app/**/*.ts
  • **/app/**/*.jsx
  • **/app/**/*.js
  • next.config.*

Format

Cursor rules

The most expressive format here. Many small .mdc files, each with frontmatter declaring when it should load, so a rule about migrations only enters context when a migration is open. Costs the most to maintain and only one editor reads it.

What the corpus says about it

Repository

Owner
tugkanboz
Language
—
License
—
Archived
no

All configs in this repo

Also in tugkanboz/awesome-cursorrules

Diff this repo’s formats

One repository carrying more than one format is the comparison this product exists for: does anyone actually write different content in each file, or is one a copy of the other?

The other instruction files in this repository
RepositoryFormatStackCoversScoreChanged
tugkanboz/awesome-cursorrulesexample-structures/cypress/.cursor/rules/api-testing.mdc · 18Cursor rulesunclassifiedtesttesting-strategyapi58/1002 days ago
tugkanboz/awesome-cursorrulesexample-structures/cypress/.cursor/rules/testing-fundamentals.mdc · 18Cursor rulesunclassifiedtestarchtesting-strategysecurity+262/1002 days ago
tugkanboz/awesome-cursorrulesrules/appium-mobile-test-automation-framework/.cursorrules · 18.cursorrulesunclassifiedteststylearchperformance+256/1002 days ago
tugkanboz/awesome-cursorrulesrules/cypress-javascript-test-automation-framework/.cursorrules · 18.cursorrulesunclassifiedtestlint-formatstylearch+459/1002 days ago
tugkanboz/awesome-cursorrulesrules/k6-performance-test-framework/.cursorrules · 18.cursorrulesunclassifiedsetupteststylearch+256/1002 days ago
tugkanboz/awesome-cursorrulesrules/restassured-java-framework/.cursorrules · 18.cursorrulesunclassifiedteststylearchsecurity+456/1002 days ago
tugkanboz/awesome-cursorrulesrules/selenium-net-test-automation-framework/.cursorrules · 18.cursorrulesunclassifiedteststylearchdeployment+156/1002 days ago
tugkanboz/awesome-cursorrulesrules/selenium-python-test-automation-framework/.cursorrules · 18.cursorrulesunclassifiedtestlint-formatstylearch+359/1002 days ago
tugkanboz/awesome-cursorrulesrules/webdriverio-javascript-test-automation-framework/.cursorrules · 18.cursorrulesunclassifiedtestlint-formatstylearch+469/1002 days ago
tugkanboz/awesome-cursorrulesexample-structures/react-typescript/.cursor/rules/component-development.mdc · 18Cursor rulesunclassifiedteststylearchtypes+158/1002 days ago
tugkanboz/awesome-cursorrulesexample-structures/selenium-python/.cursor/rules/framework-architecture.mdc · 18Cursor rulesunclassifiedsetuptestlint-formatstyle+393/1002 days ago
tugkanboz/awesome-cursorrulesexample-structures/selenium-python/.cursor/rules/page-object-patterns.mdc · 18Cursor rulesunclassifiedui54/1002 days ago
tugkanboz/awesome-cursorrulesexample-structures/selenium-python/.cursor/rules/test-patterns.mdc · 18Cursor rulesunclassifiedteststylearchtesting-strategy+166/1002 days ago
tugkanboz/awesome-cursorrulesframeworks/cypress/.cursor/rules/cypress-excellence.mdc · 18Cursor rulesunclassifiedtesttesting-strategysecurityperformance+158/1002 days ago
tugkanboz/awesome-cursorrulesrules/playwright-javascript-test-automation-framework/.cursorrules · 18.cursorrulesunclassifiedtestlint-formatstylearch+359/1002 days ago
tugkanboz/awesome-cursorrulesrules/vitest-javascript-unit-test-framework/.cursorrules · 18.cursorrulesunclassifiedsetupteststylearch+560/1002 days ago
Diff against example-structures/cypress/.cursor/rules/api-testing.mdc Diff against example-structures/cypress/.cursor/rules/testing-fundamentals.mdc Diff against rules/appium-mobile-test-automation-framework/.cursorrules Diff against rules/cypress-javascript-test-automation-framework/.cursorrules Diff against rules/k6-performance-test-framework/.cursorrules Diff against rules/restassured-java-framework/.cursorrules Diff against rules/selenium-net-test-automation-framework/.cursorrules Diff against rules/selenium-python-test-automation-framework/.cursorrules Diff against rules/webdriverio-javascript-test-automation-framework/.cursorrules Diff against example-structures/react-typescript/.cursor/rules/component-development.mdc Diff against example-structures/selenium-python/.cursor/rules/framework-architecture.mdc Diff against example-structures/selenium-python/.cursor/rules/page-object-patterns.mdc Diff against example-structures/selenium-python/.cursor/rules/test-patterns.mdc Diff against frameworks/cypress/.cursor/rules/cypress-excellence.mdc Diff against rules/playwright-javascript-test-automation-framework/.cursorrules Diff against rules/vitest-javascript-unit-test-framework/.cursorrules
RuleStack

Built by

Kynth Studio

Directory

Configs
Stacks
Compare formats
Diff two configs
Best AGENTS.md examples

Formats

AGENTS.md
CLAUDE.md
Cursor rules
Copilot instructions

Reference

Read API
Corpus health
Privacy Policy
Terms

RuleStack

RuleStack

Built by

Kynth Studio

Directory

Configs
Stacks
Compare formats
Diff two configs
Best AGENTS.md examples

Formats

AGENTS.md
CLAUDE.md
Cursor rules
Copilot instructions

Reference

Read API
Corpus health
Privacy Policy
Terms

RuleStack

RuleStack

Built by

Kynth Studio

Directory

Configs
Stacks
Compare formats
Diff two configs
Best AGENTS.md examples

Formats

AGENTS.md
CLAUDE.md
Cursor rules
Copilot instructions

Reference

Read API
Corpus health
Privacy Policy
Terms

RuleStack