cfsa-antigravity 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/cli.mjs +324 -0
- package/package.json +34 -0
- package/template/.agent/instructions/commands.md +48 -0
- package/template/.agent/instructions/patterns.md +61 -0
- package/template/.agent/instructions/structure.md +29 -0
- package/template/.agent/instructions/tech-stack.md +43 -0
- package/template/.agent/instructions/workflow.md +41 -0
- package/template/.agent/kit-sync.md +15 -0
- package/template/.agent/rules/boundary-not-placeholder.md +146 -0
- package/template/.agent/rules/completion-checklist.md +48 -0
- package/template/.agent/rules/decision-classification.md +103 -0
- package/template/.agent/rules/extensibility.md +47 -0
- package/template/.agent/rules/question-vs-command.md +81 -0
- package/template/.agent/rules/security-first.md +43 -0
- package/template/.agent/rules/specificity-standards.md +54 -0
- package/template/.agent/rules/tdd-contract-first.md +57 -0
- package/template/.agent/rules/vertical-slices.md +42 -0
- package/template/.agent/skill-library/MANIFEST.md +480 -0
- package/template/.agent/skill-library/README.md +38 -0
- package/template/.agent/skill-library/meta/brand-guidelines/SKILL.md +73 -0
- package/template/.agent/skill-library/meta/claude-code/README.md +9 -0
- package/template/.agent/skill-library/meta/claude-code/agent-development/SKILL.md +415 -0
- package/template/.agent/skill-library/meta/claude-code/hook-development/SKILL.md +712 -0
- package/template/.agent/skill-library/meta/claude-code/plugin-structure/SKILL.md +476 -0
- package/template/.agent/skill-library/meta/git-advanced/SKILL.md +972 -0
- package/template/.agent/skill-library/meta/mcp-builder/SKILL.md +236 -0
- package/template/.agent/skill-library/meta/product-marketing-context/SKILL.md +241 -0
- package/template/.agent/skill-library/meta/regex-patterns/SKILL.md +751 -0
- package/template/.agent/skill-library/meta/tmux-processes/SKILL.md +210 -0
- package/template/.agent/skill-library/meta/using-tmux-for-interactive-commands/SKILL.md +178 -0
- package/template/.agent/skill-library/stack/3d/threejs-pro/SKILL.md +300 -0
- package/template/.agent/skill-library/stack/ai/ai-sdk/SKILL.md +77 -0
- package/template/.agent/skill-library/stack/ai/langchain/SKILL.md +530 -0
- package/template/.agent/skill-library/stack/ai/ollama/SKILL.md +321 -0
- package/template/.agent/skill-library/stack/ai/openai-sdk/SKILL.md +549 -0
- package/template/.agent/skill-library/stack/analytics/google-analytics/SKILL.md +153 -0
- package/template/.agent/skill-library/stack/api/graphql/SKILL.md +1061 -0
- package/template/.agent/skill-library/stack/api/trpc/SKILL.md +576 -0
- package/template/.agent/skill-library/stack/auth/authjs/SKILL.md +569 -0
- package/template/.agent/skill-library/stack/auth/clerk/SKILL.md +590 -0
- package/template/.agent/skill-library/stack/auth/firebase-auth/SKILL.md +734 -0
- package/template/.agent/skill-library/stack/cms/payload-cms/SKILL.md +573 -0
- package/template/.agent/skill-library/stack/cms/shopify/SKILL.md +1193 -0
- package/template/.agent/skill-library/stack/cms/wordpress/SKILL.md +1104 -0
- package/template/.agent/skill-library/stack/css/sass-scss/SKILL.md +1121 -0
- package/template/.agent/skill-library/stack/css/tailwind-css-patterns/SKILL.md +863 -0
- package/template/.agent/skill-library/stack/css/tailwind-design-system/SKILL.md +490 -0
- package/template/.agent/skill-library/stack/css/vanilla-css/SKILL.md +1078 -0
- package/template/.agent/skill-library/stack/databases/clickhouse/SKILL.md +311 -0
- package/template/.agent/skill-library/stack/databases/influxdb/SKILL.md +280 -0
- package/template/.agent/skill-library/stack/databases/lancedb/SKILL.md +415 -0
- package/template/.agent/skill-library/stack/databases/mongodb/SKILL.md +1169 -0
- package/template/.agent/skill-library/stack/databases/neo4j/SKILL.md +839 -0
- package/template/.agent/skill-library/stack/databases/pgvector/SKILL.md +241 -0
- package/template/.agent/skill-library/stack/databases/pinecone/SKILL.md +212 -0
- package/template/.agent/skill-library/stack/databases/postgresql/SKILL.md +658 -0
- package/template/.agent/skill-library/stack/databases/qdrant/SKILL.md +312 -0
- package/template/.agent/skill-library/stack/databases/redis/SKILL.md +1079 -0
- package/template/.agent/skill-library/stack/databases/spacetimedb/SKILL.md +532 -0
- package/template/.agent/skill-library/stack/databases/sqlite/SKILL.md +1132 -0
- package/template/.agent/skill-library/stack/databases/supabase/SKILL.md +640 -0
- package/template/.agent/skill-library/stack/databases/surrealdb-expert/SKILL.md +945 -0
- package/template/.agent/skill-library/stack/databases/timescaledb/SKILL.md +745 -0
- package/template/.agent/skill-library/stack/databases/weaviate/SKILL.md +218 -0
- package/template/.agent/skill-library/stack/devops/github-actions/SKILL.md +554 -0
- package/template/.agent/skill-library/stack/devops/kubernetes/SKILL.md +950 -0
- package/template/.agent/skill-library/stack/devops/nginx/SKILL.md +841 -0
- package/template/.agent/skill-library/stack/devops/terraform/SKILL.md +860 -0
- package/template/.agent/skill-library/stack/email/resend/SKILL.md +391 -0
- package/template/.agent/skill-library/stack/engines/godot/SKILL.md +488 -0
- package/template/.agent/skill-library/stack/extensions/chrome-extension/SKILL.md +375 -0
- package/template/.agent/skill-library/stack/extensions/vscode-extension/SKILL.md +453 -0
- package/template/.agent/skill-library/stack/frameworks/astro-framework/SKILL.md +162 -0
- package/template/.agent/skill-library/stack/frameworks/electron/SKILL.md +1286 -0
- package/template/.agent/skill-library/stack/frameworks/fastapi/SKILL.md +650 -0
- package/template/.agent/skill-library/stack/frameworks/hono/SKILL.md +90 -0
- package/template/.agent/skill-library/stack/frameworks/nestjs/SKILL.md +878 -0
- package/template/.agent/skill-library/stack/frameworks/nextjs/SKILL.md +635 -0
- package/template/.agent/skill-library/stack/frameworks/nuxt/SKILL.md +564 -0
- package/template/.agent/skill-library/stack/frameworks/sveltekit/SKILL.md +614 -0
- package/template/.agent/skill-library/stack/frameworks/tauri/SKILL.md +920 -0
- package/template/.agent/skill-library/stack/gamedev/godot/SKILL.md +1032 -0
- package/template/.agent/skill-library/stack/gamedev/unity/SKILL.md +1175 -0
- package/template/.agent/skill-library/stack/hosting/aws/SKILL.md +467 -0
- package/template/.agent/skill-library/stack/hosting/cloudflare/SKILL.md +201 -0
- package/template/.agent/skill-library/stack/hosting/docker-expert/SKILL.md +409 -0
- package/template/.agent/skill-library/stack/hosting/vercel/SKILL.md +484 -0
- package/template/.agent/skill-library/stack/languages/bash-scripting/SKILL.md +773 -0
- package/template/.agent/skill-library/stack/languages/c-cpp/SKILL.md +712 -0
- package/template/.agent/skill-library/stack/languages/gdscript/SKILL.md +789 -0
- package/template/.agent/skill-library/stack/languages/go/SKILL.md +664 -0
- package/template/.agent/skill-library/stack/languages/java/SKILL.md +778 -0
- package/template/.agent/skill-library/stack/languages/kotlin/SKILL.md +665 -0
- package/template/.agent/skill-library/stack/languages/python/SKILL.md +678 -0
- package/template/.agent/skill-library/stack/languages/rust/SKILL.md +673 -0
- package/template/.agent/skill-library/stack/languages/typescript-advanced-patterns/SKILL.md +141 -0
- package/template/.agent/skill-library/stack/languages/typescript-advanced-patterns/references/advanced-generics.md +90 -0
- package/template/.agent/skill-library/stack/languages/typescript-advanced-patterns/references/branded-types.md +57 -0
- package/template/.agent/skill-library/stack/languages/typescript-advanced-patterns/references/builder-pattern.md +71 -0
- package/template/.agent/skill-library/stack/languages/typescript-advanced-patterns/references/common-pitfalls.md +135 -0
- package/template/.agent/skill-library/stack/languages/typescript-advanced-patterns/references/conditional-types.md +27 -0
- package/template/.agent/skill-library/stack/languages/typescript-advanced-patterns/references/decorators.md +98 -0
- package/template/.agent/skill-library/stack/languages/typescript-advanced-patterns/references/discriminated-unions.md +62 -0
- package/template/.agent/skill-library/stack/languages/typescript-advanced-patterns/references/mapped-types.md +53 -0
- package/template/.agent/skill-library/stack/languages/typescript-advanced-patterns/references/performance-best-practices.md +104 -0
- package/template/.agent/skill-library/stack/languages/typescript-advanced-patterns/references/template-literal-types.md +49 -0
- package/template/.agent/skill-library/stack/languages/typescript-advanced-patterns/references/testing-types.md +112 -0
- package/template/.agent/skill-library/stack/languages/typescript-advanced-patterns/references/type-guards.md +70 -0
- package/template/.agent/skill-library/stack/languages/typescript-advanced-patterns/references/type-inference.md +101 -0
- package/template/.agent/skill-library/stack/languages/typescript-advanced-patterns/references/utility-types.md +98 -0
- package/template/.agent/skill-library/stack/languages/vanilla-javascript/SKILL.md +803 -0
- package/template/.agent/skill-library/stack/messaging/kafka/SKILL.md +235 -0
- package/template/.agent/skill-library/stack/mobile/expo-react-native/SKILL.md +665 -0
- package/template/.agent/skill-library/stack/mobile/flutter/SKILL.md +316 -0
- package/template/.agent/skill-library/stack/mobile/react-native/SKILL.md +337 -0
- package/template/.agent/skill-library/stack/monitoring/posthog/SKILL.md +396 -0
- package/template/.agent/skill-library/stack/monitoring/sentry/SKILL.md +509 -0
- package/template/.agent/skill-library/stack/observability/datadog/SKILL.md +179 -0
- package/template/.agent/skill-library/stack/observability/distributed-tracing/SKILL.md +140 -0
- package/template/.agent/skill-library/stack/observability/logging-best-practices/SKILL.md +168 -0
- package/template/.agent/skill-library/stack/observability/opentelemetry/SKILL.md +164 -0
- package/template/.agent/skill-library/stack/observability/prometheus-grafana/SKILL.md +246 -0
- package/template/.agent/skill-library/stack/observability/python-observability/SKILL.md +158 -0
- package/template/.agent/skill-library/stack/orm/drizzle-orm/SKILL.md +613 -0
- package/template/.agent/skill-library/stack/orm/prisma/SKILL.md +744 -0
- package/template/.agent/skill-library/stack/payments/lemonsqueezy/SKILL.md +393 -0
- package/template/.agent/skill-library/stack/payments/stripe-integration/SKILL.md +457 -0
- package/template/.agent/skill-library/stack/queue/bullmq/SKILL.md +385 -0
- package/template/.agent/skill-library/stack/queue/inngest/SKILL.md +438 -0
- package/template/.agent/skill-library/stack/realtime/socketio/SKILL.md +595 -0
- package/template/.agent/skill-library/stack/search/elasticsearch/SKILL.md +248 -0
- package/template/.agent/skill-library/stack/search/meilisearch/SKILL.md +385 -0
- package/template/.agent/skill-library/stack/security/crypto-patterns/SKILL.md +437 -0
- package/template/.agent/skill-library/stack/security/csp-cors-headers/SKILL.md +588 -0
- package/template/.agent/skill-library/stack/security/dependency-auditing/SKILL.md +560 -0
- package/template/.agent/skill-library/stack/security/input-sanitization/SKILL.md +430 -0
- package/template/.agent/skill-library/stack/security/owasp-web-security/SKILL.md +421 -0
- package/template/.agent/skill-library/stack/state/tanstack-query/SKILL.md +637 -0
- package/template/.agent/skill-library/stack/state/zustand/SKILL.md +483 -0
- package/template/.agent/skill-library/stack/storage/aws-s3/SKILL.md +415 -0
- package/template/.agent/skill-library/stack/testing/playwright/SKILL.md +641 -0
- package/template/.agent/skill-library/stack/testing/storybook/SKILL.md +923 -0
- package/template/.agent/skill-library/stack/testing/testing-library/SKILL.md +872 -0
- package/template/.agent/skill-library/stack/testing/vitest/SKILL.md +714 -0
- package/template/.agent/skill-library/stack/ui/react-best-practices/SKILL.md +877 -0
- package/template/.agent/skill-library/stack/ui/react-composition-patterns/SKILL.md +1107 -0
- package/template/.agent/skill-library/stack/ui/react-flow/SKILL.md +425 -0
- package/template/.agent/skill-library/stack/ui/shadcn-ui/SKILL.md +703 -0
- package/template/.agent/skill-library/surface/api/api-caching/SKILL.md +458 -0
- package/template/.agent/skill-library/surface/api/api-documentation-openapi/SKILL.md +697 -0
- package/template/.agent/skill-library/surface/api/api-error-handling/SKILL.md +478 -0
- package/template/.agent/skill-library/surface/api/api-security-checklist/SKILL.md +147 -0
- package/template/.agent/skill-library/surface/api/api-versioning/SKILL.md +420 -0
- package/template/.agent/skill-library/surface/api/email-best-practices/SKILL.md +59 -0
- package/template/.agent/skill-library/surface/api/rate-limiting-abuse-protection/SKILL.md +147 -0
- package/template/.agent/skill-library/surface/api/rest-api-design/SKILL.md +478 -0
- package/template/.agent/skill-library/surface/api/webhook-design/SKILL.md +752 -0
- package/template/.agent/skill-library/surface/cli/cli-configuration-management/SKILL.md +445 -0
- package/template/.agent/skill-library/surface/cli/cli-error-diagnostics/SKILL.md +515 -0
- package/template/.agent/skill-library/surface/cli/cli-shell-integration/SKILL.md +479 -0
- package/template/.agent/skill-library/surface/cli/cli-ux-design/SKILL.md +477 -0
- package/template/.agent/skill-library/surface/desktop/desktop-app-distribution/SKILL.md +416 -0
- package/template/.agent/skill-library/surface/desktop/desktop-security-sandboxing/SKILL.md +407 -0
- package/template/.agent/skill-library/surface/desktop/desktop-ux-conventions/SKILL.md +361 -0
- package/template/.agent/skill-library/surface/desktop/native-os-integration/SKILL.md +563 -0
- package/template/.agent/skill-library/surface/extension/browser-extension-patterns/SKILL.md +482 -0
- package/template/.agent/skill-library/surface/extension/plugin-architecture-design/SKILL.md +632 -0
- package/template/.agent/skill-library/surface/extension/vscode-extension-development/SKILL.md +728 -0
- package/template/.agent/skill-library/surface/mobile/app-store-submission/SKILL.md +304 -0
- package/template/.agent/skill-library/surface/mobile/mobile-offline-sync/SKILL.md +443 -0
- package/template/.agent/skill-library/surface/mobile/mobile-responsive-patterns/SKILL.md +432 -0
- package/template/.agent/skill-library/surface/mobile/push-notifications/SKILL.md +495 -0
- package/template/.agent/skill-library/surface/web/accessibility-compliance/SKILL.md +827 -0
- package/template/.agent/skill-library/surface/web/ai-seo/SKILL.md +398 -0
- package/template/.agent/skill-library/surface/web/ai-seo/references/content-patterns.md +285 -0
- package/template/.agent/skill-library/surface/web/ai-seo/references/platform-ranking-factors.md +152 -0
- package/template/.agent/skill-library/surface/web/analytics-tracking/SKILL.md +309 -0
- package/template/.agent/skill-library/surface/web/analytics-tracking/references/event-library.md +260 -0
- package/template/.agent/skill-library/surface/web/analytics-tracking/references/ga4-implementation.md +300 -0
- package/template/.agent/skill-library/surface/web/analytics-tracking/references/gtm-implementation.md +390 -0
- package/template/.agent/skill-library/surface/web/authentication-ui-flows/SKILL.md +530 -0
- package/template/.agent/skill-library/surface/web/dark-mode-theming/SKILL.md +516 -0
- package/template/.agent/skill-library/surface/web/design-reference-data/SKILL.md +105 -0
- package/template/.agent/skill-library/surface/web/design-reference-data/data/charts.csv +26 -0
- package/template/.agent/skill-library/surface/web/design-reference-data/data/colors.csv +97 -0
- package/template/.agent/skill-library/surface/web/design-reference-data/data/landing.csv +31 -0
- package/template/.agent/skill-library/surface/web/design-reference-data/data/styles.csv +59 -0
- package/template/.agent/skill-library/surface/web/design-reference-data/data/typography.csv +58 -0
- package/template/.agent/skill-library/surface/web/design-reference-data/data/ux-guidelines.csv +100 -0
- package/template/.agent/skill-library/surface/web/design-reference-data/scripts/core.py +258 -0
- package/template/.agent/skill-library/surface/web/design-reference-data/scripts/design_system.py +1067 -0
- package/template/.agent/skill-library/surface/web/design-reference-data/scripts/search.py +106 -0
- package/template/.agent/skill-library/surface/web/form-handling-validation/SKILL.md +675 -0
- package/template/.agent/skill-library/surface/web/frontend-design/SKILL.md +1393 -0
- package/template/.agent/skill-library/surface/web/frontend-design/templates/cppn-hero.tsx +299 -0
- package/template/.agent/skill-library/surface/web/frontend-design/templates/wave-hero.tsx +875 -0
- package/template/.agent/skill-library/surface/web/frontend-verification/SKILL.md +111 -0
- package/template/.agent/skill-library/surface/web/frontend-verification/scripts/ux_audit.py +739 -0
- package/template/.agent/skill-library/surface/web/i18n-localization/SKILL.md +154 -0
- package/template/.agent/skill-library/surface/web/offline-first-pwa/SKILL.md +657 -0
- package/template/.agent/skill-library/surface/web/page-cro/SKILL.md +182 -0
- package/template/.agent/skill-library/surface/web/page-cro/references/experiments.md +248 -0
- package/template/.agent/skill-library/surface/web/programmatic-seo/SKILL.md +238 -0
- package/template/.agent/skill-library/surface/web/programmatic-seo/references/playbooks.md +308 -0
- package/template/.agent/skill-library/surface/web/schema-markup/SKILL.md +179 -0
- package/template/.agent/skill-library/surface/web/schema-markup/references/schema-examples.md +398 -0
- package/template/.agent/skill-library/surface/web/seo-audit/SKILL.md +394 -0
- package/template/.agent/skill-library/surface/web/seo-audit/references/ai-writing-detection.md +200 -0
- package/template/.agent/skill-library/surface/web/web-performance-optimization/SKILL.md +646 -0
- package/template/.agent/skill-library/surface/web/web-scraping/SKILL.md +58 -0
- package/template/.agent/skills/accessibility/SKILL.md +522 -0
- package/template/.agent/skills/accessibility/references/WCAG.md +162 -0
- package/template/.agent/skills/adversarial-review/SKILL.md +90 -0
- package/template/.agent/skills/antigravity-workflows/SKILL.md +81 -0
- package/template/.agent/skills/antigravity-workflows/resources/implementation-playbook.md +36 -0
- package/template/.agent/skills/api-design-principles/SKILL.md +37 -0
- package/template/.agent/skills/api-design-principles/assets/api-design-checklist.md +155 -0
- package/template/.agent/skills/api-design-principles/assets/rest-api-template.py +182 -0
- package/template/.agent/skills/api-design-principles/references/graphql-schema-design.md +583 -0
- package/template/.agent/skills/api-design-principles/references/rest-best-practices.md +408 -0
- package/template/.agent/skills/api-design-principles/resources/implementation-playbook.md +513 -0
- package/template/.agent/skills/api-versioning/SKILL.md +420 -0
- package/template/.agent/skills/architecture-mapping/SKILL.md +219 -0
- package/template/.agent/skills/bootstrap-agents/SKILL.md +259 -0
- package/template/.agent/skills/brainstorming/SKILL.md +236 -0
- package/template/.agent/skills/brand-guidelines/SKILL.md +44 -0
- package/template/.agent/skills/clean-code/SKILL.md +94 -0
- package/template/.agent/skills/code-review-pro/SKILL.md +152 -0
- package/template/.agent/skills/concise-planning/SKILL.md +68 -0
- package/template/.agent/skills/cross-layer-consistency/SKILL.md +117 -0
- package/template/.agent/skills/database-schema-design/SKILL.md +429 -0
- package/template/.agent/skills/deployment-procedures/SKILL.md +241 -0
- package/template/.agent/skills/design-anti-cliche/SKILL.md +159 -0
- package/template/.agent/skills/design-direction/SKILL.md +45 -0
- package/template/.agent/skills/error-handling-patterns/SKILL.md +721 -0
- package/template/.agent/skills/find-skills/SKILL.md +145 -0
- package/template/.agent/skills/git-advanced/SKILL.md +972 -0
- package/template/.agent/skills/git-workflow/SKILL.md +420 -0
- package/template/.agent/skills/idea-extraction/SKILL.md +271 -0
- package/template/.agent/skills/logging-best-practices/SKILL.md +851 -0
- package/template/.agent/skills/migration-management/SKILL.md +384 -0
- package/template/.agent/skills/minimalist-surgical-development/SKILL.md +69 -0
- package/template/.agent/skills/parallel-agents/SKILL.md +165 -0
- package/template/.agent/skills/parallel-debugging/SKILL.md +135 -0
- package/template/.agent/skills/parallel-feature-development/SKILL.md +166 -0
- package/template/.agent/skills/performance-budgeting/SKILL.md +144 -0
- package/template/.agent/skills/pipeline-rubrics/SKILL.md +51 -0
- package/template/.agent/skills/pipeline-rubrics/references/architecture-rubric.md +19 -0
- package/template/.agent/skills/pipeline-rubrics/references/be-rubric.md +21 -0
- package/template/.agent/skills/pipeline-rubrics/references/fe-rubric.md +20 -0
- package/template/.agent/skills/pipeline-rubrics/references/ia-rubric.md +19 -0
- package/template/.agent/skills/pipeline-rubrics/references/scoring.md +28 -0
- package/template/.agent/skills/pipeline-rubrics/references/vision-rubric.md +11 -0
- package/template/.agent/skills/prd-templates/SKILL.md +88 -0
- package/template/.agent/skills/prd-templates/references/architecture-design-template.md +88 -0
- package/template/.agent/skills/prd-templates/references/be-spec-template.md +101 -0
- package/template/.agent/skills/prd-templates/references/data-placement-template.md +74 -0
- package/template/.agent/skills/prd-templates/references/decomposition-templates.md +211 -0
- package/template/.agent/skills/prd-templates/references/design-system-decisions.md +198 -0
- package/template/.agent/skills/prd-templates/references/engineering-standards-template.md +124 -0
- package/template/.agent/skills/prd-templates/references/fe-classification-procedures.md +47 -0
- package/template/.agent/skills/prd-templates/references/fe-spec-template.md +84 -0
- package/template/.agent/skills/prd-templates/references/infrastructure-report-template.md +71 -0
- package/template/.agent/skills/prd-templates/references/operational-templates.md +116 -0
- package/template/.agent/skills/prd-templates/references/placeholder-guard-template.md +21 -0
- package/template/.agent/skills/prd-templates/references/surface-model.md +61 -0
- package/template/.agent/skills/prd-templates/references/vision-template.md +66 -0
- package/template/.agent/skills/prompt-engineer/README.md +659 -0
- package/template/.agent/skills/prompt-engineer/SKILL.md +249 -0
- package/template/.agent/skills/regex-patterns/SKILL.md +751 -0
- package/template/.agent/skills/resolve-ambiguity/SKILL.md +278 -0
- package/template/.agent/skills/rest-api-design/SKILL.md +478 -0
- package/template/.agent/skills/security-scanning-security-hardening/SKILL.md +231 -0
- package/template/.agent/skills/session-continuity/SKILL.md +730 -0
- package/template/.agent/skills/session-continuity/protocols/01-session-resumption.md +38 -0
- package/template/.agent/skills/session-continuity/protocols/02-progress-generation.md +85 -0
- package/template/.agent/skills/session-continuity/protocols/03-progress-update.md +70 -0
- package/template/.agent/skills/session-continuity/protocols/04-pattern-extraction.md +60 -0
- package/template/.agent/skills/session-continuity/protocols/05-session-close.md +37 -0
- package/template/.agent/skills/session-continuity/protocols/06-decision-analysis.md +84 -0
- package/template/.agent/skills/session-continuity/protocols/07-spec-pipeline-generation.md +48 -0
- package/template/.agent/skills/session-continuity/protocols/08-spec-pipeline-update.md +43 -0
- package/template/.agent/skills/session-continuity/protocols/09-parallel-claim.md +122 -0
- package/template/.agent/skills/session-continuity/protocols/10-placeholder-verification-gate.md +104 -0
- package/template/.agent/skills/session-continuity/protocols/ambiguity-gates.md +48 -0
- package/template/.agent/skills/skill-creator/LICENSE.txt +202 -0
- package/template/.agent/skills/skill-creator/README.md +270 -0
- package/template/.agent/skills/skill-creator/SKILL.md +590 -0
- package/template/.agent/skills/skill-creator/references/output-patterns.md +82 -0
- package/template/.agent/skills/skill-creator/references/workflows.md +28 -0
- package/template/.agent/skills/skill-creator/scripts/init_skill.py +303 -0
- package/template/.agent/skills/skill-creator/scripts/package_skill.py +110 -0
- package/template/.agent/skills/skill-creator/scripts/quick_validate.py +95 -0
- package/template/.agent/skills/spec-writing/SKILL.md +110 -0
- package/template/.agent/skills/systematic-debugging/CREATION-LOG.md +119 -0
- package/template/.agent/skills/systematic-debugging/SKILL.md +297 -0
- package/template/.agent/skills/systematic-debugging/condition-based-waiting-example.ts +158 -0
- package/template/.agent/skills/systematic-debugging/condition-based-waiting.md +115 -0
- package/template/.agent/skills/systematic-debugging/defense-in-depth.md +122 -0
- package/template/.agent/skills/systematic-debugging/find-polluter.sh +63 -0
- package/template/.agent/skills/systematic-debugging/root-cause-tracing.md +169 -0
- package/template/.agent/skills/systematic-debugging/test-academic.md +14 -0
- package/template/.agent/skills/systematic-debugging/test-pressure-1.md +58 -0
- package/template/.agent/skills/systematic-debugging/test-pressure-2.md +68 -0
- package/template/.agent/skills/systematic-debugging/test-pressure-3.md +69 -0
- package/template/.agent/skills/tdd-workflow/SKILL.md +409 -0
- package/template/.agent/skills/tech-stack-catalog/SKILL.md +49 -0
- package/template/.agent/skills/tech-stack-catalog/references/constraint-questions.md +21 -0
- package/template/.agent/skills/tech-stack-catalog/references/dev-tooling-decisions.md +37 -0
- package/template/.agent/skills/tech-stack-catalog/references/surface-decision-tables.md +69 -0
- package/template/.agent/skills/technical-writer/SKILL.md +242 -0
- package/template/.agent/skills/testing-strategist/SKILL.md +932 -0
- package/template/.agent/skills/verification-before-completion/SKILL.md +145 -0
- package/template/.agent/skills/workflow-automation/SKILL.md +73 -0
- package/template/.agent/workflows/audit-ambiguity-execute.md +165 -0
- package/template/.agent/workflows/audit-ambiguity-rubrics.md +83 -0
- package/template/.agent/workflows/audit-ambiguity.md +64 -0
- package/template/.agent/workflows/bootstrap-agents-fill.md +201 -0
- package/template/.agent/workflows/bootstrap-agents-provision.md +197 -0
- package/template/.agent/workflows/bootstrap-agents.md +66 -0
- package/template/.agent/workflows/create-prd-architecture.md +119 -0
- package/template/.agent/workflows/create-prd-compile.md +138 -0
- package/template/.agent/workflows/create-prd-design-system.md +135 -0
- package/template/.agent/workflows/create-prd-security.md +113 -0
- package/template/.agent/workflows/create-prd-stack.md +91 -0
- package/template/.agent/workflows/create-prd.md +168 -0
- package/template/.agent/workflows/decompose-architecture-structure.md +82 -0
- package/template/.agent/workflows/decompose-architecture-validate.md +119 -0
- package/template/.agent/workflows/decompose-architecture.md +111 -0
- package/template/.agent/workflows/evolve-contract.md +98 -0
- package/template/.agent/workflows/evolve-feature-cascade.md +140 -0
- package/template/.agent/workflows/evolve-feature-classify.md +116 -0
- package/template/.agent/workflows/evolve-feature.md +56 -0
- package/template/.agent/workflows/ideate-discover.md +144 -0
- package/template/.agent/workflows/ideate-extract.md +129 -0
- package/template/.agent/workflows/ideate-validate.md +117 -0
- package/template/.agent/workflows/ideate.md +113 -0
- package/template/.agent/workflows/implement-slice-setup.md +113 -0
- package/template/.agent/workflows/implement-slice-tdd.md +198 -0
- package/template/.agent/workflows/implement-slice.md +50 -0
- package/template/.agent/workflows/plan-phase.md +202 -0
- package/template/.agent/workflows/propagate-decision-apply.md +135 -0
- package/template/.agent/workflows/propagate-decision-scan.md +147 -0
- package/template/.agent/workflows/propagate-decision.md +56 -0
- package/template/.agent/workflows/remediate-pipeline-assess.md +138 -0
- package/template/.agent/workflows/remediate-pipeline-execute.md +135 -0
- package/template/.agent/workflows/remediate-pipeline.md +55 -0
- package/template/.agent/workflows/resolve-ambiguity.md +82 -0
- package/template/.agent/workflows/sync-kit.md +209 -0
- package/template/.agent/workflows/update-architecture-map.md +74 -0
- package/template/.agent/workflows/validate-phase.md +219 -0
- package/template/.agent/workflows/verify-infrastructure.md +207 -0
- package/template/.agent/workflows/write-architecture-spec-deepen.md +139 -0
- package/template/.agent/workflows/write-architecture-spec-design.md +202 -0
- package/template/.agent/workflows/write-architecture-spec.md +63 -0
- package/template/.agent/workflows/write-be-spec-classify.md +165 -0
- package/template/.agent/workflows/write-be-spec-write.md +98 -0
- package/template/.agent/workflows/write-be-spec.md +76 -0
- package/template/.agent/workflows/write-fe-spec-classify.md +170 -0
- package/template/.agent/workflows/write-fe-spec-write.md +94 -0
- package/template/.agent/workflows/write-fe-spec.md +71 -0
- package/template/AGENTS.md +176 -0
- package/template/GEMINI.md +177 -0
- package/template/docs/README.md +187 -0
- package/template/docs/audits/.gitkeep +0 -0
- package/template/docs/audits/README.md +10 -0
- package/template/docs/plans/.gitkeep +0 -0
- package/template/docs/plans/README.md +21 -0
- package/template/docs/plans/be/.gitkeep +0 -0
- package/template/docs/plans/be/README.md +11 -0
- package/template/docs/plans/fe/.gitkeep +0 -0
- package/template/docs/plans/fe/README.md +11 -0
- package/template/docs/plans/ia/.gitkeep +0 -0
- package/template/docs/plans/ia/README.md +17 -0
- package/template/docs/plans/ia/deep-dives/.gitkeep +0 -0
- package/template/docs/plans/ia/deep-dives/README.md +5 -0
- package/template/docs/plans/phases/.gitkeep +0 -0
- package/template/docs/plans/phases/README.md +11 -0
|
@@ -0,0 +1,877 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: react-best-practices
|
|
3
|
+
description: "Comprehensive React performance and architecture guide covering Server Component patterns, eliminating client/server waterfalls, bundle size reduction, re-render optimization, rendering performance, JavaScript runtime performance, Server Actions, error boundaries, and React 19 features. Use when building React applications with Next.js or any RSC-capable framework, optimizing performance, or architecting component trees."
|
|
4
|
+
version: 1.0.0
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# React Best Practices
|
|
8
|
+
|
|
9
|
+
## 1. Philosophy
|
|
10
|
+
|
|
11
|
+
React applications must be **fast by default, not fast by accident**. Performance is not an afterthought bolted on with profiling tools -- it is an architectural decision made at the component tree level before a single line of implementation is written.
|
|
12
|
+
|
|
13
|
+
**Key principles**:
|
|
14
|
+
- Server Components are the default. Client Components are the exception.
|
|
15
|
+
- Data fetching happens at the top of the tree, not sprinkled throughout.
|
|
16
|
+
- Bundle size is a budget, not a metric you check after shipping.
|
|
17
|
+
- Re-renders are signals of architectural problems, not inevitable costs.
|
|
18
|
+
- The main thread is sacred. Never block it with synchronous computation.
|
|
19
|
+
|
|
20
|
+
---
|
|
21
|
+
|
|
22
|
+
## 2. Server Component Patterns
|
|
23
|
+
|
|
24
|
+
### Data Fetching at the Top
|
|
25
|
+
|
|
26
|
+
Server Components fetch data without client-side JavaScript. Push data fetching as high in the tree as possible.
|
|
27
|
+
|
|
28
|
+
```tsx
|
|
29
|
+
// GOOD: Server Component fetches data at the page level
|
|
30
|
+
// app/dashboard/page.tsx
|
|
31
|
+
import { DashboardView } from "./dashboard-view";
|
|
32
|
+
|
|
33
|
+
export default async function DashboardPage() {
|
|
34
|
+
const [metrics, activity, alerts] = await Promise.all([
|
|
35
|
+
fetchMetrics(),
|
|
36
|
+
fetchRecentActivity(),
|
|
37
|
+
fetchAlerts(),
|
|
38
|
+
]);
|
|
39
|
+
|
|
40
|
+
return (
|
|
41
|
+
<DashboardView
|
|
42
|
+
metrics={metrics}
|
|
43
|
+
activity={activity}
|
|
44
|
+
alerts={alerts}
|
|
45
|
+
/>
|
|
46
|
+
);
|
|
47
|
+
}
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
```tsx
|
|
51
|
+
// BAD: Each child component fetches its own data independently
|
|
52
|
+
// This creates sequential waterfalls on the server
|
|
53
|
+
function Dashboard() {
|
|
54
|
+
return (
|
|
55
|
+
<div>
|
|
56
|
+
<MetricsPanel /> {/* fetches /api/metrics */}
|
|
57
|
+
<ActivityFeed /> {/* fetches /api/activity -- waits for above */}
|
|
58
|
+
<AlertsWidget /> {/* fetches /api/alerts -- waits for above */}
|
|
59
|
+
</div>
|
|
60
|
+
);
|
|
61
|
+
}
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
### Streaming with Suspense
|
|
65
|
+
|
|
66
|
+
Use Suspense boundaries to stream content progressively. Users see the shell immediately while slow data loads in the background.
|
|
67
|
+
|
|
68
|
+
```tsx
|
|
69
|
+
// app/dashboard/page.tsx
|
|
70
|
+
import { Suspense } from "react";
|
|
71
|
+
import { MetricsPanel } from "./metrics-panel";
|
|
72
|
+
import { ActivityFeed } from "./activity-feed";
|
|
73
|
+
import { MetricsSkeleton, ActivitySkeleton } from "./skeletons";
|
|
74
|
+
|
|
75
|
+
export default function DashboardPage() {
|
|
76
|
+
return (
|
|
77
|
+
<div className="grid grid-cols-2 gap-4">
|
|
78
|
+
<Suspense fallback={<MetricsSkeleton />}>
|
|
79
|
+
<MetricsPanel />
|
|
80
|
+
</Suspense>
|
|
81
|
+
<Suspense fallback={<ActivitySkeleton />}>
|
|
82
|
+
<ActivityFeed />
|
|
83
|
+
</Suspense>
|
|
84
|
+
</div>
|
|
85
|
+
);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// Each component is an async Server Component
|
|
89
|
+
async function MetricsPanel() {
|
|
90
|
+
const metrics = await fetchMetrics(); // streams when ready
|
|
91
|
+
return <MetricsDisplay data={metrics} />;
|
|
92
|
+
}
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
### Server/Client Boundary
|
|
96
|
+
|
|
97
|
+
Mark the boundary explicitly. Minimize what crosses into client territory.
|
|
98
|
+
|
|
99
|
+
```tsx
|
|
100
|
+
// GOOD: Thin client wrapper, maximum server rendering
|
|
101
|
+
// components/search-results.tsx (Server Component)
|
|
102
|
+
import { SearchInput } from "./search-input"; // "use client"
|
|
103
|
+
|
|
104
|
+
export async function SearchResults({ query }: { query: string }) {
|
|
105
|
+
const results = await searchDatabase(query);
|
|
106
|
+
return (
|
|
107
|
+
<div>
|
|
108
|
+
<SearchInput defaultValue={query} />
|
|
109
|
+
<ul>
|
|
110
|
+
{results.map((r) => (
|
|
111
|
+
<li key={r.id}>{r.title}</li>
|
|
112
|
+
))}
|
|
113
|
+
</ul>
|
|
114
|
+
</div>
|
|
115
|
+
);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// components/search-input.tsx
|
|
119
|
+
"use client";
|
|
120
|
+
import { useRouter, useSearchParams } from "next/navigation";
|
|
121
|
+
|
|
122
|
+
export function SearchInput({ defaultValue }: { defaultValue: string }) {
|
|
123
|
+
const router = useRouter();
|
|
124
|
+
// Only the input is a Client Component -- the results list is server-rendered
|
|
125
|
+
return (
|
|
126
|
+
<input
|
|
127
|
+
defaultValue={defaultValue}
|
|
128
|
+
onChange={(e) => router.push(`?q=${e.target.value}`)}
|
|
129
|
+
/>
|
|
130
|
+
);
|
|
131
|
+
}
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
---
|
|
135
|
+
|
|
136
|
+
## 3. Eliminating Waterfalls
|
|
137
|
+
|
|
138
|
+
### Parallel Data Loading
|
|
139
|
+
|
|
140
|
+
Never await sequentially when requests are independent.
|
|
141
|
+
|
|
142
|
+
```tsx
|
|
143
|
+
// BAD: Sequential -- total time = sum of all requests
|
|
144
|
+
async function Page() {
|
|
145
|
+
const user = await fetchUser(); // 200ms
|
|
146
|
+
const posts = await fetchPosts(); // 300ms
|
|
147
|
+
const comments = await fetchComments(); // 150ms
|
|
148
|
+
// Total: 650ms
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
// GOOD: Parallel -- total time = max of all requests
|
|
152
|
+
async function Page() {
|
|
153
|
+
const [user, posts, comments] = await Promise.all([
|
|
154
|
+
fetchUser(), // 200ms
|
|
155
|
+
fetchPosts(), // 300ms
|
|
156
|
+
fetchComments(), // 150ms
|
|
157
|
+
]);
|
|
158
|
+
// Total: 300ms
|
|
159
|
+
}
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
### Preloading Data
|
|
163
|
+
|
|
164
|
+
Start fetching before you need the data. Use the preload pattern to kick off requests early.
|
|
165
|
+
|
|
166
|
+
```tsx
|
|
167
|
+
// lib/data.ts
|
|
168
|
+
import { cache } from "react";
|
|
169
|
+
|
|
170
|
+
// Deduplicated: calling this multiple times in one render
|
|
171
|
+
// only hits the database once
|
|
172
|
+
export const getUser = cache(async (id: string) => {
|
|
173
|
+
return db.user.findUnique({ where: { id } });
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
// Preload function -- call this early, consume later
|
|
177
|
+
export function preloadUser(id: string) {
|
|
178
|
+
void getUser(id);
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
// app/user/[id]/page.tsx
|
|
182
|
+
import { preloadUser, getUser } from "@/lib/data";
|
|
183
|
+
|
|
184
|
+
export default async function UserPage({ params }: { params: { id: string } }) {
|
|
185
|
+
// Start fetching immediately
|
|
186
|
+
preloadUser(params.id);
|
|
187
|
+
|
|
188
|
+
// Do other work...
|
|
189
|
+
const config = await getConfig();
|
|
190
|
+
|
|
191
|
+
// Data is likely already cached by now
|
|
192
|
+
const user = await getUser(params.id);
|
|
193
|
+
return <UserProfile user={user} config={config} />;
|
|
194
|
+
}
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
### Avoiding Client-Side Fetch Waterfalls
|
|
198
|
+
|
|
199
|
+
When client-side fetching is unavoidable, prevent parent-child waterfalls.
|
|
200
|
+
|
|
201
|
+
```tsx
|
|
202
|
+
// BAD: Child fetches only after parent renders
|
|
203
|
+
function Parent() {
|
|
204
|
+
const { data: user } = useSWR("/api/user");
|
|
205
|
+
if (!user) return <Spinner />;
|
|
206
|
+
return <ChildProfile userId={user.id} />;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
function ChildProfile({ userId }: { userId: string }) {
|
|
210
|
+
// This request does not start until Parent finishes
|
|
211
|
+
const { data: profile } = useSWR(`/api/profile/${userId}`);
|
|
212
|
+
return <div>{profile?.name}</div>;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
// GOOD: Fetch in parallel at the top
|
|
216
|
+
function Parent() {
|
|
217
|
+
const { data: user } = useSWR("/api/user");
|
|
218
|
+
const { data: profile } = useSWR(
|
|
219
|
+
user ? `/api/profile/${user.id}` : null
|
|
220
|
+
);
|
|
221
|
+
if (!user || !profile) return <Spinner />;
|
|
222
|
+
return <ProfileView user={user} profile={profile} />;
|
|
223
|
+
}
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
---
|
|
227
|
+
|
|
228
|
+
## 4. Bundle Size Reduction
|
|
229
|
+
|
|
230
|
+
### Dynamic Imports
|
|
231
|
+
|
|
232
|
+
Lazy-load components and libraries that are not needed on initial render.
|
|
233
|
+
|
|
234
|
+
```tsx
|
|
235
|
+
import dynamic from "next/dynamic";
|
|
236
|
+
|
|
237
|
+
// Component only loads when rendered
|
|
238
|
+
const HeavyChart = dynamic(() => import("./heavy-chart"), {
|
|
239
|
+
loading: () => <ChartSkeleton />,
|
|
240
|
+
ssr: false, // Skip SSR if it depends on browser APIs
|
|
241
|
+
});
|
|
242
|
+
|
|
243
|
+
// React.lazy for non-Next.js
|
|
244
|
+
const HeavyEditor = lazy(() => import("./heavy-editor"));
|
|
245
|
+
|
|
246
|
+
function Dashboard() {
|
|
247
|
+
return (
|
|
248
|
+
<div>
|
|
249
|
+
<Header /> {/* Always loaded */}
|
|
250
|
+
<Suspense fallback={<ChartSkeleton />}>
|
|
251
|
+
<HeavyChart /> {/* Loaded on demand */}
|
|
252
|
+
</Suspense>
|
|
253
|
+
</div>
|
|
254
|
+
);
|
|
255
|
+
}
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
### Tree Shaking -- Import Only What You Use
|
|
259
|
+
|
|
260
|
+
```tsx
|
|
261
|
+
// BAD: Imports the entire library
|
|
262
|
+
import _ from "lodash";
|
|
263
|
+
const sorted = _.sortBy(items, "name");
|
|
264
|
+
|
|
265
|
+
// GOOD: Import only the function you need
|
|
266
|
+
import sortBy from "lodash/sortBy";
|
|
267
|
+
const sorted = sortBy(items, "name");
|
|
268
|
+
|
|
269
|
+
// BEST: Use native methods when possible
|
|
270
|
+
const sorted = [...items].sort((a, b) => a.name.localeCompare(b.name));
|
|
271
|
+
```
|
|
272
|
+
|
|
273
|
+
### Avoid Barrel Files in Large Codebases
|
|
274
|
+
|
|
275
|
+
Barrel files (`index.ts` that re-exports everything) defeat tree shaking.
|
|
276
|
+
|
|
277
|
+
```tsx
|
|
278
|
+
// BAD: Barrel file pulls in everything
|
|
279
|
+
// components/index.ts
|
|
280
|
+
export { Button } from "./button";
|
|
281
|
+
export { Dialog } from "./dialog";
|
|
282
|
+
export { DataTable } from "./data-table"; // Heavy component
|
|
283
|
+
|
|
284
|
+
// Importing Button also bundles DataTable
|
|
285
|
+
import { Button } from "@/components";
|
|
286
|
+
|
|
287
|
+
// GOOD: Import directly from the source
|
|
288
|
+
import { Button } from "@/components/button";
|
|
289
|
+
```
|
|
290
|
+
|
|
291
|
+
### Analyzing Bundle Size
|
|
292
|
+
|
|
293
|
+
```bash
|
|
294
|
+
# Next.js built-in analyzer
|
|
295
|
+
ANALYZE=true next build
|
|
296
|
+
|
|
297
|
+
# Or use @next/bundle-analyzer
|
|
298
|
+
# next.config.js
|
|
299
|
+
const withBundleAnalyzer = require("@next/bundle-analyzer")({
|
|
300
|
+
enabled: process.env.ANALYZE === "true",
|
|
301
|
+
});
|
|
302
|
+
module.exports = withBundleAnalyzer(nextConfig);
|
|
303
|
+
```
|
|
304
|
+
|
|
305
|
+
---
|
|
306
|
+
|
|
307
|
+
## 5. Re-Render Optimization
|
|
308
|
+
|
|
309
|
+
### State Colocation
|
|
310
|
+
|
|
311
|
+
Keep state as close to where it is used as possible. State at the top of the tree re-renders everything below it.
|
|
312
|
+
|
|
313
|
+
```tsx
|
|
314
|
+
// BAD: Search state lives in the page, re-renders the entire page
|
|
315
|
+
function Page() {
|
|
316
|
+
const [search, setSearch] = useState("");
|
|
317
|
+
return (
|
|
318
|
+
<div>
|
|
319
|
+
<Header /> {/* re-renders on every keystroke */}
|
|
320
|
+
<SearchInput value={search} onChange={setSearch} />
|
|
321
|
+
<ExpensiveList /> {/* re-renders on every keystroke */}
|
|
322
|
+
<Footer /> {/* re-renders on every keystroke */}
|
|
323
|
+
</div>
|
|
324
|
+
);
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
// GOOD: Search state is colocated in the search component
|
|
328
|
+
function Page() {
|
|
329
|
+
return (
|
|
330
|
+
<div>
|
|
331
|
+
<Header />
|
|
332
|
+
<SearchSection /> {/* Only this re-renders */}
|
|
333
|
+
<ExpensiveList />
|
|
334
|
+
<Footer />
|
|
335
|
+
</div>
|
|
336
|
+
);
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
function SearchSection() {
|
|
340
|
+
const [search, setSearch] = useState("");
|
|
341
|
+
return <SearchInput value={search} onChange={setSearch} />;
|
|
342
|
+
}
|
|
343
|
+
```
|
|
344
|
+
|
|
345
|
+
### React.memo for Expensive Children
|
|
346
|
+
|
|
347
|
+
Use `memo` when a component receives the same props frequently but its parent re-renders often.
|
|
348
|
+
|
|
349
|
+
```tsx
|
|
350
|
+
import { memo } from "react";
|
|
351
|
+
|
|
352
|
+
const ExpensiveList = memo(function ExpensiveList({
|
|
353
|
+
items,
|
|
354
|
+
}: {
|
|
355
|
+
items: Item[];
|
|
356
|
+
}) {
|
|
357
|
+
return (
|
|
358
|
+
<ul>
|
|
359
|
+
{items.map((item) => (
|
|
360
|
+
<li key={item.id}>{item.name}</li>
|
|
361
|
+
))}
|
|
362
|
+
</ul>
|
|
363
|
+
);
|
|
364
|
+
});
|
|
365
|
+
```
|
|
366
|
+
|
|
367
|
+
### Stable References with useCallback and useMemo
|
|
368
|
+
|
|
369
|
+
```tsx
|
|
370
|
+
function Parent({ items }: { items: Item[] }) {
|
|
371
|
+
// Without useCallback, a new function is created every render,
|
|
372
|
+
// defeating memo on the child
|
|
373
|
+
const handleClick = useCallback((id: string) => {
|
|
374
|
+
console.log("clicked", id);
|
|
375
|
+
}, []);
|
|
376
|
+
|
|
377
|
+
// Without useMemo, a new array is created every render
|
|
378
|
+
const sortedItems = useMemo(
|
|
379
|
+
() => [...items].sort((a, b) => a.name.localeCompare(b.name)),
|
|
380
|
+
[items]
|
|
381
|
+
);
|
|
382
|
+
|
|
383
|
+
return <ExpensiveList items={sortedItems} onClick={handleClick} />;
|
|
384
|
+
}
|
|
385
|
+
```
|
|
386
|
+
|
|
387
|
+
### Content Pattern (Children as Stable Nodes)
|
|
388
|
+
|
|
389
|
+
```tsx
|
|
390
|
+
// GOOD: children does not re-render when Parent's state changes
|
|
391
|
+
// because children is a stable reference passed from above
|
|
392
|
+
function ExpandablePanel({ children }: { children: React.ReactNode }) {
|
|
393
|
+
const [isOpen, setIsOpen] = useState(false);
|
|
394
|
+
return (
|
|
395
|
+
<div>
|
|
396
|
+
<button onClick={() => setIsOpen(!isOpen)}>Toggle</button>
|
|
397
|
+
{isOpen && children}
|
|
398
|
+
</div>
|
|
399
|
+
);
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
// Usage -- ExpensiveContent is not re-created on toggle
|
|
403
|
+
<ExpandablePanel>
|
|
404
|
+
<ExpensiveContent data={data} />
|
|
405
|
+
</ExpandablePanel>
|
|
406
|
+
```
|
|
407
|
+
|
|
408
|
+
---
|
|
409
|
+
|
|
410
|
+
## 6. Rendering Performance
|
|
411
|
+
|
|
412
|
+
### Virtualization for Large Lists
|
|
413
|
+
|
|
414
|
+
Never render thousands of DOM nodes. Use virtualization.
|
|
415
|
+
|
|
416
|
+
```tsx
|
|
417
|
+
import { useVirtualizer } from "@tanstack/react-virtual";
|
|
418
|
+
|
|
419
|
+
function VirtualList({ items }: { items: Item[] }) {
|
|
420
|
+
const parentRef = useRef<HTMLDivElement>(null);
|
|
421
|
+
|
|
422
|
+
const virtualizer = useVirtualizer({
|
|
423
|
+
count: items.length,
|
|
424
|
+
getScrollElement: () => parentRef.current,
|
|
425
|
+
estimateSize: () => 50,
|
|
426
|
+
overscan: 5,
|
|
427
|
+
});
|
|
428
|
+
|
|
429
|
+
return (
|
|
430
|
+
<div ref={parentRef} style={{ height: "400px", overflow: "auto" }}>
|
|
431
|
+
<div style={{ height: `${virtualizer.getTotalSize()}px`, position: "relative" }}>
|
|
432
|
+
{virtualizer.getVirtualItems().map((virtualItem) => (
|
|
433
|
+
<div
|
|
434
|
+
key={virtualItem.key}
|
|
435
|
+
style={{
|
|
436
|
+
position: "absolute",
|
|
437
|
+
top: 0,
|
|
438
|
+
left: 0,
|
|
439
|
+
width: "100%",
|
|
440
|
+
height: `${virtualItem.size}px`,
|
|
441
|
+
transform: `translateY(${virtualItem.start}px)`,
|
|
442
|
+
}}
|
|
443
|
+
>
|
|
444
|
+
{items[virtualItem.index].name}
|
|
445
|
+
</div>
|
|
446
|
+
))}
|
|
447
|
+
</div>
|
|
448
|
+
</div>
|
|
449
|
+
);
|
|
450
|
+
}
|
|
451
|
+
```
|
|
452
|
+
|
|
453
|
+
### Key Stability
|
|
454
|
+
|
|
455
|
+
Keys must be stable, unique, and derived from the data -- never from array indices when items can reorder.
|
|
456
|
+
|
|
457
|
+
```tsx
|
|
458
|
+
// BAD: Index keys cause full re-mount on reorder
|
|
459
|
+
{items.map((item, index) => (
|
|
460
|
+
<ListItem key={index} item={item} />
|
|
461
|
+
))}
|
|
462
|
+
|
|
463
|
+
// GOOD: Stable ID from data
|
|
464
|
+
{items.map((item) => (
|
|
465
|
+
<ListItem key={item.id} item={item} />
|
|
466
|
+
))}
|
|
467
|
+
|
|
468
|
+
// BAD: Random keys cause full re-mount every render
|
|
469
|
+
{items.map((item) => (
|
|
470
|
+
<ListItem key={Math.random()} item={item} />
|
|
471
|
+
))}
|
|
472
|
+
```
|
|
473
|
+
|
|
474
|
+
### Lazy Loading Below the Fold
|
|
475
|
+
|
|
476
|
+
```tsx
|
|
477
|
+
import { useInView } from "react-intersection-observer";
|
|
478
|
+
|
|
479
|
+
function LazySection({ children }: { children: React.ReactNode }) {
|
|
480
|
+
const { ref, inView } = useInView({
|
|
481
|
+
triggerOnce: true,
|
|
482
|
+
rootMargin: "200px", // Start loading 200px before visible
|
|
483
|
+
});
|
|
484
|
+
|
|
485
|
+
return (
|
|
486
|
+
<div ref={ref}>
|
|
487
|
+
{inView ? children : <Placeholder />}
|
|
488
|
+
</div>
|
|
489
|
+
);
|
|
490
|
+
}
|
|
491
|
+
```
|
|
492
|
+
|
|
493
|
+
---
|
|
494
|
+
|
|
495
|
+
## 7. JavaScript Runtime Performance
|
|
496
|
+
|
|
497
|
+
### Avoid Main Thread Blocking
|
|
498
|
+
|
|
499
|
+
Long synchronous computations block the UI. Move them off the main thread.
|
|
500
|
+
|
|
501
|
+
```tsx
|
|
502
|
+
// BAD: Blocks the main thread during render
|
|
503
|
+
function ExpensiveComponent({ data }: { data: RawData[] }) {
|
|
504
|
+
// This runs synchronously every render
|
|
505
|
+
const processed = data
|
|
506
|
+
.filter(complexFilter)
|
|
507
|
+
.map(complexTransform)
|
|
508
|
+
.sort(complexSort); // 200ms+ on large datasets
|
|
509
|
+
|
|
510
|
+
return <Chart data={processed} />;
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
// GOOD: Use useMemo to avoid recomputing
|
|
514
|
+
function ExpensiveComponent({ data }: { data: RawData[] }) {
|
|
515
|
+
const processed = useMemo(() => {
|
|
516
|
+
return data
|
|
517
|
+
.filter(complexFilter)
|
|
518
|
+
.map(complexTransform)
|
|
519
|
+
.sort(complexSort);
|
|
520
|
+
}, [data]);
|
|
521
|
+
|
|
522
|
+
return <Chart data={processed} />;
|
|
523
|
+
}
|
|
524
|
+
```
|
|
525
|
+
|
|
526
|
+
### Web Workers for Heavy Computation
|
|
527
|
+
|
|
528
|
+
```tsx
|
|
529
|
+
// workers/process-data.ts
|
|
530
|
+
self.onmessage = (event: MessageEvent<RawData[]>) => {
|
|
531
|
+
const result = heavyComputation(event.data);
|
|
532
|
+
self.postMessage(result);
|
|
533
|
+
};
|
|
534
|
+
|
|
535
|
+
// hooks/use-worker.ts
|
|
536
|
+
export function useWorker<TInput, TOutput>(workerFactory: () => Worker) {
|
|
537
|
+
const [result, setResult] = useState<TOutput | null>(null);
|
|
538
|
+
const [loading, setLoading] = useState(false);
|
|
539
|
+
const workerRef = useRef<Worker | null>(null);
|
|
540
|
+
|
|
541
|
+
useEffect(() => {
|
|
542
|
+
workerRef.current = workerFactory();
|
|
543
|
+
workerRef.current.onmessage = (event: MessageEvent<TOutput>) => {
|
|
544
|
+
setResult(event.data);
|
|
545
|
+
setLoading(false);
|
|
546
|
+
};
|
|
547
|
+
return () => workerRef.current?.terminate();
|
|
548
|
+
}, [workerFactory]);
|
|
549
|
+
|
|
550
|
+
const process = useCallback((input: TInput) => {
|
|
551
|
+
setLoading(true);
|
|
552
|
+
workerRef.current?.postMessage(input);
|
|
553
|
+
}, []);
|
|
554
|
+
|
|
555
|
+
return { result, loading, process };
|
|
556
|
+
}
|
|
557
|
+
```
|
|
558
|
+
|
|
559
|
+
### Debounce Expensive Operations
|
|
560
|
+
|
|
561
|
+
```tsx
|
|
562
|
+
import { useDeferredValue, useState } from "react";
|
|
563
|
+
|
|
564
|
+
function SearchWithDeferred() {
|
|
565
|
+
const [input, setInput] = useState("");
|
|
566
|
+
// React deprioritizes rendering with the deferred value
|
|
567
|
+
const deferredQuery = useDeferredValue(input);
|
|
568
|
+
|
|
569
|
+
return (
|
|
570
|
+
<div>
|
|
571
|
+
<input value={input} onChange={(e) => setInput(e.target.value)} />
|
|
572
|
+
{/* Results use the deferred value -- input stays responsive */}
|
|
573
|
+
<SearchResults query={deferredQuery} />
|
|
574
|
+
</div>
|
|
575
|
+
);
|
|
576
|
+
}
|
|
577
|
+
```
|
|
578
|
+
|
|
579
|
+
---
|
|
580
|
+
|
|
581
|
+
## 8. Server Actions
|
|
582
|
+
|
|
583
|
+
Server Actions allow client components to call server-side functions directly without creating API routes.
|
|
584
|
+
|
|
585
|
+
```tsx
|
|
586
|
+
// actions/user.ts
|
|
587
|
+
"use server";
|
|
588
|
+
|
|
589
|
+
import { revalidatePath } from "next/cache";
|
|
590
|
+
import { z } from "zod";
|
|
591
|
+
|
|
592
|
+
const UpdateProfileSchema = z.object({
|
|
593
|
+
name: z.string().min(1).max(100),
|
|
594
|
+
bio: z.string().max(500).optional(),
|
|
595
|
+
});
|
|
596
|
+
|
|
597
|
+
export async function updateProfile(formData: FormData) {
|
|
598
|
+
const parsed = UpdateProfileSchema.safeParse({
|
|
599
|
+
name: formData.get("name"),
|
|
600
|
+
bio: formData.get("bio"),
|
|
601
|
+
});
|
|
602
|
+
|
|
603
|
+
if (!parsed.success) {
|
|
604
|
+
return { error: parsed.error.flatten().fieldErrors };
|
|
605
|
+
}
|
|
606
|
+
|
|
607
|
+
await db.user.update({
|
|
608
|
+
where: { id: getCurrentUserId() },
|
|
609
|
+
data: parsed.data,
|
|
610
|
+
});
|
|
611
|
+
|
|
612
|
+
revalidatePath("/profile");
|
|
613
|
+
return { success: true };
|
|
614
|
+
}
|
|
615
|
+
```
|
|
616
|
+
|
|
617
|
+
```tsx
|
|
618
|
+
// components/profile-form.tsx
|
|
619
|
+
"use client";
|
|
620
|
+
|
|
621
|
+
import { useActionState } from "react";
|
|
622
|
+
import { updateProfile } from "@/actions/user";
|
|
623
|
+
|
|
624
|
+
export function ProfileForm() {
|
|
625
|
+
const [state, action, isPending] = useActionState(updateProfile, null);
|
|
626
|
+
|
|
627
|
+
return (
|
|
628
|
+
<form action={action}>
|
|
629
|
+
<input name="name" required />
|
|
630
|
+
{state?.error?.name && <p className="text-red-500">{state.error.name}</p>}
|
|
631
|
+
<textarea name="bio" />
|
|
632
|
+
<button type="submit" disabled={isPending}>
|
|
633
|
+
{isPending ? "Saving..." : "Save"}
|
|
634
|
+
</button>
|
|
635
|
+
</form>
|
|
636
|
+
);
|
|
637
|
+
}
|
|
638
|
+
```
|
|
639
|
+
|
|
640
|
+
---
|
|
641
|
+
|
|
642
|
+
## 9. Error Boundaries
|
|
643
|
+
|
|
644
|
+
Error boundaries catch rendering errors and display fallback UI instead of crashing the entire page.
|
|
645
|
+
|
|
646
|
+
```tsx
|
|
647
|
+
// components/error-boundary.tsx
|
|
648
|
+
"use client";
|
|
649
|
+
|
|
650
|
+
import { Component, type ErrorInfo, type ReactNode } from "react";
|
|
651
|
+
|
|
652
|
+
interface Props {
|
|
653
|
+
children: ReactNode;
|
|
654
|
+
fallback: ReactNode | ((error: Error, reset: () => void) => ReactNode);
|
|
655
|
+
}
|
|
656
|
+
|
|
657
|
+
interface State {
|
|
658
|
+
hasError: boolean;
|
|
659
|
+
error: Error | null;
|
|
660
|
+
}
|
|
661
|
+
|
|
662
|
+
export class ErrorBoundary extends Component<Props, State> {
|
|
663
|
+
constructor(props: Props) {
|
|
664
|
+
super(props);
|
|
665
|
+
this.state = { hasError: false, error: null };
|
|
666
|
+
}
|
|
667
|
+
|
|
668
|
+
static getDerivedStateFromError(error: Error): State {
|
|
669
|
+
return { hasError: true, error };
|
|
670
|
+
}
|
|
671
|
+
|
|
672
|
+
componentDidCatch(error: Error, info: ErrorInfo) {
|
|
673
|
+
console.error("Error boundary caught:", error, info);
|
|
674
|
+
// Report to error tracking service
|
|
675
|
+
}
|
|
676
|
+
|
|
677
|
+
reset = () => {
|
|
678
|
+
this.setState({ hasError: false, error: null });
|
|
679
|
+
};
|
|
680
|
+
|
|
681
|
+
render() {
|
|
682
|
+
if (this.state.hasError && this.state.error) {
|
|
683
|
+
const { fallback } = this.props;
|
|
684
|
+
if (typeof fallback === "function") {
|
|
685
|
+
return fallback(this.state.error, this.reset);
|
|
686
|
+
}
|
|
687
|
+
return fallback;
|
|
688
|
+
}
|
|
689
|
+
return this.props.children;
|
|
690
|
+
}
|
|
691
|
+
}
|
|
692
|
+
|
|
693
|
+
// Usage
|
|
694
|
+
<ErrorBoundary
|
|
695
|
+
fallback={(error, reset) => (
|
|
696
|
+
<div>
|
|
697
|
+
<p>Something went wrong: {error.message}</p>
|
|
698
|
+
<button onClick={reset}>Try again</button>
|
|
699
|
+
</div>
|
|
700
|
+
)}
|
|
701
|
+
>
|
|
702
|
+
<RiskyComponent />
|
|
703
|
+
</ErrorBoundary>
|
|
704
|
+
```
|
|
705
|
+
|
|
706
|
+
### Next.js error.tsx Convention
|
|
707
|
+
|
|
708
|
+
```tsx
|
|
709
|
+
// app/dashboard/error.tsx
|
|
710
|
+
"use client";
|
|
711
|
+
|
|
712
|
+
export default function DashboardError({
|
|
713
|
+
error,
|
|
714
|
+
reset,
|
|
715
|
+
}: {
|
|
716
|
+
error: Error & { digest?: string };
|
|
717
|
+
reset: () => void;
|
|
718
|
+
}) {
|
|
719
|
+
return (
|
|
720
|
+
<div className="flex flex-col items-center gap-4 p-8">
|
|
721
|
+
<h2>Dashboard failed to load</h2>
|
|
722
|
+
<p>{error.message}</p>
|
|
723
|
+
<button onClick={reset}>Retry</button>
|
|
724
|
+
</div>
|
|
725
|
+
);
|
|
726
|
+
}
|
|
727
|
+
```
|
|
728
|
+
|
|
729
|
+
---
|
|
730
|
+
|
|
731
|
+
## 10. React 19 Features
|
|
732
|
+
|
|
733
|
+
### use() Hook
|
|
734
|
+
|
|
735
|
+
The `use()` hook lets you read promises and context in render, replacing the need for useEffect-based data fetching patterns.
|
|
736
|
+
|
|
737
|
+
```tsx
|
|
738
|
+
import { use, Suspense } from "react";
|
|
739
|
+
|
|
740
|
+
// Pass a promise as a prop, read it with use()
|
|
741
|
+
function UserProfile({ userPromise }: { userPromise: Promise<User> }) {
|
|
742
|
+
const user = use(userPromise);
|
|
743
|
+
return <div>{user.name}</div>;
|
|
744
|
+
}
|
|
745
|
+
|
|
746
|
+
// Parent creates the promise, child reads it
|
|
747
|
+
function Page() {
|
|
748
|
+
const userPromise = fetchUser(); // starts immediately
|
|
749
|
+
return (
|
|
750
|
+
<Suspense fallback={<Skeleton />}>
|
|
751
|
+
<UserProfile userPromise={userPromise} />
|
|
752
|
+
</Suspense>
|
|
753
|
+
);
|
|
754
|
+
}
|
|
755
|
+
```
|
|
756
|
+
|
|
757
|
+
### useOptimistic
|
|
758
|
+
|
|
759
|
+
Instantly update the UI before the server confirms the change.
|
|
760
|
+
|
|
761
|
+
```tsx
|
|
762
|
+
"use client";
|
|
763
|
+
|
|
764
|
+
import { useOptimistic } from "react";
|
|
765
|
+
import { addTodo } from "@/actions/todos";
|
|
766
|
+
|
|
767
|
+
export function TodoList({ todos }: { todos: Todo[] }) {
|
|
768
|
+
const [optimisticTodos, addOptimisticTodo] = useOptimistic(
|
|
769
|
+
todos,
|
|
770
|
+
(currentTodos: Todo[], newTodo: string) => [
|
|
771
|
+
...currentTodos,
|
|
772
|
+
{ id: crypto.randomUUID(), text: newTodo, pending: true },
|
|
773
|
+
]
|
|
774
|
+
);
|
|
775
|
+
|
|
776
|
+
async function handleSubmit(formData: FormData) {
|
|
777
|
+
const text = formData.get("text") as string;
|
|
778
|
+
addOptimisticTodo(text); // UI updates immediately
|
|
779
|
+
await addTodo(text); // Server processes in background
|
|
780
|
+
}
|
|
781
|
+
|
|
782
|
+
return (
|
|
783
|
+
<div>
|
|
784
|
+
<form action={handleSubmit}>
|
|
785
|
+
<input name="text" required />
|
|
786
|
+
<button type="submit">Add</button>
|
|
787
|
+
</form>
|
|
788
|
+
<ul>
|
|
789
|
+
{optimisticTodos.map((todo) => (
|
|
790
|
+
<li key={todo.id} style={{ opacity: todo.pending ? 0.5 : 1 }}>
|
|
791
|
+
{todo.text}
|
|
792
|
+
</li>
|
|
793
|
+
))}
|
|
794
|
+
</ul>
|
|
795
|
+
</div>
|
|
796
|
+
);
|
|
797
|
+
}
|
|
798
|
+
```
|
|
799
|
+
|
|
800
|
+
### useFormStatus
|
|
801
|
+
|
|
802
|
+
Access form submission state from any component inside the form.
|
|
803
|
+
|
|
804
|
+
```tsx
|
|
805
|
+
"use client";
|
|
806
|
+
|
|
807
|
+
import { useFormStatus } from "react-dom";
|
|
808
|
+
|
|
809
|
+
function SubmitButton() {
|
|
810
|
+
const { pending } = useFormStatus();
|
|
811
|
+
return (
|
|
812
|
+
<button type="submit" disabled={pending}>
|
|
813
|
+
{pending ? "Submitting..." : "Submit"}
|
|
814
|
+
</button>
|
|
815
|
+
);
|
|
816
|
+
}
|
|
817
|
+
|
|
818
|
+
// Works inside any form -- no prop drilling needed
|
|
819
|
+
function MyForm() {
|
|
820
|
+
return (
|
|
821
|
+
<form action={serverAction}>
|
|
822
|
+
<input name="email" type="email" required />
|
|
823
|
+
<SubmitButton />
|
|
824
|
+
</form>
|
|
825
|
+
);
|
|
826
|
+
}
|
|
827
|
+
```
|
|
828
|
+
|
|
829
|
+
---
|
|
830
|
+
|
|
831
|
+
## 11. Anti-Patterns
|
|
832
|
+
|
|
833
|
+
### NEVER
|
|
834
|
+
|
|
835
|
+
- Fetch data inside useEffect when a Server Component could do it
|
|
836
|
+
- Use `"use client"` at the top of every file -- default to Server Components
|
|
837
|
+
- Pass serializable data through context when props would work
|
|
838
|
+
- Create new objects or arrays in JSX props without memoizing them
|
|
839
|
+
- Use index as key for lists that can reorder, filter, or insert
|
|
840
|
+
- Import entire libraries when you need a single function
|
|
841
|
+
- Store derived state in useState -- compute it during render or with useMemo
|
|
842
|
+
- Nest client components deeply just to add one event handler
|
|
843
|
+
- Use `dangerouslySetInnerHTML` without sanitization
|
|
844
|
+
- Block the main thread with synchronous loops over large datasets
|
|
845
|
+
|
|
846
|
+
### ALWAYS
|
|
847
|
+
|
|
848
|
+
- Start with Server Components and push Client Components to the leaves
|
|
849
|
+
- Use Suspense boundaries around async components
|
|
850
|
+
- Parallelize independent data fetches with Promise.all
|
|
851
|
+
- Colocate state with the components that use it
|
|
852
|
+
- Use stable keys derived from data, not indices
|
|
853
|
+
- Validate all inputs on the server (Server Actions, API routes)
|
|
854
|
+
- Provide loading and error states for every async boundary
|
|
855
|
+
- Profile before optimizing -- use React DevTools Profiler
|
|
856
|
+
- Use `useTransition` for non-urgent state updates
|
|
857
|
+
- Keep Client Component bundles small -- lazy load heavy dependencies
|
|
858
|
+
|
|
859
|
+
---
|
|
860
|
+
|
|
861
|
+
## 12. Performance Checklist
|
|
862
|
+
|
|
863
|
+
Before shipping any React feature, verify:
|
|
864
|
+
|
|
865
|
+
- [ ] Data fetching happens in Server Components (not useEffect)
|
|
866
|
+
- [ ] Independent fetches run in parallel (Promise.all or separate Suspense)
|
|
867
|
+
- [ ] No barrel file imports pulling unused code into the bundle
|
|
868
|
+
- [ ] Heavy components are lazy loaded with dynamic imports
|
|
869
|
+
- [ ] Lists with more than 100 items use virtualization
|
|
870
|
+
- [ ] State is colocated -- not lifted higher than necessary
|
|
871
|
+
- [ ] Expensive computations are memoized with useMemo
|
|
872
|
+
- [ ] Callback props are stable with useCallback where memo is used
|
|
873
|
+
- [ ] Error boundaries wrap risky async boundaries
|
|
874
|
+
- [ ] Loading skeletons match the layout of the loaded content
|
|
875
|
+
- [ ] No layout shift on load (CLS score < 0.1)
|
|
876
|
+
- [ ] Largest Contentful Paint under 2.5 seconds
|
|
877
|
+
- [ ] Total JavaScript bundle under budget (check with bundle analyzer)
|