Files
claude-plugins/dev-tools/skills/nextjs/SKILL.md
Emanuel Almeida 2cb3210962 feat: adiciona 12 plugins Descomplicar ao marketplace
Plugins: automacao, crm-ops, design-media, dev-tools, gestao,
infraestrutura, marketing, negocio, perfex-dev, project-manager,
wordpress + hello-plugin (existente).

Totais: 83 skills, 44 agents, 12 datasets.json

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-07 21:41:24 +00:00

17 KiB

name, description, author, version, quality_score, user_invocable, allowed-tools
name description author version quality_score user_invocable allowed-tools
nextjs Next.js development best practices and patterns. App router, server components, API routes, and deployment. Use when user mentions "nextjs", "next.js", "react server", "app router", "next deployment". Descomplicar® Crescimento Digital 2.0.0 75 true Glob

/nextjs - Next.js Development

Desenvolvimento Next.js moderno (13+) com App Router e Server Components.

Quando Usar

  • Criar aplicações Next.js
  • Migrar de Pages para App Router
  • Implementar Server Components
  • Configurar Server Actions
  • Optimizar SEO e performance

App Router Structure

app/
├── layout.tsx          # Root layout (obrigatório)
├── page.tsx            # Home page
├── loading.tsx         # Loading UI (Suspense)
├── error.tsx           # Error boundary
├── not-found.tsx       # 404 page
├── globals.css         # Global styles
├── (auth)/             # Route group (não afecta URL)
│   ├── login/page.tsx
│   └── register/page.tsx
├── dashboard/
│   ├── layout.tsx      # Nested layout
│   ├── page.tsx
│   └── [id]/
│       └── page.tsx    # Dynamic route
└── api/
    └── route.ts        # API route

Server vs Client Components

Server Component (default)

// ✅ PADRÃO - Runs on server
async function ProductsPage() {
  // Pode fazer fetch directo
  const products = await db.product.findMany();

  return (
    <div>
      {products.map(p => (
        <ProductCard key={p.id} product={p} />
      ))}
    </div>
  );
}

export default ProductsPage;

Vantagens:

  • Acesso directo a BD
  • Menos JavaScript no cliente
  • SEO melhor
  • Dados sempre frescos

Client Component

'use client';

import { useState } from 'react';

// ✅ Para interactividade
function Counter() {
  const [count, setCount] = useState(0);

  return (
    <button onClick={() => setCount(c => c + 1)}>
      {count}
    </button>
  );
}

Quando usar:

  • useState, useEffect, useContext
  • Event listeners (onClick, onChange)
  • Browser APIs (localStorage, window)
  • Custom hooks

Data Fetching Patterns

1. Static (SSG)

// Default behaviour - cache infinito
async function Page() {
  const data = await fetch('https://api.example.com/data', {
    cache: 'force-cache'  // Explícito (mas é default)
  });

  return <div>{data.title}</div>;
}

2. Dynamic (SSR)

// Sempre fetch fresco
async function Page() {
  const data = await fetch('https://api.example.com/data', {
    cache: 'no-store'  // NUNCA cache
  });

  return <div>{data.title}</div>;
}

3. Revalidate (ISR)

// Cache com revalidação automática
async function Page() {
  const data = await fetch('https://api.example.com/data', {
    next: { revalidate: 60 }  // Revalida a cada 60s
  });

  return <div>{data.title}</div>;
}

Server Actions

// app/actions.ts
'use server';

import { revalidatePath } from 'next/cache';
import { db } from '@/lib/db';

export async function createPost(formData: FormData) {
  const title = formData.get('title') as string;
  const content = formData.get('content') as string;

  await db.post.create({
    data: { title, content }
  });

  revalidatePath('/posts');
}

// app/create-post/page.tsx
import { createPost } from '@/app/actions';

export default function CreatePost() {
  return (
    <form action={createPost}>
      <input name="title" required />
      <textarea name="content" required />
      <button type="submit">Create</button>
    </form>
  );
}

Layouts e Loading States

Root Layout

// app/layout.tsx
import './globals.css';
import { Inter } from 'next/font/google';

const inter = Inter({ subsets: ['latin'] });

export const metadata = {
  title: 'My App',
  description: 'Description'
};

export default function RootLayout({
  children
}: {
  children: React.ReactNode
}) {
  return (
    <html lang="pt">
      <body className={inter.className}>
        <nav>...</nav>
        {children}
        <footer>...</footer>
      </body>
    </html>
  );
}

Loading UI

// app/dashboard/loading.tsx
export default function Loading() {
  return <div>Loading...</div>;
}

// Wrapper automático em <Suspense>

Error Handling

// app/error.tsx
'use client';

export default function Error({
  error,
  reset
}: {
  error: Error;
  reset: () => void;
}) {
  return (
    <div>
      <h2>Something went wrong!</h2>
      <button onClick={reset}>Try again</button>
    </div>
  );
}

Routing Avançado

Parallel Routes

// app/layout.tsx
export default function Layout({
  children,
  modal,
  sidebar
}: {
  children: React.ReactNode;
  modal: React.ReactNode;
  sidebar: React.ReactNode;
}) {
  return (
    <>
      {sidebar}
      {children}
      {modal}
    </>
  );
}

// app/@sidebar/page.tsx
// app/@modal/page.tsx

Intercepting Routes

// app/feed/page.tsx - Lista de fotos
// app/photo/[id]/page.tsx - Página completa da foto
// app/@modal/(.)photo/[id]/page.tsx - Modal da foto (intercepta)

// Navegar via <Link> abre modal
// Refresh ou URL directo abre página completa

Middleware

// middleware.ts
import { NextResponse } from 'next/server';
import type { NextRequest } from 'next/server';

export function middleware(request: NextRequest) {
  const token = request.cookies.get('token');

  // Auth guard
  if (!token && request.nextUrl.pathname.startsWith('/dashboard')) {
    return NextResponse.redirect(new URL('/login', request.url));
  }

  // Custom header
  const response = NextResponse.next();
  response.headers.set('x-custom-header', 'value');
  return response;
}

export const config = {
  matcher: ['/dashboard/:path*', '/api/:path*']
};

Performance Rules (Vercel Engineering)

57 regras priorizadas por impacto. Fonte: Vercel Engineering react-best-practices.

1. Eliminating Waterfalls (CRITICAL)

// ❌ ERRADO: Awaits sequenciais (waterfall)
const session = await getSession()
const config = await getConfig()
const user = await getUser(session.userId)

// ✅ CORRECTO: Paralelo com Promise.all()
const [session, config] = await Promise.all([
  getSession(),
  getConfig()
])
const user = await getUser(session.userId) // depende de session

// ✅ CORRECTO: Suspense boundaries estratégicos
async function Page() {
  return (
    <Layout>
      <Suspense fallback={<HeaderSkeleton />}>
        <Header />
      </Suspense>
      <Suspense fallback={<ContentSkeleton />}>
        <SlowContent />
      </Suspense>
    </Layout>
  )
}

// ✅ CORRECTO: Defer await até necessário
async function handler(req) {
  const dataPromise = fetchData() // inicia imediatamente
  if (req.method === 'HEAD') return new Response() // early return sem await
  const data = await dataPromise // só espera quando precisa
  return Response.json(data)
}

Regras:

  • async-defer-await: Mover await para onde é realmente necessário
  • async-dependency-parallel: Usar better-all para dependências parciais
  • async-prevent-waterfall: Iniciar promises antes de awaitar
  • async-promise-all: Promise.all() para operações independentes
  • async-suspense-boundaries: Suspense para UI progressiva

2. Bundle Size (CRITICAL)

// ❌ ERRADO: Barrel file imports
import { Button, Icon } from '@mui/material' // carrega tudo

// ✅ CORRECTO: Import directo
import Button from '@mui/material/Button'

// ✅ CORRECTO: optimizePackageImports (Next.js 13.5+)
// next.config.js
module.exports = {
  experimental: {
    optimizePackageImports: ['lucide-react', '@mui/material', 'lodash']
  }
}

// ✅ CORRECTO: Dynamic imports para componentes pesados
const MonacoEditor = dynamic(() => import('./MonacoEditor'), {
  ssr: false,
  loading: () => <EditorSkeleton />
})

// ✅ CORRECTO: Preload em hover/focus
function FeatureButton() {
  return (
    <button
      onMouseEnter={() => import('./HeavyFeature')}
      onClick={openFeature}
    >
      Open Feature
    </button>
  )
}

Regras:

  • bundle-barrel-imports: Evitar barrel files (index.js re-exports)
  • bundle-conditional-loading: Carregar módulos só quando feature activa
  • bundle-defer-third-party: next/dynamic com ssr: false para analytics
  • bundle-dynamic-imports: Lazy-load componentes pesados (>50KB)
  • bundle-preload-intent: Preload em hover para UX instantânea

3. Server-Side Performance (HIGH)

// ✅ Server Actions: autenticar SEMPRE (são endpoints públicos!)
'use server'
async function updateProfile(formData: FormData) {
  const session = await auth() // 1. Autenticar
  if (!session) throw new Error('Unauthorized')
  const data = schema.parse(formData) // 2. Validar input
  await db.user.update({ where: { id: session.userId }, data }) // 3. Executar
  revalidatePath('/profile')
}

// ✅ Minimizar serialização RSC - só campos necessários
async function UserCard({ userId }: { userId: string }) {
  const user = await db.user.findUnique({ where: { id: userId } })
  // Passa só o necessário, não o objecto inteiro com 50+ campos
  return <ClientCard name={user.name} avatar={user.avatar} />
}

// ✅ React.cache() para deduplicação per-request
const getUser = cache(async (id: string) => {
  return db.user.findUnique({ where: { id } })
})

// ✅ after() para operações não-bloqueantes
import { after } from 'next/server'
async function submitForm(data: FormData) {
  const result = await processForm(data)
  after(async () => {
    await logAnalytics(result) // executa APÓS resposta enviada
  })
  return result
}

Regras:

  • server-auth-actions: Autenticar Server Actions como API routes
  • server-avoid-duplicate-serialization: Transformar dados no cliente
  • server-lru-cache: LRUCache para dados cross-request
  • server-minimize-serialization: Só campos necessários em RSC props
  • server-parallel-composition: Composição paralela de Server Components
  • server-react-cache: React.cache() para deduplicação per-request
  • server-after: after() para logging/analytics não-bloqueante

4. Client-Side Data Fetching (MEDIUM-HIGH)

// ✅ SWR para deduplicação automática
const { data, isLoading } = useSWR('/api/user', fetcher)

// ✅ Passive event listeners para scroll performance
element.addEventListener('scroll', handler, { passive: true })

// ✅ localStorage versionado com try-catch
const key = 'userConfig:v2'
try {
  const data = JSON.parse(localStorage.getItem(key) ?? '{}')
} catch { /* incognito mode, quota exceeded */ }

5. Re-render Optimization (MEDIUM)

// ✅ Derived state: calcular durante render, NÃO em useEffect
function FilteredList({ items, query }: Props) {
  const filtered = items.filter(i => i.name.includes(query)) // derive!
  return <List items={filtered} />
}

// ✅ Functional setState para evitar stale closures
setItems(curr => [...curr, ...newItems]) // ✅
// setItems([...items, ...newItems]) // ❌ stale closure

// ✅ Lazy state initialization
const [index] = useState(() => buildSearchIndex(items)) // computa 1x

// ✅ useTransition para updates não-urgentes
const [isPending, startTransition] = useTransition()
startTransition(() => setSearchResults(results))

// ✅ Extrair defaults não-primitivos para module scope
const EMPTY_ARRAY: string[] = [] // ✅ fora do componente
const NOOP = () => {} // ✅ fora do componente
function Component({ items = EMPTY_ARRAY, onClick = NOOP }) { ... }

// ✅ Dependências de effect: primitivos, não objectos
useEffect(() => { fetchUser(userId) }, [userId]) // ✅ primitivo
// useEffect(() => { ... }, [user]) // ❌ objecto = nova ref cada render

6. Rendering Performance (MEDIUM)

// ✅ CSS content-visibility para listas longas
// .message { content-visibility: auto; contain-intrinsic-size: 0 80px; }

// ✅ Hoist static JSX fora do componente
const skeleton = <div className="skeleton h-4 w-full" /> // module scope

// ✅ Conditional rendering explícito (evitar && com falsy)
{count > 0 ? <Badge>{count}</Badge> : null} // ✅
// {count && <Badge>{count}</Badge>} // ❌ renderiza "0"

// ✅ useTransition em vez de useState manual para loading
const [isPending, startTransition] = useTransition()
// const [isLoading, setIsLoading] = useState(false) // ❌ manual

// ✅ Hydration: inline script para evitar flicker
// <script dangerouslySetInnerHTML={{ __html: `
//   document.documentElement.classList.add(
//     localStorage.getItem('theme') === 'dark' ? 'dark' : 'light'
//   )
// `}} />

7. JavaScript Performance (LOW-MEDIUM)

// ✅ Index Maps para lookups repetidos O(1) vs O(n)
const userMap = new Map(users.map(u => [u.id, u]))
orders.map(o => ({ ...o, user: userMap.get(o.userId) }))

// ✅ Set para membership checks
const allowedIds = new Set(['a', 'b', 'c'])
if (allowedIds.has(id)) { ... } // O(1) vs .includes() O(n)

// ✅ toSorted() para imutabilidade (React-safe)
const sorted = items.toSorted((a, b) => a.name.localeCompare(b.name))
// items.sort() // ❌ muta o array original

// ✅ Early return para evitar processamento desnecessário
function validate(items: Item[]) {
  for (const item of items) {
    if (!item.valid) return { error: item.id } // sai cedo
  }
  return { success: true }
}

// ✅ Combinar iterações de array
const { active, expired } = items.reduce((acc, item) => {
  if (item.active) acc.active.push(item)
  else acc.expired.push(item)
  return acc
}, { active: [], expired: [] })
// Em vez de items.filter(active) + items.filter(expired) = 2 iterações

8. Advanced Patterns (LOW)

// ✅ Init uma vez, não por mount (Strict Mode remonta)
let didInit = false
function App() {
  useEffect(() => {
    if (didInit) return
    didInit = true
    initializeAnalytics()
  }, [])
}

// ✅ useEffectEvent para callbacks estáveis em effects
const onTick = useEffectEvent((value) => {
  console.log(value) // sempre valor mais recente
})
useEffect(() => {
  const id = setInterval(() => onTick(count), 1000)
  return () => clearInterval(id)
}, []) // sem dependência de count!

Resumo de Impacto

Prioridade Categoria Regras Ganho
CRITICAL Waterfalls 5 2-10x latência
CRITICAL Bundle Size 5 200-800ms load
HIGH Server-Side 7 RSC payload, TTI
MEDIUM-HIGH Client Fetch 4 Request dedup
MEDIUM Re-renders 12 UI responsiveness
MEDIUM Rendering 9 Paint performance
LOW-MEDIUM JS Perf 12 CPU cycles
LOW Advanced 3 Init correctness

Optimizações

Images

import Image from 'next/image';

<Image
  src="/photo.jpg"
  width={500}
  height={300}
  alt="Photo"
  priority  // LCP image
  placeholder="blur"
  blurDataURL="data:image/..."
/>

Fonts

import { Inter, Roboto_Mono } from 'next/font/google';

const inter = Inter({
  subsets: ['latin'],
  display: 'swap',
});

const robotoMono = Roboto_Mono({
  subsets: ['latin'],
  weight: ['400', '700'],
});

Metadata (SEO)

// app/page.tsx
export const metadata = {
  title: 'Home',
  description: 'Home page',
  openGraph: {
    title: 'Home',
    description: 'Home page',
    images: ['/og-image.jpg'],
  },
  twitter: {
    card: 'summary_large_image',
  },
};

Deployment

Vercel (recomendado)

npm install -g vercel
vercel

Docker

FROM node:18-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build

FROM node:18-alpine
WORKDIR /app
COPY --from=builder /app/.next ./.next
COPY --from=builder /app/public ./public
COPY --from=builder /app/package*.json ./
RUN npm ci --production
EXPOSE 3000
CMD ["npm", "start"]

Static Export

// next.config.js
module.exports = {
  output: 'export',
  trailingSlash: true,
};
npm run build
# Output: out/

Datasets Dify

Dataset ID Prioridade
Desenvolvimento de Software e7c7decc-0ded-4351-ab14-b110b3c38ec9 1
TI (Tecnologia da Informação) 7f63ec0c-6321-488c-b107-980140199850 1

Versão: 1.0.0 | Autor: Descomplicar®


Quando NÃO Usar

  • Para tarefas fora do domínio de especialização desta skill
  • Quando outra skill mais específica está disponível
  • Para operações que requerem confirmação manual do utilizador

Protocolo

  1. Analisar requisitos da tarefa
  2. Verificar disponibilidade de ferramentas necessárias
  3. Executar operações de forma incremental
  4. Validar resultados antes de concluir
  5. Reportar status e próximos passos

Exemplos

Exemplo 1: Uso Básico

Input: [descrição da tarefa]
Output: [resultado esperado]

Exemplo 2: Uso Avançado

Input: [caso complexo]
Output: [resultado detalhado]