* feat(inbox): skip AI extraction for multi-page PDFs (#553) Bedrock churns for minutes on multi-page PDFs (sales reports, bank statements, contracts) and returns nothing useful. Above 3 pages we now skip extraction entirely and mark the row with extraction_skipped=true; the document still lands in the inbox and can be attached or converted manually. Same gate applies to the /items/:id/attach path. Client can also opt out via skip_extraction=true (skip_reason=client_opt_out). The InvoiceInboxWorkspace renders an "Inte AI-tolkad" badge for skipped rows, distinct from the "Felaktig" failure state (status='error'). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * refactor(ui): introduce DataList + DropdownMenu primitives, roll out across list pages DataList replaces the per-row Card pattern across Granskning, Transactions, Invoices, Supplier invoices, and Pending. One bordered container with hairline rows matches the flat-with-hairlines aesthetic in CLAUDE.md — no shadows, no state-tinted borders, secondary token for selected/hover. DropdownMenu fills the gap for row-level action menus on TransactionInboxCard, TransactionHistoryList, and the page-level action menus on /transactions and /pending. Replaces ad-hoc Popover + buttons constructions. Migrates list pages and the transaction inbox/history components onto the new primitives. No behavior change beyond the visual unification. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * chore: add agent skills + gnubok domain skills, gitignore compliance reports .agents/skills/ + skills-lock.json + symlinks under .claude/skills/ check in the vercel-labs/agent-skills set pinned by the local skill manager (deploy-to-vercel, vercel-cli-with-tokens, react-best-practices, composition-patterns, react-native-skills, react-view-transitions, web-design-guidelines). Keeps the team on the same versions. .claude/skills/industry/ + .claude/skills/modifier/ are hand-authored vertical and entity-modifier skills for the specialized accountant agent — industries (konsult-it, e-handel, bygg-hantverk, reklambyra, saas-ai) and entity overlays (holding-ab, single-shareholder-ab-fmb, mixed- verksamhet). Project-owned content; lives in the repo by design. Also gitignores .compliance-reports/ — those are large generated SARIF/dossier artifacts from the compliance scanner. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * refactor(transactions): unify inbox/history chrome, drop swipe flow The transactions page mixed two-tier filtering, a swipe-view detour, and per-row Card chrome that didn't carry its weight. This pass collapses those into a single editorial-list surface and removes the unused swipe path entirely. User-visible changes: - Mode toggle (Att bokföra / Alla transaktioner) moved from a Tabs row under the header into a dropdown to the right of a unified search bar. Search now persists when switching modes. - Removed the swipe categorization view ("Gå igenom alla") and its trigger button. The 800-line SwipeCategorizationView component is deleted; suggestion-fetching shrinks to what the template picker still consumes. - Inbox rows now show one primary action: invoice/supplier-invoice match shortcut when auto-detected, else "Bokför". A new visible Link2 icon button opens the customer or supplier invoice picker manually (chosen by amount sign). Delete becomes a plain trash button — no overflow menu since it only ever held one item. - Bulk action bar swaps "Markera som privat" for "Ta bort" with a single combined confirmation. - Built SupplierInvoicePicker mirroring InvoicePicker so expense transactions can be matched to supplier invoices from the inbox. Wired through /api/transactions/{id}/match-supplier-invoice. - Template picker dialog renamed to "Bokför transaktion"; "Bokför manuellt…" and "Matcha med faktura…" promoted from muted ghost buttons at the bottom to outline buttons at the top, above the template list. - Breathing room: row padding py-3 → py-4, primary text text-sm → text-base, amount text-base, button heights h-8 → h-9, trailing gap-2 → gap-3 (in the DataList primitive itself, so every list benefits slightly). 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix: align package-lock.json with merged package.json The merge resolution took origin/main's package-lock.json (which dropped pdf-lib) but kept our package.json (which still requires pdf-lib for the invoice-inbox extension's PDFDocument import). `npm ci` rejected the mismatch. Regenerate the lock from the merged package.json so both files agree. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix: regenerate package-lock.json with npm@10 for CI compat Local npm@11 produced a lock that npm@10 (CI) rejected with "Missing: @swc/helpers@0.5.21". Regenerated with npm@10 --package-lock-only so CI can install. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(transactions): supplier-invoice status-leak guard + drop dead prop Two follow-ups from the merge-risk audit: - SupplierInvoicePicker now mirrors InvoicePicker's status-leak guard: if a supplier invoice is still 'approved'/'overdue' but already has a payment voucher attached (journal_entry_id on supplier_invoice_payments), hide it. Closes a UX race window between payment and status flip. Partially-paid invoices still pass through. - Drop the unused onMarkPrivate prop on TransactionInboxCard and the matching handleMarkPrivate wrapper in the parent. Both became dead when the swipe-categorisation flow was removed. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
150 lines
7.1 KiB
Markdown
150 lines
7.1 KiB
Markdown
---
|
|
name: vercel-react-best-practices
|
|
description: React and Next.js performance optimization guidelines from Vercel Engineering. This skill should be used when writing, reviewing, or refactoring React/Next.js code to ensure optimal performance patterns. Triggers on tasks involving React components, Next.js pages, data fetching, bundle optimization, or performance improvements.
|
|
license: MIT
|
|
metadata:
|
|
author: vercel
|
|
version: "1.0.0"
|
|
---
|
|
|
|
# Vercel React Best Practices
|
|
|
|
Comprehensive performance optimization guide for React and Next.js applications, maintained by Vercel. Contains 70 rules across 8 categories, prioritized by impact to guide automated refactoring and code generation.
|
|
|
|
## When to Apply
|
|
|
|
Reference these guidelines when:
|
|
- Writing new React components or Next.js pages
|
|
- Implementing data fetching (client or server-side)
|
|
- Reviewing code for performance issues
|
|
- Refactoring existing React/Next.js code
|
|
- Optimizing bundle size or load times
|
|
|
|
## Rule Categories by Priority
|
|
|
|
| Priority | Category | Impact | Prefix |
|
|
|----------|----------|--------|--------|
|
|
| 1 | Eliminating Waterfalls | CRITICAL | `async-` |
|
|
| 2 | Bundle Size Optimization | CRITICAL | `bundle-` |
|
|
| 3 | Server-Side Performance | HIGH | `server-` |
|
|
| 4 | Client-Side Data Fetching | MEDIUM-HIGH | `client-` |
|
|
| 5 | Re-render Optimization | MEDIUM | `rerender-` |
|
|
| 6 | Rendering Performance | MEDIUM | `rendering-` |
|
|
| 7 | JavaScript Performance | LOW-MEDIUM | `js-` |
|
|
| 8 | Advanced Patterns | LOW | `advanced-` |
|
|
|
|
## Quick Reference
|
|
|
|
### 1. Eliminating Waterfalls (CRITICAL)
|
|
|
|
- `async-cheap-condition-before-await` - Check cheap sync conditions before awaiting flags or remote values
|
|
- `async-defer-await` - Move await into branches where actually used
|
|
- `async-parallel` - Use Promise.all() for independent operations
|
|
- `async-dependencies` - Use better-all for partial dependencies
|
|
- `async-api-routes` - Start promises early, await late in API routes
|
|
- `async-suspense-boundaries` - Use Suspense to stream content
|
|
|
|
### 2. Bundle Size Optimization (CRITICAL)
|
|
|
|
- `bundle-barrel-imports` - Import directly, avoid barrel files
|
|
- `bundle-analyzable-paths` - Prefer statically analyzable import and file-system paths to avoid broad bundles and traces
|
|
- `bundle-dynamic-imports` - Use next/dynamic for heavy components
|
|
- `bundle-defer-third-party` - Load analytics/logging after hydration
|
|
- `bundle-conditional` - Load modules only when feature is activated
|
|
- `bundle-preload` - Preload on hover/focus for perceived speed
|
|
|
|
### 3. Server-Side Performance (HIGH)
|
|
|
|
- `server-auth-actions` - Authenticate server actions like API routes
|
|
- `server-cache-react` - Use React.cache() for per-request deduplication
|
|
- `server-cache-lru` - Use LRU cache for cross-request caching
|
|
- `server-dedup-props` - Avoid duplicate serialization in RSC props
|
|
- `server-hoist-static-io` - Hoist static I/O (fonts, logos) to module level
|
|
- `server-no-shared-module-state` - Avoid module-level mutable request state in RSC/SSR
|
|
- `server-serialization` - Minimize data passed to client components
|
|
- `server-parallel-fetching` - Restructure components to parallelize fetches
|
|
- `server-parallel-nested-fetching` - Chain nested fetches per item in Promise.all
|
|
- `server-after-nonblocking` - Use after() for non-blocking operations
|
|
|
|
### 4. Client-Side Data Fetching (MEDIUM-HIGH)
|
|
|
|
- `client-swr-dedup` - Use SWR for automatic request deduplication
|
|
- `client-event-listeners` - Deduplicate global event listeners
|
|
- `client-passive-event-listeners` - Use passive listeners for scroll
|
|
- `client-localstorage-schema` - Version and minimize localStorage data
|
|
|
|
### 5. Re-render Optimization (MEDIUM)
|
|
|
|
- `rerender-defer-reads` - Don't subscribe to state only used in callbacks
|
|
- `rerender-memo` - Extract expensive work into memoized components
|
|
- `rerender-memo-with-default-value` - Hoist default non-primitive props
|
|
- `rerender-dependencies` - Use primitive dependencies in effects
|
|
- `rerender-derived-state` - Subscribe to derived booleans, not raw values
|
|
- `rerender-derived-state-no-effect` - Derive state during render, not effects
|
|
- `rerender-functional-setstate` - Use functional setState for stable callbacks
|
|
- `rerender-lazy-state-init` - Pass function to useState for expensive values
|
|
- `rerender-simple-expression-in-memo` - Avoid memo for simple primitives
|
|
- `rerender-split-combined-hooks` - Split hooks with independent dependencies
|
|
- `rerender-move-effect-to-event` - Put interaction logic in event handlers
|
|
- `rerender-transitions` - Use startTransition for non-urgent updates
|
|
- `rerender-use-deferred-value` - Defer expensive renders to keep input responsive
|
|
- `rerender-use-ref-transient-values` - Use refs for transient frequent values
|
|
- `rerender-no-inline-components` - Don't define components inside components
|
|
|
|
### 6. Rendering Performance (MEDIUM)
|
|
|
|
- `rendering-animate-svg-wrapper` - Animate div wrapper, not SVG element
|
|
- `rendering-content-visibility` - Use content-visibility for long lists
|
|
- `rendering-hoist-jsx` - Extract static JSX outside components
|
|
- `rendering-svg-precision` - Reduce SVG coordinate precision
|
|
- `rendering-hydration-no-flicker` - Use inline script for client-only data
|
|
- `rendering-hydration-suppress-warning` - Suppress expected mismatches
|
|
- `rendering-activity` - Use Activity component for show/hide
|
|
- `rendering-conditional-render` - Use ternary, not && for conditionals
|
|
- `rendering-usetransition-loading` - Prefer useTransition for loading state
|
|
- `rendering-resource-hints` - Use React DOM resource hints for preloading
|
|
- `rendering-script-defer-async` - Use defer or async on script tags
|
|
|
|
### 7. JavaScript Performance (LOW-MEDIUM)
|
|
|
|
- `js-batch-dom-css` - Group CSS changes via classes or cssText
|
|
- `js-index-maps` - Build Map for repeated lookups
|
|
- `js-cache-property-access` - Cache object properties in loops
|
|
- `js-cache-function-results` - Cache function results in module-level Map
|
|
- `js-cache-storage` - Cache localStorage/sessionStorage reads
|
|
- `js-combine-iterations` - Combine multiple filter/map into one loop
|
|
- `js-length-check-first` - Check array length before expensive comparison
|
|
- `js-early-exit` - Return early from functions
|
|
- `js-hoist-regexp` - Hoist RegExp creation outside loops
|
|
- `js-min-max-loop` - Use loop for min/max instead of sort
|
|
- `js-set-map-lookups` - Use Set/Map for O(1) lookups
|
|
- `js-tosorted-immutable` - Use toSorted() for immutability
|
|
- `js-flatmap-filter` - Use flatMap to map and filter in one pass
|
|
- `js-request-idle-callback` - Defer non-critical work to browser idle time
|
|
|
|
### 8. Advanced Patterns (LOW)
|
|
|
|
- `advanced-effect-event-deps` - Don't put `useEffectEvent` results in effect deps
|
|
- `advanced-event-handler-refs` - Store event handlers in refs
|
|
- `advanced-init-once` - Initialize app once per app load
|
|
- `advanced-use-latest` - useLatest for stable callback refs
|
|
|
|
## How to Use
|
|
|
|
Read individual rule files for detailed explanations and code examples:
|
|
|
|
```
|
|
rules/async-parallel.md
|
|
rules/bundle-barrel-imports.md
|
|
```
|
|
|
|
Each rule file contains:
|
|
- Brief explanation of why it matters
|
|
- Incorrect code example with explanation
|
|
- Correct code example with explanation
|
|
- Additional context and references
|
|
|
|
## Full Compiled Document
|
|
|
|
For the complete guide with all rules expanded: `AGENTS.md`
|