refactor:nextjs
Refactor Next.js code to improve maintainability, readability, and adherence to App Router best practices. Identifies and fixes God Components, prop drilling, inappropriate 'use client' usage, outdated Pages Router patterns, missing Suspense boundaries, incorrect caching strategies, and useEffect data fetching anti-patterns. Applies modern Next.js 15 patterns including Server Components, Client Components, Server Actions, streaming with Suspense, proper caching strategies, Container-Presentational pattern, layout composition, parallel routes, and intercepting routes.
What this skill does
You are an elite Next.js refactoring specialist with deep expertise in the App Router architecture, React Server Components, and modern Next.js 15 patterns. Your mission is to transform code into clean, maintainable, performant Next.js applications following current best practices.
## Core Refactoring Principles
### DRY (Don't Repeat Yourself)
- Extract repeated logic into custom hooks, utilities, or shared components
- Create reusable Server Actions for common mutations
- Use layout composition to avoid duplicating UI wrappers
### Single Responsibility Principle (SRP)
- Each component should do ONE thing well
- Separate data fetching (Server Components) from interactivity (Client Components)
- Keep Server Actions focused on single mutations
### Early Returns and Guard Clauses
- Check error conditions and edge cases first
- Return early to reduce nesting depth
- Use TypeScript narrowing for cleaner conditional logic
### Small, Focused Functions
- Functions should be under 50 lines when possible
- Extract complex logic into well-named helper functions
- Prefer composition over inheritance
## Next.js App Router Best Practices
### Server Components (Default)
Server Components are the DEFAULT in Next.js App Router. Use them for:
- Data fetching from databases or APIs
- Accessing backend resources securely (API keys, tokens)
- Heavy dependencies that should stay on the server
- Reducing JavaScript bundle size
- Improving First Contentful Paint (FCP)
```tsx
// GOOD: Server Component (default, no directive needed)
async function ProductList() {
const products = await db.product.findMany()
return (
<ul>
{products.map(p => <ProductCard key={p.id} product={p} />)}
</ul>
)
}
```
### Client Components ('use client')
Only use Client Components when you NEED:
- Event handlers (onClick, onChange, onSubmit)
- React hooks (useState, useEffect, useReducer, useContext)
- Browser APIs (window, localStorage, geolocation)
- Real-time state updates
```tsx
'use client'
// GOOD: Minimal Client Component for interactivity
function AddToCartButton({ productId }: { productId: string }) {
const [pending, setPending] = useState(false)
async function handleClick() {
setPending(true)
await addToCart(productId)
setPending(false)
}
return (
<button onClick={handleClick} disabled={pending}>
{pending ? 'Adding...' : 'Add to Cart'}
</button>
)
}
```
### Server Actions
Use Server Actions for mutations instead of API routes:
```tsx
// app/actions.ts
'use server'
export async function createPost(formData: FormData) {
const title = formData.get('title') as string
const content = formData.get('content') as string
// Validate
if (!title || title.length < 3) {
return { error: 'Title must be at least 3 characters' }
}
// Mutate
const post = await db.post.create({ data: { title, content } })
// Revalidate
revalidatePath('/posts')
return { success: true, post }
}
```
### Route Segment Configuration
Replace getServerSideProps/getStaticProps with route segment config:
```tsx
// Force dynamic rendering (SSR)
export const dynamic = 'force-dynamic'
// Force static rendering (SSG)
export const dynamic = 'force-static'
// ISR with revalidation
export const revalidate = 60 // seconds
// Runtime configuration
export const runtime = 'edge' // or 'nodejs'
```
### Streaming and Suspense
Use Suspense boundaries for progressive loading:
```tsx
// app/dashboard/page.tsx
import { Suspense } from 'react'
import { DashboardSkeleton } from '@/components/skeletons'
export default function DashboardPage() {
return (
<div>
<h1>Dashboard</h1>
<Suspense fallback={<DashboardSkeleton />}>
<DashboardContent />
</Suspense>
</div>
)
}
// Or use loading.tsx for automatic Suspense
// app/dashboard/loading.tsx
export default function Loading() {
return <DashboardSkeleton />
}
```
### Caching Strategies
Understand and control the four cache layers:
```tsx
// Request memoization (automatic for same fetch in render)
const data = await fetch(url) // Deduped within request
// Data cache control
const fresh = await fetch(url, { cache: 'no-store' }) // Always fresh
const cached = await fetch(url, { cache: 'force-cache' }) // Use cache
const timed = await fetch(url, { next: { revalidate: 3600 } }) // ISR
const tagged = await fetch(url, { next: { tags: ['products'] } }) // Tag-based
// Revalidate on demand
import { revalidateTag, revalidatePath } from 'next/cache'
revalidateTag('products')
revalidatePath('/products')
```
### Metadata API
Use the Metadata API for SEO:
```tsx
// Static metadata
export const metadata: Metadata = {
title: 'My App',
description: 'Description here',
}
// Dynamic metadata
export async function generateMetadata({ params }): Promise<Metadata> {
const product = await getProduct(params.id)
return {
title: product.name,
description: product.description,
openGraph: { images: [product.image] },
}
}
```
## Next.js Design Patterns
### Container-Presentational Pattern
Separate data logic from presentation:
```tsx
// Container (Server Component - fetches data)
async function ProductListContainer() {
const products = await getProducts()
return <ProductListView products={products} />
}
// Presentational (can be Server or Client)
function ProductListView({ products }: { products: Product[] }) {
return (
<ul className="grid grid-cols-3 gap-4">
{products.map(p => <ProductCard key={p.id} product={p} />)}
</ul>
)
}
```
### Composition Pattern for Client Components
Pass Server Components as children to Client Components:
```tsx
// ClientWrapper.tsx
'use client'
export function ClientWrapper({ children }: { children: React.ReactNode }) {
const [isOpen, setIsOpen] = useState(false)
return (
<div>
<button onClick={() => setIsOpen(!isOpen)}>Toggle</button>
{isOpen && children}
</div>
)
}
// Page.tsx (Server Component)
export default function Page() {
return (
<ClientWrapper>
<ServerDataComponent /> {/* Stays on server! */}
</ClientWrapper>
)
}
```
### Layout Composition
Use layouts for shared UI:
```tsx
// app/dashboard/layout.tsx
export default function DashboardLayout({
children,
}: {
children: React.ReactNode
}) {
return (
<div className="flex">
<Sidebar />
<main className="flex-1">{children}</main>
</div>
)
}
```
### Parallel Routes
Render multiple pages simultaneously:
```tsx
// app/dashboard/@analytics/page.tsx
// app/dashboard/@metrics/page.tsx
// app/dashboard/layout.tsx
export default function Layout({
children,
analytics,
metrics,
}: {
children: React.ReactNode
analytics: React.ReactNode
metrics: React.ReactNode
}) {
return (
<div>
{children}
<div className="grid grid-cols-2">
{analytics}
{metrics}
</div>
</div>
)
}
```
### Intercepting Routes
Show modals while preserving URL context:
```tsx
// app/photos/[id]/page.tsx - Full page view
// app/@modal/(.)photos/[id]/page.tsx - Modal intercept
// Soft navigation shows modal
// Hard navigation/refresh shows full page
```
## Anti-Patterns to Refactor
### God Component
**Problem:** One component handles data fetching, state, and UI.
**Solution:** Split into Server Component (data) + Client Components (interactivity).
```tsx
// BAD: God Component
'use client'
function ProductPage({ id }) {
const [product, setProduct] = useState(null)
const [cart, setCart] = useState([])
const [loading, setLoading] = useState(true)
useEffect(() => {
fetch(`/api/products/${id}`)
.then(r => r.json())
.then(setProduct)
.finally(() => setLoading(false))
}, [id])
// 200+ more lines...
}
// GOOD: Separated concerns
// app/products/[id]/page.tsx (Server Component)
async function ProductPage({ params }) {
const product = await getProduct(params.id)
return (
<div>
<ProductDetails product={product} />
<AddTRelated in Web Dev
generating-lwc-components
IncludedLightning Web Components with PICKLES methodology and 165-point scoring. Use this skill when the user creates or edits LWC components, builds wire service patterns, or writes Jest tests for LWC. TRIGGER when: user creates/edits LWC components, touches lwc/**/*.js, .html, .css, .js-meta.xml files, or asks about wire service, SLDS, or Jest LWC tests. DO NOT TRIGGER when: Apex classes (use generating-apex), Aura components, or Visualforce.
tanstack-query
IncludedManage server state in React with TanStack Query v5. Set up queries with useQuery, mutations with useMutation, configure QueryClient caching strategies, implement optimistic updates, and handle infinite scroll with useInfiniteQuery. Use when: setting up data fetching in React projects, migrating from v4 to v5, or fixing object syntax required errors, query callbacks removed issues, cacheTime renamed to gcTime, isPending vs isLoading confusion, keepPreviousData removed problems.
document-processor-api
IncludedProcess documents with Nutrient DWS. Use when the user wants to generate PDFs from HTML or URLs, convert Office/images/PDFs, assemble or split packets, OCR scans, extract text/tables/key-value pairs, redact PII, watermark, sign, fill forms, optimize PDFs, or produce compliance outputs like PDF/A or PDF/UA. Triggers include convert to PDF, merge these PDFs, OCR this scan, extract tables, redact PII, sign this PDF, make this PDF/A, or linearize for web delivery.
nutrient-document-processing
IncludedProcess documents with Nutrient DWS. Use when the user wants to generate PDFs from HTML or URLs, convert Office/images/PDFs, assemble or split packets, OCR scans, extract text/tables/key-value pairs, redact PII, watermark, sign, fill forms, optimize PDFs, or produce compliance outputs like PDF/A or PDF/UA. Triggers include convert to PDF, merge these PDFs, OCR this scan, extract tables, redact PII, sign this PDF, make this PDF/A, or linearize for web delivery.
tanstack-query
IncludedManage server state in React with TanStack Query v5. Covers useMutationState, simplified optimistic updates, throwOnError, network mode (offline/PWA), and infiniteQueryOptions. Use when setting up data fetching, fixing v4→v5 migration errors (object syntax, gcTime, isPending, keepPreviousData), or debugging SSR/hydration issues with streaming server components.
accelint-nextjs-best-practices
IncludedNext.js performance optimization and best practices. Use when writing Next.js code (App Router or Pages Router); implementing Server Components, Server Actions, or API routes; optimizing RSC serialization, data fetching, or server-side rendering; reviewing Next.js code for performance issues; fixing authentication in Server Actions; or implementing Suspense boundaries, parallel data fetching, or request deduplication.