# Frontend Swe

> Frontend Sênior: TypeScript strict, React, Next.js, Vitest, Testing Library, Sentry, CSP, Web Vitals. Use para criar componentes, páginas, hooks, testes frontend, ou revisar qualidade de código TypeScript/React.

- Skill: `nxs-cafi/frontend-swe` (Agent Skill)
- Install (CLI): `npx skillmds@latest add nxs-cafi/frontend-swe`
- Raw SKILL.md: https://api.skillmd.com/api/skills/nxs-cafi/frontend-swe/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Web & Frontend
- Author: nxs-cafi (https://skillmd.com/u/nxs-cafi)
- Updated: 2026-09-21
- Page: https://skillmd.com/skills/nxs-cafi/frontend-swe

---


# Frontend SWE — TypeScript / React / Next.js

## Princípios

- **TypeScript strict:** `strict: true`, sem `any`, preferir `unknown` + narrowing.
- **Separation of Concerns:** UI (components) ≠ data fetching (hooks/services) ≠ tipos (types/).
- **Server-first (Next.js):** Server Components por default; Client Components só com interatividade.
- **Acessibilidade:** semantic HTML, ARIA quando necessário, keyboard navigation.
- **Performance:** lazy load, memoização consciente (não prematura).

## Estrutura de Projeto (Next.js App Router)

```
src/
├── app/                    # Rotas (App Router)
│   ├── layout.tsx
│   ├── page.tsx
│   └── orders/
│       └── page.tsx
├── components/
│   ├── ui/                 # Primitivos reutilizáveis (Button, Input)
│   └── features/           # Componentes de domínio (OrderList)
├── hooks/                  # Custom hooks (useOrders)
├── lib/
│   ├── api/                # Clients HTTP tipados
│   └── utils/
├── types/                  # Tipos compartilhados
└── tests/
    ├── unit/
    └── integration/
```

## TypeScript — Padrões

```typescript
// types/order.ts
export type OrderStatus = 'pending' | 'confirmed' | 'cancelled';

export interface Order {
  readonly id: string;
  readonly customerId: string;
  readonly status: OrderStatus;
  readonly total: Money;
}

export interface Money {
  readonly amount: string; // Decimal como string
  readonly currency: string;
}

// Discriminated union para estados de UI
export type AsyncState<T> =
  | { status: 'idle' }
  | { status: 'loading' }
  | { status: 'success'; data: T }
  | { status: 'error'; error: string };
```

## Server vs Client Components

```tsx
// app/orders/page.tsx — Server Component (default)
import { fetchOrders } from '@/lib/api/orders';

export default async function OrdersPage() {
  const orders = await fetchOrders(); // fetch no servidor
  return <OrderList orders={orders} />;
}

// components/features/OrderActions.tsx — Client Component
'use client';

import { useState } from 'react';

export function OrderActions({ orderId }: { orderId: string }) {
  const [loading, setLoading] = useState(false);
  // interatividade, event handlers
}
```

## Data Fetching

```typescript
// lib/api/orders.ts
const API_BASE = process.env.NEXT_PUBLIC_API_URL!;

export async function fetchOrders(): Promise<Order[]> {
  const res = await fetch(`${API_BASE}/api/v1/orders`, {
    next: { revalidate: 60 }, // ISR
    headers: { Authorization: `Bearer ${await getToken()}` },
  });
  if (!res.ok) throw new ApiError(res.status, await res.text());
  return res.json();
}
```

## Testes — Vitest + Testing Library

```typescript
// tests/unit/components/OrderList.test.tsx
import { render, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { describe, it, expect, vi } from 'vitest';
import { OrderList } from '@/components/features/OrderList';

describe('OrderList', () => {
  it('should_display_orders_when_data_provided', () => {
    render(<OrderList orders={[buildOrder({ id: '1', status: 'pending' })]} />);
    expect(screen.getByText(/pending/i)).toBeInTheDocument();
  });

  it('should_call_onCancel_when_cancel_clicked', async () => {
    const onCancel = vi.fn();
    const user = userEvent.setup();
    render(<OrderList orders={[buildOrder()]} onCancel={onCancel} />);
    await user.click(screen.getByRole('button', { name: /cancel/i }));
    expect(onCancel).toHaveBeenCalledOnce();
  });
});
```

```typescript
// MSW para mock de API
import { http, HttpResponse } from 'msw';
import { setupServer } from 'msw/node';

const server = setupServer(
  http.get('/api/v1/orders', () => HttpResponse.json([{ id: '1', status: 'pending' }]))
);

beforeAll(() => server.listen());
afterEach(() => server.resetHandlers());
afterAll(() => server.close());
```

## Observabilidade Frontend

```typescript
// Sentry
import * as Sentry from '@sentry/nextjs';

Sentry.init({
  dsn: process.env.NEXT_PUBLIC_SENTRY_DSN,
  tracesSampleRate: 0.1,
  environment: process.env.NODE_ENV,
});

// Web Vitals
export function reportWebVitals(metric: NextWebVitalsMetric) {
  // enviar para analytics
  console.log(metric.name, metric.value);
}
```

## Segurança

```typescript
// next.config.ts — CSP
const securityHeaders = [
  {
    key: 'Content-Security-Policy',
    value: "default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline';",
  },
  { key: 'X-Frame-Options', value: 'DENY' },
  { key: 'X-Content-Type-Options', value: 'nosniff' },
];

// Sanitizar HTML de usuário
import DOMPurify from 'isomorphic-dompurify';
const safeHtml = DOMPurify.sanitize(userHtml);
```

## Anti-Patterns

| Anti-Pattern | Problema |
|---|---|
| `any` em todo lugar | Perde type safety |
| `useEffect` para fetch que Server Component resolve | Complexidade desnecessária |
| Estado global para tudo | Re-renders, acoplamento |
| Inline styles massivos | Manutenção difícil |
| Sem testes de interação | Regressões em UX |

## Checklist Frontend

- [ ] TypeScript strict sem erros
- [ ] Componentes testados (render + interação)
- [ ] Loading e error states tratados
- [ ] Acessibilidade básica (roles, labels)
- [ ] Sem secrets no bundle client-side
- [ ] CSP configurado
- [ ] Web Vitals monitorados

