feat: biblioteca inteligente libs/ + 5 novas skills (20 skills total)
NOVAS SKILLS: - next-best-practices v0.1.0 (CLEAN) — Next.js App Router, RSC, caching, data - nextjs-patterns v1.0.0 (CLEAN) — Next.js 15: Server Actions, route handlers - vite v1.0.0 (CLEAN) — env vars, aliases, proxy, CJS compat - uncle-bob v1.0.0 (CLEAN) — Clean Code, SOLID, Clean Architecture - clean-code-review v1.0.0 (CLEAN) — naming, guard clauses, anti-patterns, refactoring - vue v1.0.0 (CLEAN) — Vue framework - vue-composition-api-best-practices v1.0.0 (CLEAN) — composables, Pinia, reactivity BIBLIOTECA INTELIGENTE libs/ (10 dominios, 11 arquivos): - typescript/ — TS safe + generics gotchas - react/ — Next.js App Router + Vite config - vue/ — Composition API + Pinia - linux/ — System diagnostic cheatsheet - database/ — PostgreSQL + MySQL patterns - browser/ — Chromium CLI + E2E testing - security/ — SAST audit (OWASP Top 10) - best-practices/ — Clean Code + SOLID + Clean Architecture - deploy/ — Docker multi-stack + OpenClaw ops - + INDEX.md como guia de navegacao .learnings/ — LRN-20260519-003 criado (biblioteca compartilhada)
This commit is contained in:
@@ -0,0 +1,145 @@
|
||||
---
|
||||
name: nextjs-patterns
|
||||
description: >
|
||||
Apply Next.js 15 best practices and modern patterns including App Router,
|
||||
Server Components, Server Actions, caching strategies, and performance
|
||||
optimization. Use when building or reviewing Next.js 15 applications to
|
||||
ensure idiomatic, production-ready code.
|
||||
---
|
||||
|
||||
# Next.js 15 Best Practices
|
||||
|
||||
## Core Principles
|
||||
|
||||
1. **Default to Server Components** — only add `"use client"` when you need interactivity or browser APIs
|
||||
2. **Colocate by feature** — keep components, hooks, and utils near the routes that use them
|
||||
3. **Type everything** — leverage TypeScript with strict mode enabled
|
||||
4. **Cache deliberately** — understand the four caching layers and opt in/out explicitly
|
||||
|
||||
## Project Structure
|
||||
|
||||
```
|
||||
app/
|
||||
(marketing)/ # route group: no URL segment
|
||||
page.tsx
|
||||
(dashboard)/
|
||||
layout.tsx
|
||||
[id]/
|
||||
page.tsx
|
||||
api/
|
||||
route.ts
|
||||
components/
|
||||
ui/ # shared, "dumb" UI components
|
||||
features/ # feature-specific components
|
||||
lib/
|
||||
db.ts # database client (singleton)
|
||||
auth.ts # auth helpers
|
||||
utils.ts
|
||||
```
|
||||
|
||||
## Server vs. Client Components
|
||||
|
||||
```tsx
|
||||
// ✅ Server Component (default) — runs on server, no JS sent to client
|
||||
export default async function ProductList() {
|
||||
const products = await db.product.findMany()
|
||||
return <ul>{products.map(p => <li key={p.id}>{p.name}</li>)}</ul>
|
||||
}
|
||||
|
||||
// ✅ Client Component — only when needed
|
||||
"use client"
|
||||
import { useState } from "react"
|
||||
export function Counter() {
|
||||
const [n, setN] = useState(0)
|
||||
return <button onClick={() => setN(n + 1)}>{n}</button>
|
||||
}
|
||||
```
|
||||
|
||||
## Data Fetching Patterns
|
||||
|
||||
```tsx
|
||||
// Parallel fetching (avoid sequential waterfalls)
|
||||
export default async function Dashboard() {
|
||||
const [user, stats] = await Promise.all([
|
||||
fetchUser(),
|
||||
fetchStats(),
|
||||
])
|
||||
return <View user={user} stats={stats} />
|
||||
}
|
||||
|
||||
// fetch with cache control
|
||||
const data = await fetch("https://api.example.com/data", {
|
||||
next: { revalidate: 60 }, // ISR: revalidate every 60s
|
||||
// cache: "no-store" // always fresh
|
||||
// cache: "force-cache" // static, until manual revalidation
|
||||
})
|
||||
```
|
||||
|
||||
## Server Actions
|
||||
|
||||
```tsx
|
||||
// app/actions.ts
|
||||
"use server"
|
||||
import { revalidatePath } from "next/cache"
|
||||
|
||||
export async function createPost(formData: FormData) {
|
||||
const title = formData.get("title") as string
|
||||
await db.post.create({ data: { title } })
|
||||
revalidatePath("/posts")
|
||||
}
|
||||
|
||||
// app/posts/new/page.tsx
|
||||
import { createPost } from "../actions"
|
||||
export default function NewPost() {
|
||||
return (
|
||||
<form action={createPost}>
|
||||
<input name="title" />
|
||||
<button type="submit">Create</button>
|
||||
</form>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
## Metadata & SEO
|
||||
|
||||
```tsx
|
||||
// Static metadata
|
||||
export const metadata = {
|
||||
title: "My App",
|
||||
description: "...",
|
||||
}
|
||||
|
||||
// Dynamic metadata
|
||||
export async function generateMetadata({ params }) {
|
||||
const post = await fetchPost(params.slug)
|
||||
return { title: post.title, description: post.excerpt }
|
||||
}
|
||||
```
|
||||
|
||||
## Error & Loading States
|
||||
|
||||
```tsx
|
||||
// app/posts/loading.tsx — automatic Suspense boundary
|
||||
export default function Loading() {
|
||||
return <Skeleton />
|
||||
}
|
||||
|
||||
// app/posts/error.tsx — automatic error boundary
|
||||
"use client"
|
||||
export default function Error({ error, reset }) {
|
||||
return <button onClick={reset}>Retry: {error.message}</button>
|
||||
}
|
||||
```
|
||||
|
||||
## Performance Checklist
|
||||
|
||||
- [ ] Images use `next/image` with explicit `width`/`height`
|
||||
- [ ] Fonts use `next/font` (zero layout shift)
|
||||
- [ ] Dynamic imports for heavy client components: `dynamic(() => import(...))`
|
||||
- [ ] `generateStaticParams` for known dynamic routes
|
||||
- [ ] Bundle analyzer run: `ANALYZE=true next build`
|
||||
- [ ] Partial Prerendering (PPR) considered for mixed static/dynamic pages
|
||||
|
||||
## References
|
||||
|
||||
See `references/` folder for routing patterns, caching deep-dive, and migration guide.
|
||||
Reference in New Issue
Block a user