How Cursor Project Rules Can Improve Next.js App Development
Next.js apps thrive on consistency. Predictable file structures, standardized component patterns, and reusable boilerplate all make development faster and more maintainable. But enforcing those conventions across a growing team is hard.
Cursor’s project rules feature provides a framework to automate these decisions. By encoding rules directly into your repo, you ensure that new code always follows your team’s conventions — without relying on documentation or memory.
In this guide, we’ll cover:
- How project rules work in Cursor
- How to set them up for a Next.js project
- Practical examples for pages, components, and API routes
- Benefits for large projects
How Project Rules Work in Cursor
Project rules act as instructions that Cursor evaluates before generating or editing code. They define constraints for naming, structure, imports, and styling. Once in place, Cursor enforces these rules automatically so generated code doesn’t drift from your project’s style.
Setting Up Project Rules
Rules live in the .cursor/rules
directory at the root of your repo. Each rule is a .md
file containing natural-language instructions and optional code examples. Because rules live in source control, they become part of the project’s single source of truth.
For example, to ensure all pages use the Next.js app
router, create .cursor/rules/pages.md
:
# Next.js Page Rules All new pages must: - Be server components ("use client"
must not be present). - Live under theapp/
directory. - Usedefault export
for the page component. - Import styles with module CSS or Tailwind (not global CSS).
Now, when you ask Cursor to generate a page, it won’t create client components or place files in the legacy pages/
folder.
Enforcing Component Conventions
Large projects often need predictable component patterns. Suppose you want all components in components/
to:
- Use PascalCase filenames
- Export TypeScript props interfaces from the same file
- Use named exports (not default)
Enforce this with .cursor/rules/components.md
:
# Component Conventions All components insidecomponents/
must: - Use PascalCase for filenames (e.g.,Navbar.tsx
). - Define props interfaces with the name<ComponentName>Props
. - Use TypeScript function components with explicit return types. - Export components as named functions, not defaults.
Generated by Cursor:
// components/Navbar.tsx import React from "react"; export interface NavbarProps { title: string; } export function Navbar({ title }: NavbarProps): JSX.Element { return ( <nav className="p-4 border-b border-gray-200"> <h1 className="text-lg font-bold">{title}</h1> </nav> ); }
Automating Boilerplate for API Routes
API routes are a common source of repetitive boilerplate. Codify best practices:
# API Route Rules When creating API routes: - Place them underapp/api/{route}/route.ts
. - Export functions for supported HTTP methods (GET
,POST
, etc.). - Always validate request data with Zod. - Return JSON responses with appropriate status codes.
Generated implementation:
// app/api/users/route.ts import { NextResponse } from "next/server"; import { z } from "zod"; const userSchema = z.object({ name: z.string(), email: z.string().email(), }); export async function POST(request: Request) { const body = await request.json(); const parsed = userSchema.safeParse(body); if (!parsed.success) { return NextResponse.json({ error: "Invalid input" }, { status: 400 }); } return NextResponse.json({ message: "User created" }, { status: 201 }); }
Instead of scaffolding manually, developers always get a validated, consistent API route.
Applying Rules to Next.js Development
File Structure: App vs Pages Router
Rules can prevent mixing the legacy pages/
router with the app
directory:
# Next.js Routing Rules - All routes must be inside theapp/
directory. - The legacypages/
directory must not be used. - Each route directory must contain apage.tsx
entry point.
Generated result:
// app/dashboard/page.tsx export default function DashboardPage() { return <div>Dashboard</div>; }
Standardizing Imports and API Calls
Enforce absolute imports and a shared API helper:
# Import Rules - Always use absolute imports from the@/
alias intsconfig.json
. - Never use relative imports like../../components/...
. - Use thelib/api.ts
helper for all API calls.
// components/UserList.tsx import React from "react"; import { get } from "@/lib/api"; export async function UserList() { const users = await get("/api/users"); return ( <ul> {users.map((user: any) => ( <li key={user.id}>{user.name}</li> ))} </ul> ); }
// lib/api.ts export async function get(url: string) { const res = await fetch(url); if (!res.ok) throw new Error("Failed to fetch"); return res.json(); }
Enforcing TypeScript and ESLint Rules
Embed expectations so generated files stay consistent:
# TypeScript and ESLint Rules - All new files must use.ts
or.tsx
extensions. - Props must always have an explicit TypeScript interface or type alias. - Return types must be declared for all functions. - ESLint rules in.eslintrc.json
must be followed.
// components/Button.tsx import React from "react"; export interface ButtonProps { label: string; onClick: () => void; } export function Button({ label, onClick }: ButtonProps): JSX.Element { return ( <button onClick={onClick} className="rounded-md px-4 py-2 bg-blue-600 text-white" > {label} </button> ); }
Rule: API Routes Structure
Define a consistent route structure in .cursor/rules/api.md
:
# API Route Rules
- Place all routes under app/api/{name}/route.ts
. - Always use Zod for request validation. - Return JSON with proper status codes. - Use async functions with explicit return types.
// app/api/posts/route.ts import { NextResponse } from "next/server"; import { z } from "zod"; const postSchema = z.object({ title: z.string(), content: z.string(), }); export async function POST(request: Request): Promise<Response> { const body = await request.json(); const parsed = postSchema.safeParse(body); if (!parsed.success) { return NextResponse.json({ error: "Invalid input" }, { status: 400 }); } return NextResponse.json({ message: "Post created" }, { status: 201 }); }
Rule: Pages and Layout Structure
Enforce that routes include both page and layout files:
# Page and Layout Rules - Every route directory must contain bothpage.tsx
andlayout.tsx
. - Layout files must wrap children with a<section>
element. - Use Tailwind classes for layout spacing.
// app/profile/page.tsx export default function ProfilePage() { return <div>User Profile</div>; } // app/profile/layout.tsx export default function ProfileLayout({ children, }: { children: React.ReactNode; }) { return <section className="max-w-4xl mx-auto p-6">{children}</section>; }
Rule: Consistent Component Styling
If your project uses Tailwind, encode styling expectations:
# Component Styling Rules - All components must use Tailwind CSS for styling. - Avoid inline styles and global CSS imports. - Use semantic HTML elements where possible.
// components/Card.tsx import React from "react"; export interface CardProps { title: string; children: React.ReactNode; } export function Card({ title, children }: CardProps): JSX.Element { return ( <div className="rounded-xl shadow p-4 border border-gray-200"> <h2 className="text-lg font-semibold mb-2">{title}</h2> <div>{children}</div> </div> ); }
Benefits of Project Rules in Large Next.js Projects
- Improved onboarding: New developers don’t need tribal knowledge — rules make expectations explicit and enforceable.
- Consistent code quality: Rules prevent deviations like relative imports or JavaScript files in a TypeScript codebase.
- Faster iteration: Automated scaffolding speeds development and reduces cleanup work.
Conclusion
Cursor project rules turn unwritten conventions into executable, version-controlled instructions. By guiding how pages, components, and API routes are created, they make large Next.js projects more consistent, easier to onboard into, and faster to iterate on. Instead of enforcing standards manually, let Cursor enforce them automatically — so your team can focus on building features, not fixing structure.
The post How Cursor Project Rules Can Improve Next.js App Development appeared first on LogRocket Blog.
This post first appeared on Read More