@wazir-dev/cli 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/AGENTS.md +111 -0
- package/CHANGELOG.md +14 -0
- package/CONTRIBUTING.md +101 -0
- package/LICENSE +21 -0
- package/README.md +314 -0
- package/assets/composition-engine.mmd +34 -0
- package/assets/demo-script.sh +17 -0
- package/assets/logo-dark.svg +14 -0
- package/assets/logo.svg +14 -0
- package/assets/pipeline.mmd +39 -0
- package/assets/record-demo.sh +51 -0
- package/docs/README.md +51 -0
- package/docs/adapters/context-mode.md +60 -0
- package/docs/concepts/architecture.md +87 -0
- package/docs/concepts/artifact-model.md +60 -0
- package/docs/concepts/composition-engine.md +36 -0
- package/docs/concepts/indexing-and-recall.md +160 -0
- package/docs/concepts/observability.md +41 -0
- package/docs/concepts/roles-and-workflows.md +59 -0
- package/docs/concepts/terminology-policy.md +27 -0
- package/docs/getting-started/01-installation.md +78 -0
- package/docs/getting-started/02-first-run.md +102 -0
- package/docs/getting-started/03-adding-to-project.md +15 -0
- package/docs/getting-started/04-host-setup.md +15 -0
- package/docs/guides/ci-integration.md +15 -0
- package/docs/guides/creating-skills.md +15 -0
- package/docs/guides/expertise-module-authoring.md +15 -0
- package/docs/guides/hook-development.md +15 -0
- package/docs/guides/memory-and-learnings.md +34 -0
- package/docs/guides/multi-host-export.md +15 -0
- package/docs/guides/troubleshooting.md +101 -0
- package/docs/guides/writing-custom-roles.md +15 -0
- package/docs/plans/2026-03-15-cli-pipeline-integration-design.md +592 -0
- package/docs/plans/2026-03-15-cli-pipeline-integration-plan.md +598 -0
- package/docs/plans/2026-03-15-docs-enforcement-plan.md +238 -0
- package/docs/readmes/INDEX.md +99 -0
- package/docs/readmes/features/expertise/README.md +171 -0
- package/docs/readmes/features/exports/README.md +222 -0
- package/docs/readmes/features/hooks/README.md +103 -0
- package/docs/readmes/features/hooks/loop-cap-guard.md +133 -0
- package/docs/readmes/features/hooks/post-tool-capture.md +121 -0
- package/docs/readmes/features/hooks/post-tool-lint.md +130 -0
- package/docs/readmes/features/hooks/pre-compact-summary.md +122 -0
- package/docs/readmes/features/hooks/pre-tool-capture-route.md +100 -0
- package/docs/readmes/features/hooks/protected-path-write-guard.md +128 -0
- package/docs/readmes/features/hooks/session-start.md +119 -0
- package/docs/readmes/features/hooks/stop-handoff-harvest.md +125 -0
- package/docs/readmes/features/roles/README.md +157 -0
- package/docs/readmes/features/roles/clarifier.md +152 -0
- package/docs/readmes/features/roles/content-author.md +190 -0
- package/docs/readmes/features/roles/designer.md +193 -0
- package/docs/readmes/features/roles/executor.md +184 -0
- package/docs/readmes/features/roles/learner.md +210 -0
- package/docs/readmes/features/roles/planner.md +182 -0
- package/docs/readmes/features/roles/researcher.md +164 -0
- package/docs/readmes/features/roles/reviewer.md +184 -0
- package/docs/readmes/features/roles/specifier.md +162 -0
- package/docs/readmes/features/roles/verifier.md +215 -0
- package/docs/readmes/features/schemas/README.md +178 -0
- package/docs/readmes/features/skills/README.md +63 -0
- package/docs/readmes/features/skills/brainstorming.md +96 -0
- package/docs/readmes/features/skills/debugging.md +148 -0
- package/docs/readmes/features/skills/design.md +120 -0
- package/docs/readmes/features/skills/prepare-next.md +109 -0
- package/docs/readmes/features/skills/run-audit.md +159 -0
- package/docs/readmes/features/skills/scan-project.md +109 -0
- package/docs/readmes/features/skills/self-audit.md +176 -0
- package/docs/readmes/features/skills/tdd.md +137 -0
- package/docs/readmes/features/skills/using-skills.md +92 -0
- package/docs/readmes/features/skills/verification.md +120 -0
- package/docs/readmes/features/skills/writing-plans.md +104 -0
- package/docs/readmes/features/tooling/README.md +320 -0
- package/docs/readmes/features/workflows/README.md +186 -0
- package/docs/readmes/features/workflows/author.md +181 -0
- package/docs/readmes/features/workflows/clarify.md +154 -0
- package/docs/readmes/features/workflows/design-review.md +171 -0
- package/docs/readmes/features/workflows/design.md +169 -0
- package/docs/readmes/features/workflows/discover.md +162 -0
- package/docs/readmes/features/workflows/execute.md +173 -0
- package/docs/readmes/features/workflows/learn.md +167 -0
- package/docs/readmes/features/workflows/plan-review.md +165 -0
- package/docs/readmes/features/workflows/plan.md +170 -0
- package/docs/readmes/features/workflows/prepare-next.md +167 -0
- package/docs/readmes/features/workflows/review.md +169 -0
- package/docs/readmes/features/workflows/run-audit.md +191 -0
- package/docs/readmes/features/workflows/spec-challenge.md +159 -0
- package/docs/readmes/features/workflows/specify.md +160 -0
- package/docs/readmes/features/workflows/verify.md +177 -0
- package/docs/readmes/packages/README.md +50 -0
- package/docs/readmes/packages/ajv.md +117 -0
- package/docs/readmes/packages/context-mode.md +118 -0
- package/docs/readmes/packages/gray-matter.md +116 -0
- package/docs/readmes/packages/node-test.md +137 -0
- package/docs/readmes/packages/yaml.md +112 -0
- package/docs/reference/configuration-reference.md +159 -0
- package/docs/reference/expertise-index.md +52 -0
- package/docs/reference/git-flow.md +43 -0
- package/docs/reference/hooks.md +87 -0
- package/docs/reference/host-exports.md +50 -0
- package/docs/reference/launch-checklist.md +172 -0
- package/docs/reference/marketplace-listings.md +76 -0
- package/docs/reference/release-process.md +34 -0
- package/docs/reference/roles-reference.md +77 -0
- package/docs/reference/skills.md +33 -0
- package/docs/reference/templates.md +29 -0
- package/docs/reference/tooling-cli.md +94 -0
- package/docs/truth-claims.yaml +222 -0
- package/expertise/PROGRESS.md +63 -0
- package/expertise/README.md +18 -0
- package/expertise/antipatterns/PROGRESS.md +56 -0
- package/expertise/antipatterns/backend/api-design-antipatterns.md +1271 -0
- package/expertise/antipatterns/backend/auth-antipatterns.md +1195 -0
- package/expertise/antipatterns/backend/caching-antipatterns.md +622 -0
- package/expertise/antipatterns/backend/database-antipatterns.md +1038 -0
- package/expertise/antipatterns/backend/index.md +24 -0
- package/expertise/antipatterns/backend/microservices-antipatterns.md +850 -0
- package/expertise/antipatterns/code/architecture-antipatterns.md +919 -0
- package/expertise/antipatterns/code/async-antipatterns.md +622 -0
- package/expertise/antipatterns/code/code-smells.md +1186 -0
- package/expertise/antipatterns/code/dependency-antipatterns.md +1209 -0
- package/expertise/antipatterns/code/error-handling-antipatterns.md +1360 -0
- package/expertise/antipatterns/code/index.md +27 -0
- package/expertise/antipatterns/code/naming-and-abstraction.md +1118 -0
- package/expertise/antipatterns/code/state-management-antipatterns.md +1076 -0
- package/expertise/antipatterns/code/testing-antipatterns.md +1053 -0
- package/expertise/antipatterns/design/accessibility-antipatterns.md +1136 -0
- package/expertise/antipatterns/design/dark-patterns.md +1121 -0
- package/expertise/antipatterns/design/index.md +22 -0
- package/expertise/antipatterns/design/ui-antipatterns.md +1202 -0
- package/expertise/antipatterns/design/ux-antipatterns.md +680 -0
- package/expertise/antipatterns/frontend/css-layout-antipatterns.md +691 -0
- package/expertise/antipatterns/frontend/flutter-antipatterns.md +1827 -0
- package/expertise/antipatterns/frontend/index.md +23 -0
- package/expertise/antipatterns/frontend/mobile-antipatterns.md +573 -0
- package/expertise/antipatterns/frontend/react-antipatterns.md +1128 -0
- package/expertise/antipatterns/frontend/spa-antipatterns.md +1235 -0
- package/expertise/antipatterns/index.md +31 -0
- package/expertise/antipatterns/performance/index.md +20 -0
- package/expertise/antipatterns/performance/performance-antipatterns.md +1013 -0
- package/expertise/antipatterns/performance/premature-optimization.md +623 -0
- package/expertise/antipatterns/performance/scaling-antipatterns.md +785 -0
- package/expertise/antipatterns/process/ai-coding-antipatterns.md +853 -0
- package/expertise/antipatterns/process/code-review-antipatterns.md +656 -0
- package/expertise/antipatterns/process/deployment-antipatterns.md +920 -0
- package/expertise/antipatterns/process/index.md +23 -0
- package/expertise/antipatterns/process/technical-debt-antipatterns.md +647 -0
- package/expertise/antipatterns/security/index.md +20 -0
- package/expertise/antipatterns/security/secrets-antipatterns.md +849 -0
- package/expertise/antipatterns/security/security-theater.md +843 -0
- package/expertise/antipatterns/security/vulnerability-patterns.md +801 -0
- package/expertise/architecture/PROGRESS.md +70 -0
- package/expertise/architecture/data/caching-architecture.md +671 -0
- package/expertise/architecture/data/data-consistency.md +574 -0
- package/expertise/architecture/data/data-modeling.md +536 -0
- package/expertise/architecture/data/event-streams-and-queues.md +634 -0
- package/expertise/architecture/data/index.md +25 -0
- package/expertise/architecture/data/search-architecture.md +663 -0
- package/expertise/architecture/data/sql-vs-nosql.md +708 -0
- package/expertise/architecture/decisions/architecture-decision-records.md +640 -0
- package/expertise/architecture/decisions/build-vs-buy.md +616 -0
- package/expertise/architecture/decisions/index.md +23 -0
- package/expertise/architecture/decisions/monolith-to-microservices.md +790 -0
- package/expertise/architecture/decisions/technology-selection.md +616 -0
- package/expertise/architecture/distributed/cap-theorem-and-tradeoffs.md +800 -0
- package/expertise/architecture/distributed/circuit-breaker-bulkhead.md +741 -0
- package/expertise/architecture/distributed/consensus-and-coordination.md +796 -0
- package/expertise/architecture/distributed/distributed-systems-fundamentals.md +564 -0
- package/expertise/architecture/distributed/idempotency-and-retry.md +796 -0
- package/expertise/architecture/distributed/index.md +25 -0
- package/expertise/architecture/distributed/saga-pattern.md +797 -0
- package/expertise/architecture/foundations/architectural-thinking.md +460 -0
- package/expertise/architecture/foundations/coupling-and-cohesion.md +770 -0
- package/expertise/architecture/foundations/design-principles-solid.md +649 -0
- package/expertise/architecture/foundations/domain-driven-design.md +719 -0
- package/expertise/architecture/foundations/index.md +25 -0
- package/expertise/architecture/foundations/separation-of-concerns.md +472 -0
- package/expertise/architecture/foundations/twelve-factor-app.md +797 -0
- package/expertise/architecture/index.md +34 -0
- package/expertise/architecture/integration/api-design-graphql.md +638 -0
- package/expertise/architecture/integration/api-design-grpc.md +804 -0
- package/expertise/architecture/integration/api-design-rest.md +892 -0
- package/expertise/architecture/integration/index.md +25 -0
- package/expertise/architecture/integration/third-party-integration.md +795 -0
- package/expertise/architecture/integration/webhooks-and-callbacks.md +1152 -0
- package/expertise/architecture/integration/websockets-realtime.md +791 -0
- package/expertise/architecture/mobile-architecture/index.md +22 -0
- package/expertise/architecture/mobile-architecture/mobile-app-architecture.md +780 -0
- package/expertise/architecture/mobile-architecture/mobile-backend-for-frontend.md +670 -0
- package/expertise/architecture/mobile-architecture/offline-first.md +719 -0
- package/expertise/architecture/mobile-architecture/push-and-sync.md +782 -0
- package/expertise/architecture/patterns/cqrs-event-sourcing.md +717 -0
- package/expertise/architecture/patterns/event-driven.md +797 -0
- package/expertise/architecture/patterns/hexagonal-clean-architecture.md +870 -0
- package/expertise/architecture/patterns/index.md +27 -0
- package/expertise/architecture/patterns/layered-architecture.md +736 -0
- package/expertise/architecture/patterns/microservices.md +753 -0
- package/expertise/architecture/patterns/modular-monolith.md +692 -0
- package/expertise/architecture/patterns/monolith.md +626 -0
- package/expertise/architecture/patterns/plugin-architecture.md +735 -0
- package/expertise/architecture/patterns/serverless.md +780 -0
- package/expertise/architecture/scaling/database-scaling.md +615 -0
- package/expertise/architecture/scaling/feature-flags-and-rollouts.md +757 -0
- package/expertise/architecture/scaling/horizontal-vs-vertical.md +606 -0
- package/expertise/architecture/scaling/index.md +24 -0
- package/expertise/architecture/scaling/multi-tenancy.md +800 -0
- package/expertise/architecture/scaling/stateless-design.md +787 -0
- package/expertise/backend/embedded-firmware.md +625 -0
- package/expertise/backend/go.md +853 -0
- package/expertise/backend/index.md +24 -0
- package/expertise/backend/java-spring.md +448 -0
- package/expertise/backend/node-typescript.md +625 -0
- package/expertise/backend/python-fastapi.md +724 -0
- package/expertise/backend/rust.md +458 -0
- package/expertise/backend/solidity.md +711 -0
- package/expertise/composition-map.yaml +443 -0
- package/expertise/content/foundations/content-modeling.md +395 -0
- package/expertise/content/foundations/editorial-standards.md +449 -0
- package/expertise/content/foundations/index.md +24 -0
- package/expertise/content/foundations/microcopy.md +455 -0
- package/expertise/content/foundations/terminology-governance.md +509 -0
- package/expertise/content/index.md +34 -0
- package/expertise/content/patterns/accessibility-copy.md +518 -0
- package/expertise/content/patterns/index.md +24 -0
- package/expertise/content/patterns/notification-content.md +433 -0
- package/expertise/content/patterns/sample-content.md +486 -0
- package/expertise/content/patterns/state-copy.md +439 -0
- package/expertise/design/PROGRESS.md +58 -0
- package/expertise/design/disciplines/dark-mode-theming.md +577 -0
- package/expertise/design/disciplines/design-systems.md +595 -0
- package/expertise/design/disciplines/index.md +25 -0
- package/expertise/design/disciplines/information-architecture.md +800 -0
- package/expertise/design/disciplines/interaction-design.md +788 -0
- package/expertise/design/disciplines/responsive-design.md +552 -0
- package/expertise/design/disciplines/usability-testing.md +516 -0
- package/expertise/design/disciplines/user-research.md +792 -0
- package/expertise/design/foundations/accessibility-design.md +796 -0
- package/expertise/design/foundations/color-theory.md +797 -0
- package/expertise/design/foundations/iconography.md +795 -0
- package/expertise/design/foundations/index.md +26 -0
- package/expertise/design/foundations/motion-and-animation.md +653 -0
- package/expertise/design/foundations/rtl-design.md +585 -0
- package/expertise/design/foundations/spacing-and-layout.md +607 -0
- package/expertise/design/foundations/typography.md +800 -0
- package/expertise/design/foundations/visual-hierarchy.md +761 -0
- package/expertise/design/index.md +32 -0
- package/expertise/design/patterns/authentication-flows.md +474 -0
- package/expertise/design/patterns/content-consumption.md +789 -0
- package/expertise/design/patterns/data-display.md +618 -0
- package/expertise/design/patterns/e-commerce.md +1494 -0
- package/expertise/design/patterns/feedback-and-states.md +642 -0
- package/expertise/design/patterns/forms-and-input.md +819 -0
- package/expertise/design/patterns/gamification.md +801 -0
- package/expertise/design/patterns/index.md +31 -0
- package/expertise/design/patterns/microinteractions.md +449 -0
- package/expertise/design/patterns/navigation.md +800 -0
- package/expertise/design/patterns/notifications.md +705 -0
- package/expertise/design/patterns/onboarding.md +700 -0
- package/expertise/design/patterns/search-and-filter.md +601 -0
- package/expertise/design/patterns/settings-and-preferences.md +768 -0
- package/expertise/design/patterns/social-and-community.md +748 -0
- package/expertise/design/platforms/desktop-native.md +612 -0
- package/expertise/design/platforms/index.md +25 -0
- package/expertise/design/platforms/mobile-android.md +825 -0
- package/expertise/design/platforms/mobile-cross-platform.md +983 -0
- package/expertise/design/platforms/mobile-ios.md +699 -0
- package/expertise/design/platforms/tablet.md +794 -0
- package/expertise/design/platforms/web-dashboard.md +790 -0
- package/expertise/design/platforms/web-responsive.md +550 -0
- package/expertise/design/psychology/behavioral-nudges.md +449 -0
- package/expertise/design/psychology/cognitive-load.md +1191 -0
- package/expertise/design/psychology/error-psychology.md +778 -0
- package/expertise/design/psychology/index.md +22 -0
- package/expertise/design/psychology/persuasive-design.md +736 -0
- package/expertise/design/psychology/user-mental-models.md +623 -0
- package/expertise/design/tooling/open-pencil.md +266 -0
- package/expertise/frontend/angular.md +1073 -0
- package/expertise/frontend/desktop-electron.md +546 -0
- package/expertise/frontend/flutter.md +782 -0
- package/expertise/frontend/index.md +27 -0
- package/expertise/frontend/native-android.md +409 -0
- package/expertise/frontend/native-ios.md +490 -0
- package/expertise/frontend/react-native.md +1160 -0
- package/expertise/frontend/react.md +808 -0
- package/expertise/frontend/vue.md +1089 -0
- package/expertise/humanize/domain-rules-code.md +79 -0
- package/expertise/humanize/domain-rules-content.md +67 -0
- package/expertise/humanize/domain-rules-technical-docs.md +56 -0
- package/expertise/humanize/index.md +35 -0
- package/expertise/humanize/self-audit-checklist.md +87 -0
- package/expertise/humanize/sentence-patterns.md +218 -0
- package/expertise/humanize/vocabulary-blacklist.md +105 -0
- package/expertise/i18n/PROGRESS.md +65 -0
- package/expertise/i18n/advanced/accessibility-and-i18n.md +28 -0
- package/expertise/i18n/advanced/bidirectional-text-algorithm.md +38 -0
- package/expertise/i18n/advanced/complex-scripts.md +30 -0
- package/expertise/i18n/advanced/performance-and-i18n.md +27 -0
- package/expertise/i18n/advanced/testing-i18n.md +28 -0
- package/expertise/i18n/content/content-adaptation.md +23 -0
- package/expertise/i18n/content/locale-specific-formatting.md +23 -0
- package/expertise/i18n/content/machine-translation-integration.md +28 -0
- package/expertise/i18n/content/translation-management.md +29 -0
- package/expertise/i18n/foundations/date-time-calendars.md +67 -0
- package/expertise/i18n/foundations/i18n-architecture.md +272 -0
- package/expertise/i18n/foundations/locale-and-language-tags.md +79 -0
- package/expertise/i18n/foundations/numbers-currency-units.md +61 -0
- package/expertise/i18n/foundations/pluralization-and-gender.md +109 -0
- package/expertise/i18n/foundations/string-externalization.md +236 -0
- package/expertise/i18n/foundations/text-direction-bidi.md +241 -0
- package/expertise/i18n/foundations/unicode-and-encoding.md +86 -0
- package/expertise/i18n/index.md +38 -0
- package/expertise/i18n/platform/backend-i18n.md +31 -0
- package/expertise/i18n/platform/flutter-i18n.md +148 -0
- package/expertise/i18n/platform/native-android-i18n.md +36 -0
- package/expertise/i18n/platform/native-ios-i18n.md +36 -0
- package/expertise/i18n/platform/react-i18n.md +103 -0
- package/expertise/i18n/platform/web-css-i18n.md +81 -0
- package/expertise/i18n/rtl/arabic-specific.md +175 -0
- package/expertise/i18n/rtl/hebrew-specific.md +149 -0
- package/expertise/i18n/rtl/rtl-animations-and-transitions.md +111 -0
- package/expertise/i18n/rtl/rtl-forms-and-input.md +161 -0
- package/expertise/i18n/rtl/rtl-fundamentals.md +211 -0
- package/expertise/i18n/rtl/rtl-icons-and-images.md +181 -0
- package/expertise/i18n/rtl/rtl-layout-mirroring.md +252 -0
- package/expertise/i18n/rtl/rtl-navigation-and-gestures.md +107 -0
- package/expertise/i18n/rtl/rtl-testing-and-qa.md +147 -0
- package/expertise/i18n/rtl/rtl-typography.md +160 -0
- package/expertise/index.md +113 -0
- package/expertise/index.yaml +216 -0
- package/expertise/infrastructure/cloud-aws.md +597 -0
- package/expertise/infrastructure/cloud-gcp.md +599 -0
- package/expertise/infrastructure/cybersecurity.md +816 -0
- package/expertise/infrastructure/database-mongodb.md +447 -0
- package/expertise/infrastructure/database-postgres.md +400 -0
- package/expertise/infrastructure/devops-cicd.md +787 -0
- package/expertise/infrastructure/index.md +27 -0
- package/expertise/performance/PROGRESS.md +50 -0
- package/expertise/performance/backend/api-latency.md +1204 -0
- package/expertise/performance/backend/background-jobs.md +506 -0
- package/expertise/performance/backend/connection-pooling.md +1209 -0
- package/expertise/performance/backend/database-query-optimization.md +515 -0
- package/expertise/performance/backend/index.md +23 -0
- package/expertise/performance/backend/rate-limiting-and-throttling.md +971 -0
- package/expertise/performance/foundations/algorithmic-complexity.md +954 -0
- package/expertise/performance/foundations/caching-strategies.md +489 -0
- package/expertise/performance/foundations/concurrency-and-parallelism.md +847 -0
- package/expertise/performance/foundations/index.md +24 -0
- package/expertise/performance/foundations/measuring-and-profiling.md +440 -0
- package/expertise/performance/foundations/memory-management.md +964 -0
- package/expertise/performance/foundations/performance-budgets.md +1314 -0
- package/expertise/performance/index.md +31 -0
- package/expertise/performance/infrastructure/auto-scaling.md +1059 -0
- package/expertise/performance/infrastructure/cdn-and-edge.md +1081 -0
- package/expertise/performance/infrastructure/index.md +22 -0
- package/expertise/performance/infrastructure/load-balancing.md +1081 -0
- package/expertise/performance/infrastructure/observability.md +1079 -0
- package/expertise/performance/mobile/index.md +23 -0
- package/expertise/performance/mobile/mobile-animations.md +544 -0
- package/expertise/performance/mobile/mobile-memory-battery.md +416 -0
- package/expertise/performance/mobile/mobile-network.md +452 -0
- package/expertise/performance/mobile/mobile-rendering.md +599 -0
- package/expertise/performance/mobile/mobile-startup-time.md +505 -0
- package/expertise/performance/platform-specific/flutter-performance.md +647 -0
- package/expertise/performance/platform-specific/index.md +22 -0
- package/expertise/performance/platform-specific/node-performance.md +1307 -0
- package/expertise/performance/platform-specific/postgres-performance.md +1366 -0
- package/expertise/performance/platform-specific/react-performance.md +1403 -0
- package/expertise/performance/web/bundle-optimization.md +1239 -0
- package/expertise/performance/web/image-and-media.md +636 -0
- package/expertise/performance/web/index.md +24 -0
- package/expertise/performance/web/network-optimization.md +1133 -0
- package/expertise/performance/web/rendering-performance.md +1098 -0
- package/expertise/performance/web/ssr-and-hydration.md +918 -0
- package/expertise/performance/web/web-vitals.md +1374 -0
- package/expertise/quality/accessibility.md +985 -0
- package/expertise/quality/evidence-based-verification.md +499 -0
- package/expertise/quality/index.md +24 -0
- package/expertise/quality/ml-model-audit.md +614 -0
- package/expertise/quality/performance.md +600 -0
- package/expertise/quality/testing-api.md +891 -0
- package/expertise/quality/testing-mobile.md +496 -0
- package/expertise/quality/testing-web.md +849 -0
- package/expertise/security/PROGRESS.md +54 -0
- package/expertise/security/agentic-identity.md +540 -0
- package/expertise/security/compliance-frameworks.md +601 -0
- package/expertise/security/data/data-encryption.md +364 -0
- package/expertise/security/data/data-privacy-gdpr.md +692 -0
- package/expertise/security/data/database-security.md +1171 -0
- package/expertise/security/data/index.md +22 -0
- package/expertise/security/data/pii-handling.md +531 -0
- package/expertise/security/foundations/authentication.md +1041 -0
- package/expertise/security/foundations/authorization.md +603 -0
- package/expertise/security/foundations/cryptography.md +1001 -0
- package/expertise/security/foundations/index.md +25 -0
- package/expertise/security/foundations/owasp-top-10.md +1354 -0
- package/expertise/security/foundations/secrets-management.md +1217 -0
- package/expertise/security/foundations/secure-sdlc.md +700 -0
- package/expertise/security/foundations/supply-chain-security.md +698 -0
- package/expertise/security/index.md +31 -0
- package/expertise/security/infrastructure/cloud-security-aws.md +1296 -0
- package/expertise/security/infrastructure/cloud-security-gcp.md +1376 -0
- package/expertise/security/infrastructure/container-security.md +721 -0
- package/expertise/security/infrastructure/incident-response.md +1295 -0
- package/expertise/security/infrastructure/index.md +24 -0
- package/expertise/security/infrastructure/logging-and-monitoring.md +1618 -0
- package/expertise/security/infrastructure/network-security.md +1337 -0
- package/expertise/security/mobile/index.md +23 -0
- package/expertise/security/mobile/mobile-android-security.md +1218 -0
- package/expertise/security/mobile/mobile-binary-protection.md +1229 -0
- package/expertise/security/mobile/mobile-data-storage.md +1265 -0
- package/expertise/security/mobile/mobile-ios-security.md +1401 -0
- package/expertise/security/mobile/mobile-network-security.md +1520 -0
- package/expertise/security/smart-contract-security.md +594 -0
- package/expertise/security/testing/index.md +22 -0
- package/expertise/security/testing/penetration-testing.md +1258 -0
- package/expertise/security/testing/security-code-review.md +1765 -0
- package/expertise/security/testing/threat-modeling.md +1074 -0
- package/expertise/security/testing/vulnerability-scanning.md +1062 -0
- package/expertise/security/web/api-security.md +586 -0
- package/expertise/security/web/cors-and-headers.md +433 -0
- package/expertise/security/web/csrf.md +562 -0
- package/expertise/security/web/file-upload.md +1477 -0
- package/expertise/security/web/index.md +25 -0
- package/expertise/security/web/injection.md +1375 -0
- package/expertise/security/web/session-management.md +1101 -0
- package/expertise/security/web/xss.md +1158 -0
- package/exports/README.md +17 -0
- package/exports/hosts/claude/.claude/agents/clarifier.md +42 -0
- package/exports/hosts/claude/.claude/agents/content-author.md +63 -0
- package/exports/hosts/claude/.claude/agents/designer.md +55 -0
- package/exports/hosts/claude/.claude/agents/executor.md +55 -0
- package/exports/hosts/claude/.claude/agents/learner.md +51 -0
- package/exports/hosts/claude/.claude/agents/planner.md +53 -0
- package/exports/hosts/claude/.claude/agents/researcher.md +43 -0
- package/exports/hosts/claude/.claude/agents/reviewer.md +54 -0
- package/exports/hosts/claude/.claude/agents/specifier.md +47 -0
- package/exports/hosts/claude/.claude/agents/verifier.md +71 -0
- package/exports/hosts/claude/.claude/commands/author.md +42 -0
- package/exports/hosts/claude/.claude/commands/clarify.md +38 -0
- package/exports/hosts/claude/.claude/commands/design-review.md +46 -0
- package/exports/hosts/claude/.claude/commands/design.md +44 -0
- package/exports/hosts/claude/.claude/commands/discover.md +37 -0
- package/exports/hosts/claude/.claude/commands/execute.md +48 -0
- package/exports/hosts/claude/.claude/commands/learn.md +38 -0
- package/exports/hosts/claude/.claude/commands/plan-review.md +42 -0
- package/exports/hosts/claude/.claude/commands/plan.md +39 -0
- package/exports/hosts/claude/.claude/commands/prepare-next.md +37 -0
- package/exports/hosts/claude/.claude/commands/review.md +40 -0
- package/exports/hosts/claude/.claude/commands/run-audit.md +41 -0
- package/exports/hosts/claude/.claude/commands/spec-challenge.md +41 -0
- package/exports/hosts/claude/.claude/commands/specify.md +38 -0
- package/exports/hosts/claude/.claude/commands/verify.md +37 -0
- package/exports/hosts/claude/.claude/settings.json +34 -0
- package/exports/hosts/claude/CLAUDE.md +19 -0
- package/exports/hosts/claude/export.manifest.json +38 -0
- package/exports/hosts/claude/host-package.json +67 -0
- package/exports/hosts/codex/AGENTS.md +19 -0
- package/exports/hosts/codex/export.manifest.json +38 -0
- package/exports/hosts/codex/host-package.json +41 -0
- package/exports/hosts/cursor/.cursor/hooks.json +16 -0
- package/exports/hosts/cursor/.cursor/rules/wazir-core.mdc +19 -0
- package/exports/hosts/cursor/export.manifest.json +38 -0
- package/exports/hosts/cursor/host-package.json +42 -0
- package/exports/hosts/gemini/GEMINI.md +19 -0
- package/exports/hosts/gemini/export.manifest.json +38 -0
- package/exports/hosts/gemini/host-package.json +41 -0
- package/hooks/README.md +18 -0
- package/hooks/definitions/loop_cap_guard.yaml +21 -0
- package/hooks/definitions/post_tool_capture.yaml +24 -0
- package/hooks/definitions/pre_compact_summary.yaml +19 -0
- package/hooks/definitions/pre_tool_capture_route.yaml +19 -0
- package/hooks/definitions/protected_path_write_guard.yaml +19 -0
- package/hooks/definitions/session_start.yaml +19 -0
- package/hooks/definitions/stop_handoff_harvest.yaml +20 -0
- package/hooks/loop-cap-guard +17 -0
- package/hooks/post-tool-lint +36 -0
- package/hooks/protected-path-write-guard +17 -0
- package/hooks/session-start +41 -0
- package/llms-full.txt +2355 -0
- package/llms.txt +43 -0
- package/package.json +79 -0
- package/roles/README.md +20 -0
- package/roles/clarifier.md +42 -0
- package/roles/content-author.md +63 -0
- package/roles/designer.md +55 -0
- package/roles/executor.md +55 -0
- package/roles/learner.md +51 -0
- package/roles/planner.md +53 -0
- package/roles/researcher.md +43 -0
- package/roles/reviewer.md +54 -0
- package/roles/specifier.md +47 -0
- package/roles/verifier.md +71 -0
- package/schemas/README.md +24 -0
- package/schemas/accepted-learning.schema.json +20 -0
- package/schemas/author-artifact.schema.json +156 -0
- package/schemas/clarification.schema.json +19 -0
- package/schemas/design-artifact.schema.json +80 -0
- package/schemas/docs-claim.schema.json +18 -0
- package/schemas/export-manifest.schema.json +20 -0
- package/schemas/hook.schema.json +67 -0
- package/schemas/host-export-package.schema.json +18 -0
- package/schemas/implementation-plan.schema.json +19 -0
- package/schemas/proposed-learning.schema.json +19 -0
- package/schemas/research.schema.json +18 -0
- package/schemas/review.schema.json +29 -0
- package/schemas/run-manifest.schema.json +18 -0
- package/schemas/spec-challenge.schema.json +18 -0
- package/schemas/spec.schema.json +20 -0
- package/schemas/usage.schema.json +102 -0
- package/schemas/verification-proof.schema.json +29 -0
- package/schemas/wazir-manifest.schema.json +173 -0
- package/skills/README.md +40 -0
- package/skills/brainstorming/SKILL.md +77 -0
- package/skills/debugging/SKILL.md +50 -0
- package/skills/design/SKILL.md +61 -0
- package/skills/dispatching-parallel-agents/SKILL.md +128 -0
- package/skills/executing-plans/SKILL.md +70 -0
- package/skills/finishing-a-development-branch/SKILL.md +169 -0
- package/skills/humanize/SKILL.md +123 -0
- package/skills/init-pipeline/SKILL.md +124 -0
- package/skills/prepare-next/SKILL.md +20 -0
- package/skills/receiving-code-review/SKILL.md +123 -0
- package/skills/requesting-code-review/SKILL.md +105 -0
- package/skills/requesting-code-review/code-reviewer.md +108 -0
- package/skills/run-audit/SKILL.md +197 -0
- package/skills/scan-project/SKILL.md +41 -0
- package/skills/self-audit/SKILL.md +153 -0
- package/skills/subagent-driven-development/SKILL.md +154 -0
- package/skills/subagent-driven-development/code-quality-reviewer-prompt.md +26 -0
- package/skills/subagent-driven-development/implementer-prompt.md +102 -0
- package/skills/subagent-driven-development/spec-reviewer-prompt.md +61 -0
- package/skills/tdd/SKILL.md +23 -0
- package/skills/using-git-worktrees/SKILL.md +163 -0
- package/skills/using-skills/SKILL.md +95 -0
- package/skills/verification/SKILL.md +22 -0
- package/skills/wazir/SKILL.md +463 -0
- package/skills/writing-plans/SKILL.md +30 -0
- package/skills/writing-skills/SKILL.md +157 -0
- package/skills/writing-skills/anthropic-best-practices.md +122 -0
- package/skills/writing-skills/persuasion-principles.md +50 -0
- package/templates/README.md +20 -0
- package/templates/artifacts/README.md +10 -0
- package/templates/artifacts/accepted-learning.md +19 -0
- package/templates/artifacts/accepted-learning.template.json +12 -0
- package/templates/artifacts/author.md +74 -0
- package/templates/artifacts/author.template.json +19 -0
- package/templates/artifacts/clarification.md +21 -0
- package/templates/artifacts/clarification.template.json +12 -0
- package/templates/artifacts/execute-notes.md +19 -0
- package/templates/artifacts/implementation-plan.md +21 -0
- package/templates/artifacts/implementation-plan.template.json +11 -0
- package/templates/artifacts/learning-proposal.md +19 -0
- package/templates/artifacts/next-run-handoff.md +21 -0
- package/templates/artifacts/plan-review.md +19 -0
- package/templates/artifacts/proposed-learning.template.json +12 -0
- package/templates/artifacts/research.md +21 -0
- package/templates/artifacts/research.template.json +12 -0
- package/templates/artifacts/review-findings.md +19 -0
- package/templates/artifacts/review.template.json +11 -0
- package/templates/artifacts/run-manifest.template.json +8 -0
- package/templates/artifacts/spec-challenge.md +19 -0
- package/templates/artifacts/spec-challenge.template.json +11 -0
- package/templates/artifacts/spec.md +21 -0
- package/templates/artifacts/spec.template.json +12 -0
- package/templates/artifacts/verification-proof.md +19 -0
- package/templates/artifacts/verification-proof.template.json +11 -0
- package/templates/examples/accepted-learning.example.json +14 -0
- package/templates/examples/author.example.json +152 -0
- package/templates/examples/clarification.example.json +15 -0
- package/templates/examples/docs-claim.example.json +8 -0
- package/templates/examples/export-manifest.example.json +7 -0
- package/templates/examples/host-export-package.example.json +11 -0
- package/templates/examples/implementation-plan.example.json +17 -0
- package/templates/examples/proposed-learning.example.json +13 -0
- package/templates/examples/research.example.json +15 -0
- package/templates/examples/research.example.md +6 -0
- package/templates/examples/review.example.json +17 -0
- package/templates/examples/run-manifest.example.json +9 -0
- package/templates/examples/spec-challenge.example.json +14 -0
- package/templates/examples/spec.example.json +21 -0
- package/templates/examples/verification-proof.example.json +21 -0
- package/templates/examples/wazir-manifest.example.yaml +65 -0
- package/templates/task-definition-schema.md +99 -0
- package/tooling/README.md +20 -0
- package/tooling/src/adapters/context-mode.js +50 -0
- package/tooling/src/capture/command.js +376 -0
- package/tooling/src/capture/store.js +99 -0
- package/tooling/src/capture/usage.js +270 -0
- package/tooling/src/checks/branches.js +50 -0
- package/tooling/src/checks/brand-truth.js +110 -0
- package/tooling/src/checks/changelog.js +231 -0
- package/tooling/src/checks/command-registry.js +36 -0
- package/tooling/src/checks/commits.js +102 -0
- package/tooling/src/checks/docs-drift.js +103 -0
- package/tooling/src/checks/docs-truth.js +201 -0
- package/tooling/src/checks/runtime-surface.js +156 -0
- package/tooling/src/cli.js +116 -0
- package/tooling/src/command-options.js +56 -0
- package/tooling/src/commands/validate.js +320 -0
- package/tooling/src/doctor/command.js +91 -0
- package/tooling/src/export/command.js +77 -0
- package/tooling/src/export/compiler.js +498 -0
- package/tooling/src/guards/loop-cap-guard.js +52 -0
- package/tooling/src/guards/protected-path-write-guard.js +67 -0
- package/tooling/src/index/command.js +152 -0
- package/tooling/src/index/storage.js +1061 -0
- package/tooling/src/index/summarizers.js +261 -0
- package/tooling/src/loaders.js +18 -0
- package/tooling/src/project-root.js +22 -0
- package/tooling/src/recall/command.js +225 -0
- package/tooling/src/schema-validator.js +30 -0
- package/tooling/src/state-root.js +40 -0
- package/tooling/src/status/command.js +71 -0
- package/wazir.manifest.yaml +135 -0
- package/workflows/README.md +19 -0
- package/workflows/author.md +42 -0
- package/workflows/clarify.md +38 -0
- package/workflows/design-review.md +46 -0
- package/workflows/design.md +44 -0
- package/workflows/discover.md +37 -0
- package/workflows/execute.md +48 -0
- package/workflows/learn.md +38 -0
- package/workflows/plan-review.md +42 -0
- package/workflows/plan.md +39 -0
- package/workflows/prepare-next.md +37 -0
- package/workflows/review.md +40 -0
- package/workflows/run-audit.md +41 -0
- package/workflows/spec-challenge.md +41 -0
- package/workflows/specify.md +38 -0
- package/workflows/verify.md +37 -0
|
@@ -0,0 +1,1403 @@
|
|
|
1
|
+
# React Performance -- Performance Expertise Module
|
|
2
|
+
|
|
3
|
+
> React's virtual DOM diffing provides good default performance, but poorly structured components can cause cascading re-renders that bring apps to a crawl. Understanding React's rendering model -- when components re-render, how reconciliation works, and when to memoize -- is the key to building fast React apps.
|
|
4
|
+
|
|
5
|
+
> **Impact:** Critical
|
|
6
|
+
> **Applies to:** Web (React, Next.js, Remix)
|
|
7
|
+
> **Key metrics:** Component render time, Re-render frequency, Bundle size, Time to Interactive (TTI), Interaction to Next Paint (INP)
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## 1. Why This Matters
|
|
12
|
+
|
|
13
|
+
### Re-render Cascades
|
|
14
|
+
|
|
15
|
+
React's rendering pipeline has three phases: trigger (state/prop change), render (calling component functions), and commit (applying changes to the DOM). Most performance problems occur in the render phase, where components execute unnecessarily.
|
|
16
|
+
|
|
17
|
+
A single state change at the top of a component tree can trigger re-renders in every descendant. In a tree of 500 components, a poorly placed `useState` at the root can cause all 500 components to re-execute their render functions on every keystroke. At 2ms per component, that is 1 second of blocked main thread per interaction -- well beyond the 200ms INP threshold Google requires for "good" Core Web Vitals.
|
|
18
|
+
|
|
19
|
+
### Bundle Bloat
|
|
20
|
+
|
|
21
|
+
The average React SPA ships 200-400 KB of gzipped JavaScript. Every additional KB increases parse time by approximately 1ms on a mid-range mobile device. A 400 KB bundle takes ~400ms just to parse before any rendering begins. Combined with hydration costs, this pushes Time to Interactive (TTI) to 4-6 seconds on 4G connections.
|
|
22
|
+
|
|
23
|
+
Research from Google shows that 53% of mobile users abandon sites that take longer than 3 seconds to load. Bundle bloat is the primary contributor to slow React app load times.
|
|
24
|
+
|
|
25
|
+
### Hydration Costs
|
|
26
|
+
|
|
27
|
+
Server-side rendered React apps must "hydrate" on the client -- the browser downloads all JavaScript bundles, then reconnects static HTML to the React tree and attaches event listeners. The average React SSR app has a TTI of 4.2 seconds on mobile devices. Hydration blocks interactivity: users see content but cannot interact with it until hydration completes.
|
|
28
|
+
|
|
29
|
+
Wix Engineering reported that selective hydration reduced their interaction delays by 40%. Deferring hydration of non-critical page sections (e.g., a large footer) can reduce Total Blocking Time (TBT) by 40% and initial hydration cost by approximately 45% (128ms down to 70ms with idle-until-urgent patterns).
|
|
30
|
+
|
|
31
|
+
---
|
|
32
|
+
|
|
33
|
+
## 2. Performance Budgets & Targets
|
|
34
|
+
|
|
35
|
+
| Metric | Target | Rationale |
|
|
36
|
+
|---|---|---|
|
|
37
|
+
| Component render time | < 16ms (single), < 5ms (frequent) | 60fps = 16.67ms per frame budget |
|
|
38
|
+
| Re-renders per interaction | < 3 component tree traversals | Each traversal consumes frame budget |
|
|
39
|
+
| Total JS bundle (gzipped) | < 200 KB initial, < 350 KB total | Parse cost ~1ms/KB on mobile |
|
|
40
|
+
| Largest Contentful Paint (LCP) | < 2.5s | Google Core Web Vital threshold |
|
|
41
|
+
| Interaction to Next Paint (INP) | < 200ms | Google Core Web Vital threshold (replaced FID March 2024) |
|
|
42
|
+
| Time to Interactive (TTI) | < 3.5s on 4G | User abandonment threshold |
|
|
43
|
+
| First Contentful Paint (FCP) | < 1.8s | Perceived load speed |
|
|
44
|
+
| Cumulative Layout Shift (CLS) | < 0.1 | Visual stability |
|
|
45
|
+
| Hydration time | < 100ms | Blocks interactivity |
|
|
46
|
+
| React Profiler commit time | < 16ms total | Full tree render within frame budget |
|
|
47
|
+
|
|
48
|
+
**Bundle budget breakdown for a typical SPA:**
|
|
49
|
+
|
|
50
|
+
| Chunk | Budget (gzipped) |
|
|
51
|
+
|---|---|
|
|
52
|
+
| React + ReactDOM | ~45 KB |
|
|
53
|
+
| Router | ~15 KB |
|
|
54
|
+
| State management | ~5-15 KB |
|
|
55
|
+
| UI component library | ~30-50 KB |
|
|
56
|
+
| Application code (initial route) | ~50-80 KB |
|
|
57
|
+
| **Total initial load** | **< 200 KB** |
|
|
58
|
+
|
|
59
|
+
---
|
|
60
|
+
|
|
61
|
+
## 3. Measurement & Profiling
|
|
62
|
+
|
|
63
|
+
### React DevTools Profiler
|
|
64
|
+
|
|
65
|
+
The React DevTools Profiler is the primary tool for identifying render performance issues.
|
|
66
|
+
|
|
67
|
+
**Flame chart reading:**
|
|
68
|
+
- Each bar represents a component; width indicates render duration including children.
|
|
69
|
+
- Grey bars indicate components that did not re-render (good).
|
|
70
|
+
- Color gradient from green (fast) to yellow (slow) indicates relative render cost.
|
|
71
|
+
- Enable "Record why each component rendered while profiling" to see render triggers.
|
|
72
|
+
- Use the commit filter to hide commits faster than a threshold (e.g., 5ms) to focus on slow renders.
|
|
73
|
+
|
|
74
|
+
**Key workflow:**
|
|
75
|
+
1. Open React DevTools Profiler tab.
|
|
76
|
+
2. Click Record, perform the interaction you want to measure.
|
|
77
|
+
3. Stop recording. Examine the flame chart for the slowest commits.
|
|
78
|
+
4. Look for wide yellow bars -- these are your bottleneck components.
|
|
79
|
+
5. Check "Why did this render?" for each slow component.
|
|
80
|
+
6. Target components that rendered but produced identical output (wasted renders).
|
|
81
|
+
|
|
82
|
+
### React Performance Tracks (React 19+)
|
|
83
|
+
|
|
84
|
+
React 19 introduced browser-native performance tracks visible in Chrome DevTools Performance tab. The Components track visualizes render durations as a flamegraph directly in the browser's performance timeline, eliminating the need to switch between tools.
|
|
85
|
+
|
|
86
|
+
### why-did-you-render
|
|
87
|
+
|
|
88
|
+
The `@welldone-software/why-did-you-render` library monkey-patches React to detect and report avoidable re-renders with human-readable explanations.
|
|
89
|
+
|
|
90
|
+
```javascript
|
|
91
|
+
// wdyr.js -- import as FIRST import in your app (development only)
|
|
92
|
+
if (process.env.NODE_ENV === 'development') {
|
|
93
|
+
const whyDidYouRender = require('@welldone-software/why-did-you-render');
|
|
94
|
+
whyDidYouRender(React, {
|
|
95
|
+
trackAllPureComponents: true,
|
|
96
|
+
trackHooks: true,
|
|
97
|
+
logOnDifferentValues: true,
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
**Warning:** Never include in production builds. It adds overhead and can cause edge-case breakage.
|
|
103
|
+
|
|
104
|
+
### Chrome Performance Tab
|
|
105
|
+
|
|
106
|
+
For lower-level analysis beyond React-specific tooling:
|
|
107
|
+
1. Record a performance trace during the slow interaction.
|
|
108
|
+
2. Look for long tasks (> 50ms, marked with red triangles).
|
|
109
|
+
3. Drill into the call tree to find React's `commitRoot` and `renderRootSync` calls.
|
|
110
|
+
4. Measure the total scripting time during interactions against the 200ms INP budget.
|
|
111
|
+
|
|
112
|
+
### React Profiler API (Programmatic)
|
|
113
|
+
|
|
114
|
+
```jsx
|
|
115
|
+
import { Profiler } from 'react';
|
|
116
|
+
|
|
117
|
+
function onRender(id, phase, actualDuration, baseDuration, startTime, commitTime) {
|
|
118
|
+
// Send to analytics if actualDuration exceeds threshold
|
|
119
|
+
if (actualDuration > 16) {
|
|
120
|
+
console.warn(`Slow render: ${id} took ${actualDuration.toFixed(1)}ms (phase: ${phase})`);
|
|
121
|
+
analytics.track('slow_render', { id, actualDuration, baseDuration });
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
<Profiler id="Dashboard" onRender={onRender}>
|
|
126
|
+
<Dashboard />
|
|
127
|
+
</Profiler>
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
---
|
|
131
|
+
|
|
132
|
+
## 4. Common Bottlenecks
|
|
133
|
+
|
|
134
|
+
### 4.1 Unnecessary Re-renders from Parent State Changes
|
|
135
|
+
|
|
136
|
+
**Problem:** A parent component's state change re-renders all children, even those that receive unchanged props.
|
|
137
|
+
|
|
138
|
+
```jsx
|
|
139
|
+
// BAD: Every keystroke re-renders ExpensiveChart
|
|
140
|
+
function Dashboard() {
|
|
141
|
+
const [search, setSearch] = useState('');
|
|
142
|
+
return (
|
|
143
|
+
<div>
|
|
144
|
+
<input value={search} onChange={e => setSearch(e.target.value)} />
|
|
145
|
+
<ExpensiveChart data={chartData} /> {/* Re-renders on every keystroke */}
|
|
146
|
+
</div>
|
|
147
|
+
);
|
|
148
|
+
}
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
**Impact:** In a dashboard with 20 chart components at 8ms each, typing triggers 160ms of rendering per keystroke.
|
|
152
|
+
|
|
153
|
+
### 4.2 Prop Drilling Causing Cascade Re-renders
|
|
154
|
+
|
|
155
|
+
**Problem:** Passing state through multiple intermediate components forces each layer to re-render.
|
|
156
|
+
|
|
157
|
+
**Impact:** A 5-level deep prop drill means 5 components re-render when only the leaf needs the value. At scale, this multiplies across every branch.
|
|
158
|
+
|
|
159
|
+
### 4.3 Missing React.memo on Expensive Pure Components
|
|
160
|
+
|
|
161
|
+
**Problem:** Components that receive the same props still re-render because React does not shallow-compare props by default.
|
|
162
|
+
|
|
163
|
+
**Impact:** A list of 100 items where each item component takes 3ms to render wastes 300ms on every parent re-render.
|
|
164
|
+
|
|
165
|
+
### 4.4 Context Over-use
|
|
166
|
+
|
|
167
|
+
**Problem:** When any value in a Context changes, every consumer of that Context re-renders, even if they only use a portion of the context value.
|
|
168
|
+
|
|
169
|
+
**Impact:** A global context with user data, theme, and cart state causes every consumer to re-render when any single value changes. Measured at 4-10x more re-renders than necessary in typical apps.
|
|
170
|
+
|
|
171
|
+
### 4.5 Large Component Trees Without Boundaries
|
|
172
|
+
|
|
173
|
+
**Problem:** Monolithic component trees lack memoization boundaries, so renders cascade unchecked.
|
|
174
|
+
|
|
175
|
+
**Impact:** A 1,000-node component tree at 1ms per node = 1 second blocked render.
|
|
176
|
+
|
|
177
|
+
### 4.6 Inline Functions in JSX
|
|
178
|
+
|
|
179
|
+
**Problem:** Arrow functions in JSX (`onClick={() => handleClick(id)}`) create new function references every render, breaking shallow comparison in `React.memo`.
|
|
180
|
+
|
|
181
|
+
```jsx
|
|
182
|
+
// BAD: New function reference every render
|
|
183
|
+
<Button onClick={() => handleClick(item.id)} />
|
|
184
|
+
|
|
185
|
+
// GOOD: Stable reference with useCallback
|
|
186
|
+
const handleItemClick = useCallback(() => handleClick(item.id), [item.id]);
|
|
187
|
+
<Button onClick={handleItemClick} />
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
### 4.7 Inline Object/Array Literals as Props
|
|
191
|
+
|
|
192
|
+
**Problem:** `style={{ color: 'red' }}` or `data={[1,2,3]}` creates a new object/array reference every render.
|
|
193
|
+
|
|
194
|
+
```jsx
|
|
195
|
+
// BAD: New object every render -- breaks memo
|
|
196
|
+
<Chart style={{ width: 300, height: 200 }} config={{ animate: true }} />
|
|
197
|
+
|
|
198
|
+
// GOOD: Stable references
|
|
199
|
+
const chartStyle = useMemo(() => ({ width: 300, height: 200 }), []);
|
|
200
|
+
const chartConfig = useMemo(() => ({ animate: true }), []);
|
|
201
|
+
<Chart style={chartStyle} config={chartConfig} />
|
|
202
|
+
|
|
203
|
+
// BEST: Constants outside component (if truly static)
|
|
204
|
+
const CHART_STYLE = { width: 300, height: 200 };
|
|
205
|
+
const CHART_CONFIG = { animate: true };
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
### 4.8 Expensive Computations in Render
|
|
209
|
+
|
|
210
|
+
**Problem:** Filtering, sorting, or transforming large datasets on every render without memoization.
|
|
211
|
+
|
|
212
|
+
**Impact:** Sorting 10,000 items takes ~5-15ms. If the parent re-renders 10 times during an interaction, that is 50-150ms wasted on identical sorts.
|
|
213
|
+
|
|
214
|
+
### 4.9 Unvirtualized Long Lists
|
|
215
|
+
|
|
216
|
+
**Problem:** Rendering 10,000+ DOM nodes for a scrollable list when only 20-30 are visible.
|
|
217
|
+
|
|
218
|
+
**Impact:** Initial render of 10,000 list items can take 2-5 seconds. DOM node count above 1,500 degrades INP significantly.
|
|
219
|
+
|
|
220
|
+
### 4.10 Uncontrolled Bundle Growth
|
|
221
|
+
|
|
222
|
+
**Problem:** Importing entire libraries (`import _ from 'lodash'` instead of `import debounce from 'lodash/debounce'`) or lacking code splitting.
|
|
223
|
+
|
|
224
|
+
**Impact:** Lodash full import = 71 KB gzipped vs. single function = 1-2 KB. Moment.js = 67 KB gzipped vs. date-fns individual function = 1-3 KB.
|
|
225
|
+
|
|
226
|
+
### 4.11 Synchronous Expensive Updates Blocking Input
|
|
227
|
+
|
|
228
|
+
**Problem:** CPU-intensive operations (filtering large datasets, complex calculations) run synchronously, blocking the main thread during user input.
|
|
229
|
+
|
|
230
|
+
**Impact:** Typing in a search field that synchronously filters 50,000 items causes 200-500ms input lag per keystroke.
|
|
231
|
+
|
|
232
|
+
### 4.12 Excessive useEffect Chains
|
|
233
|
+
|
|
234
|
+
**Problem:** Multiple `useEffect` hooks that trigger state updates, causing cascading re-renders across multiple commit cycles.
|
|
235
|
+
|
|
236
|
+
**Impact:** Each `useEffect` that calls `setState` triggers a new render cycle. Three chained effects = three extra renders after the initial one.
|
|
237
|
+
|
|
238
|
+
### 4.13 Unoptimized Images and Media
|
|
239
|
+
|
|
240
|
+
**Problem:** Loading full-resolution images without lazy loading, proper sizing, or modern formats.
|
|
241
|
+
|
|
242
|
+
**Impact:** A single unoptimized hero image can add 500 KB-2 MB to page weight, dominating LCP.
|
|
243
|
+
|
|
244
|
+
### 4.14 Third-party Script Bloat
|
|
245
|
+
|
|
246
|
+
**Problem:** Analytics, chat widgets, A/B testing tools adding 100-300 KB of additional JavaScript.
|
|
247
|
+
|
|
248
|
+
**Impact:** Third-party scripts compete for main thread time during hydration. Google recommends a 50 KB budget for third-party scripts.
|
|
249
|
+
|
|
250
|
+
### 4.15 Layout Thrashing from DOM Reads/Writes
|
|
251
|
+
|
|
252
|
+
**Problem:** Measuring DOM elements (getBoundingClientRect) interleaved with mutations forces synchronous layout recalculation.
|
|
253
|
+
|
|
254
|
+
**Impact:** A loop that reads and writes 100 element positions can take 50-200ms instead of < 5ms when batched.
|
|
255
|
+
|
|
256
|
+
---
|
|
257
|
+
|
|
258
|
+
## 5. Optimization Patterns
|
|
259
|
+
|
|
260
|
+
### 5.1 React.memo -- Prevent Unnecessary Re-renders
|
|
261
|
+
|
|
262
|
+
Use `React.memo` to skip re-rendering when props have not changed (shallow comparison).
|
|
263
|
+
|
|
264
|
+
```jsx
|
|
265
|
+
// BEFORE: ProductCard re-renders every time parent re-renders (~8ms each)
|
|
266
|
+
function ProductCard({ product, onAddToCart }) {
|
|
267
|
+
return (
|
|
268
|
+
<div>
|
|
269
|
+
<img src={product.image} alt={product.name} />
|
|
270
|
+
<h3>{product.name}</h3>
|
|
271
|
+
<p>${product.price}</p>
|
|
272
|
+
<button onClick={() => onAddToCart(product.id)}>Add to Cart</button>
|
|
273
|
+
</div>
|
|
274
|
+
);
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
// AFTER: Only re-renders when product or onAddToCart reference changes (~0ms when skipped)
|
|
278
|
+
const ProductCard = React.memo(function ProductCard({ product, onAddToCart }) {
|
|
279
|
+
return (
|
|
280
|
+
<div>
|
|
281
|
+
<img src={product.image} alt={product.name} />
|
|
282
|
+
<h3>{product.name}</h3>
|
|
283
|
+
<p>${product.price}</p>
|
|
284
|
+
<button onClick={() => onAddToCart(product.id)}>Add to Cart</button>
|
|
285
|
+
</div>
|
|
286
|
+
);
|
|
287
|
+
});
|
|
288
|
+
// Measured improvement: List of 100 products, parent re-render drops from 300ms to <1ms
|
|
289
|
+
```
|
|
290
|
+
|
|
291
|
+
**When to use:** Components that render the same output for the same props, especially in lists, data grids, and chart components. Not useful for components that almost always receive different props.
|
|
292
|
+
|
|
293
|
+
### 5.2 useMemo -- Cache Expensive Computations
|
|
294
|
+
|
|
295
|
+
```jsx
|
|
296
|
+
// BEFORE: Sorts 10,000 items on every render (~12ms per render)
|
|
297
|
+
function ProductList({ products, sortBy }) {
|
|
298
|
+
const sorted = products.sort((a, b) => a[sortBy] - b[sortBy]); // Mutates + recalculates
|
|
299
|
+
return sorted.map(p => <ProductCard key={p.id} product={p} />);
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
// AFTER: Only re-sorts when products or sortBy changes (~0ms on unchanged renders)
|
|
303
|
+
function ProductList({ products, sortBy }) {
|
|
304
|
+
const sorted = useMemo(
|
|
305
|
+
() => [...products].sort((a, b) => a[sortBy] - b[sortBy]),
|
|
306
|
+
[products, sortBy]
|
|
307
|
+
);
|
|
308
|
+
return sorted.map(p => <ProductCard key={p.id} product={p} />);
|
|
309
|
+
}
|
|
310
|
+
// Measured improvement: 10 parent re-renders save ~120ms total
|
|
311
|
+
```
|
|
312
|
+
|
|
313
|
+
**When to use:** Computations that are measurably slow (> 2ms), where dependencies change infrequently. Do not wrap trivial operations -- the hook itself has overhead (~0.1ms).
|
|
314
|
+
|
|
315
|
+
### 5.3 useCallback -- Stabilize Function References
|
|
316
|
+
|
|
317
|
+
```jsx
|
|
318
|
+
// BEFORE: New function reference every render breaks React.memo on children
|
|
319
|
+
function ProductList({ products }) {
|
|
320
|
+
const [cart, setCart] = useState([]);
|
|
321
|
+
|
|
322
|
+
// This function is recreated every render
|
|
323
|
+
const handleAddToCart = (id) => {
|
|
324
|
+
setCart(prev => [...prev, id]);
|
|
325
|
+
};
|
|
326
|
+
|
|
327
|
+
return products.map(p => (
|
|
328
|
+
<ProductCard key={p.id} product={p} onAddToCart={handleAddToCart} />
|
|
329
|
+
));
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
// AFTER: Stable reference lets React.memo work on ProductCard
|
|
333
|
+
function ProductList({ products }) {
|
|
334
|
+
const [cart, setCart] = useState([]);
|
|
335
|
+
|
|
336
|
+
const handleAddToCart = useCallback((id) => {
|
|
337
|
+
setCart(prev => [...prev, id]);
|
|
338
|
+
}, []); // Empty deps because we use functional updater
|
|
339
|
+
|
|
340
|
+
return products.map(p => (
|
|
341
|
+
<MemoizedProductCard key={p.id} product={p} onAddToCart={handleAddToCart} />
|
|
342
|
+
));
|
|
343
|
+
}
|
|
344
|
+
// Measured improvement: 200 product list re-render drops from 400ms to <5ms
|
|
345
|
+
```
|
|
346
|
+
|
|
347
|
+
**Critical rule:** `useCallback` is useless without `React.memo` on the child component. Always memoize the child first, then stabilize the callback.
|
|
348
|
+
|
|
349
|
+
### 5.4 Code Splitting with React.lazy and Suspense
|
|
350
|
+
|
|
351
|
+
```jsx
|
|
352
|
+
// BEFORE: All routes loaded upfront (2.3 MB bundle)
|
|
353
|
+
import Dashboard from './Dashboard';
|
|
354
|
+
import Settings from './Settings';
|
|
355
|
+
import Analytics from './Analytics';
|
|
356
|
+
|
|
357
|
+
// AFTER: Routes loaded on demand (875 KB initial bundle -- 62% reduction)
|
|
358
|
+
const Dashboard = React.lazy(() => import('./Dashboard'));
|
|
359
|
+
const Settings = React.lazy(() => import('./Settings'));
|
|
360
|
+
const Analytics = React.lazy(() => import('./Analytics'));
|
|
361
|
+
|
|
362
|
+
function App() {
|
|
363
|
+
return (
|
|
364
|
+
<Suspense fallback={<LoadingSpinner />}>
|
|
365
|
+
<Routes>
|
|
366
|
+
<Route path="/dashboard" element={<Dashboard />} />
|
|
367
|
+
<Route path="/settings" element={<Settings />} />
|
|
368
|
+
<Route path="/analytics" element={<Analytics />} />
|
|
369
|
+
</Routes>
|
|
370
|
+
</Suspense>
|
|
371
|
+
);
|
|
372
|
+
}
|
|
373
|
+
// Measured improvement: TTI from 5.2s to 2.7s (48% improvement)
|
|
374
|
+
// FCP from 1.8s to 1.2s (33% improvement)
|
|
375
|
+
```
|
|
376
|
+
|
|
377
|
+
**Best practice:** Split at route boundaries first, then at heavy component boundaries (modals, drawers, complex forms). Use webpack magic comments for named chunks: `import(/* webpackChunkName: "analytics" */ './Analytics')`.
|
|
378
|
+
|
|
379
|
+
### 5.5 List Virtualization
|
|
380
|
+
|
|
381
|
+
Only render items visible in the viewport. For lists of 1,000+ items, virtualization is essential.
|
|
382
|
+
|
|
383
|
+
```jsx
|
|
384
|
+
// BEFORE: Renders all 10,000 rows (~3 seconds initial render)
|
|
385
|
+
function UserList({ users }) {
|
|
386
|
+
return (
|
|
387
|
+
<div style={{ height: '600px', overflow: 'auto' }}>
|
|
388
|
+
{users.map(user => <UserRow key={user.id} user={user} />)}
|
|
389
|
+
</div>
|
|
390
|
+
);
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
// AFTER: Renders only ~15 visible rows (~15ms initial render)
|
|
394
|
+
import { FixedSizeList } from 'react-window';
|
|
395
|
+
|
|
396
|
+
function UserList({ users }) {
|
|
397
|
+
const Row = ({ index, style }) => (
|
|
398
|
+
<div style={style}>
|
|
399
|
+
<UserRow user={users[index]} />
|
|
400
|
+
</div>
|
|
401
|
+
);
|
|
402
|
+
return (
|
|
403
|
+
<FixedSizeList
|
|
404
|
+
height={600}
|
|
405
|
+
itemCount={users.length}
|
|
406
|
+
itemSize={50}
|
|
407
|
+
width="100%"
|
|
408
|
+
>
|
|
409
|
+
{Row}
|
|
410
|
+
</FixedSizeList>
|
|
411
|
+
);
|
|
412
|
+
}
|
|
413
|
+
// Measured improvement: 10,000 items render time from ~3000ms to ~15ms (200x faster)
|
|
414
|
+
```
|
|
415
|
+
|
|
416
|
+
**Library comparison:**
|
|
417
|
+
|
|
418
|
+
| Library | Size (gzipped) | Best for |
|
|
419
|
+
|---|---|---|
|
|
420
|
+
| react-window | ~6 KB | Fixed or variable size lists/grids (lighter) |
|
|
421
|
+
| react-virtualized | ~27 KB | Complex tables, multi-column grids |
|
|
422
|
+
| @tanstack/react-virtual | ~4 KB | Headless, framework-agnostic virtualization |
|
|
423
|
+
| react-virtuoso | ~15 KB | Variable height items with smooth scrolling |
|
|
424
|
+
|
|
425
|
+
Use `FixedSizeList` when all items have the same height -- it is the most performant option because position calculation is O(1) instead of O(n).
|
|
426
|
+
|
|
427
|
+
### 5.6 React Server Components (RSC)
|
|
428
|
+
|
|
429
|
+
Server Components execute only on the server. Their code never ships to the client, reducing bundle size and moving data fetching closer to the data source.
|
|
430
|
+
|
|
431
|
+
```jsx
|
|
432
|
+
// Server Component (default in Next.js App Router) -- zero client JS
|
|
433
|
+
async function ProductPage({ params }) {
|
|
434
|
+
const product = await db.products.findById(params.id); // Direct DB access
|
|
435
|
+
const reviews = await db.reviews.findByProduct(params.id);
|
|
436
|
+
|
|
437
|
+
return (
|
|
438
|
+
<div>
|
|
439
|
+
<ProductDetails product={product} /> {/* Server Component */}
|
|
440
|
+
<ReviewList reviews={reviews} /> {/* Server Component */}
|
|
441
|
+
<AddToCartButton productId={product.id} /> {/* Client Component */}
|
|
442
|
+
</div>
|
|
443
|
+
);
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
// Client Component -- only interactive parts ship JS
|
|
447
|
+
'use client';
|
|
448
|
+
function AddToCartButton({ productId }) {
|
|
449
|
+
const [added, setAdded] = useState(false);
|
|
450
|
+
return (
|
|
451
|
+
<button onClick={() => { addToCart(productId); setAdded(true); }}>
|
|
452
|
+
{added ? 'Added' : 'Add to Cart'}
|
|
453
|
+
</button>
|
|
454
|
+
);
|
|
455
|
+
}
|
|
456
|
+
```
|
|
457
|
+
|
|
458
|
+
**Measured benefits:**
|
|
459
|
+
- Bundle size reduction: 40-60% for data-heavy pages
|
|
460
|
+
- LCP improvement: up to 67% (server renders closer to data source)
|
|
461
|
+
- TTI improvement: up to 63% (less JavaScript to parse and execute)
|
|
462
|
+
|
|
463
|
+
**When to use RSC:**
|
|
464
|
+
- Components that fetch and display data without interactivity
|
|
465
|
+
- Components using heavy libraries (markdown parsers, syntax highlighters, date formatters)
|
|
466
|
+
- Layout components, navigation, footers
|
|
467
|
+
|
|
468
|
+
**When to use Client Components:**
|
|
469
|
+
- Components with `useState`, `useEffect`, event handlers
|
|
470
|
+
- Components using browser APIs (localStorage, geolocation)
|
|
471
|
+
- Components requiring real-time updates
|
|
472
|
+
|
|
473
|
+
### 5.7 useTransition -- Non-blocking State Updates
|
|
474
|
+
|
|
475
|
+
Mark expensive updates as transitions so they do not block user input.
|
|
476
|
+
|
|
477
|
+
```jsx
|
|
478
|
+
// BEFORE: Filtering 50,000 items blocks input (~300ms per keystroke)
|
|
479
|
+
function SearchProducts({ products }) {
|
|
480
|
+
const [query, setQuery] = useState('');
|
|
481
|
+
const filtered = products.filter(p =>
|
|
482
|
+
p.name.toLowerCase().includes(query.toLowerCase())
|
|
483
|
+
);
|
|
484
|
+
|
|
485
|
+
return (
|
|
486
|
+
<>
|
|
487
|
+
<input value={query} onChange={e => setQuery(e.target.value)} />
|
|
488
|
+
<ProductGrid products={filtered} />
|
|
489
|
+
</>
|
|
490
|
+
);
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
// AFTER: Input stays responsive, list updates in background
|
|
494
|
+
function SearchProducts({ products }) {
|
|
495
|
+
const [query, setQuery] = useState('');
|
|
496
|
+
const [deferredQuery, setDeferredQuery] = useState('');
|
|
497
|
+
const [isPending, startTransition] = useTransition();
|
|
498
|
+
|
|
499
|
+
const handleChange = (e) => {
|
|
500
|
+
setQuery(e.target.value); // Urgent: update input immediately
|
|
501
|
+
startTransition(() => {
|
|
502
|
+
setDeferredQuery(e.target.value); // Non-urgent: update list in background
|
|
503
|
+
});
|
|
504
|
+
};
|
|
505
|
+
|
|
506
|
+
const filtered = useMemo(
|
|
507
|
+
() => products.filter(p => p.name.toLowerCase().includes(deferredQuery.toLowerCase())),
|
|
508
|
+
[products, deferredQuery]
|
|
509
|
+
);
|
|
510
|
+
|
|
511
|
+
return (
|
|
512
|
+
<>
|
|
513
|
+
<input value={query} onChange={handleChange} />
|
|
514
|
+
<div style={{ opacity: isPending ? 0.7 : 1 }}>
|
|
515
|
+
<ProductGrid products={filtered} />
|
|
516
|
+
</div>
|
|
517
|
+
</>
|
|
518
|
+
);
|
|
519
|
+
}
|
|
520
|
+
// Measured improvement: Input latency drops from 300ms to <50ms
|
|
521
|
+
```
|
|
522
|
+
|
|
523
|
+
**Important caveat:** `useTransition` causes double re-renders. Only use it when the deferred work is genuinely expensive (> 100ms). Never use it for all state updates.
|
|
524
|
+
|
|
525
|
+
### 5.8 useDeferredValue -- Defer Expensive Derived Renders
|
|
526
|
+
|
|
527
|
+
```jsx
|
|
528
|
+
// AFTER: Defer the value itself rather than the update
|
|
529
|
+
function SearchProducts({ products }) {
|
|
530
|
+
const [query, setQuery] = useState('');
|
|
531
|
+
const deferredQuery = useDeferredValue(query);
|
|
532
|
+
const isStale = query !== deferredQuery;
|
|
533
|
+
|
|
534
|
+
const filtered = useMemo(
|
|
535
|
+
() => products.filter(p => p.name.toLowerCase().includes(deferredQuery.toLowerCase())),
|
|
536
|
+
[products, deferredQuery]
|
|
537
|
+
);
|
|
538
|
+
|
|
539
|
+
return (
|
|
540
|
+
<>
|
|
541
|
+
<input value={query} onChange={e => setQuery(e.target.value)} />
|
|
542
|
+
<div style={{ opacity: isStale ? 0.7 : 1 }}>
|
|
543
|
+
<ProductGrid products={filtered} />
|
|
544
|
+
</div>
|
|
545
|
+
</>
|
|
546
|
+
);
|
|
547
|
+
}
|
|
548
|
+
```
|
|
549
|
+
|
|
550
|
+
**When to prefer `useDeferredValue` over `useTransition`:** When you do not control the state-setting code (e.g., value comes as a prop or from an external API).
|
|
551
|
+
|
|
552
|
+
### 5.9 State Colocation -- Move State Down
|
|
553
|
+
|
|
554
|
+
```jsx
|
|
555
|
+
// BEFORE: Search state at root re-renders entire dashboard
|
|
556
|
+
function Dashboard() {
|
|
557
|
+
const [search, setSearch] = useState('');
|
|
558
|
+
return (
|
|
559
|
+
<div>
|
|
560
|
+
<Header />
|
|
561
|
+
<SearchBar value={search} onChange={setSearch} />
|
|
562
|
+
<Charts /> {/* Expensive: 50ms render, re-renders on every keystroke */}
|
|
563
|
+
<DataTable />
|
|
564
|
+
</div>
|
|
565
|
+
);
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
// AFTER: Search state colocated -- Charts never re-renders during typing
|
|
569
|
+
function Dashboard() {
|
|
570
|
+
return (
|
|
571
|
+
<div>
|
|
572
|
+
<Header />
|
|
573
|
+
<SearchSection /> {/* Contains its own search state */}
|
|
574
|
+
<Charts />
|
|
575
|
+
<DataTable />
|
|
576
|
+
</div>
|
|
577
|
+
);
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
function SearchSection() {
|
|
581
|
+
const [search, setSearch] = useState('');
|
|
582
|
+
return <SearchBar value={search} onChange={setSearch} />;
|
|
583
|
+
}
|
|
584
|
+
// Measured improvement: Eliminates 50ms Charts re-render on every keystroke
|
|
585
|
+
```
|
|
586
|
+
|
|
587
|
+
### 5.10 Streaming SSR with Suspense
|
|
588
|
+
|
|
589
|
+
```jsx
|
|
590
|
+
// Server component with streaming
|
|
591
|
+
async function ProductPage({ params }) {
|
|
592
|
+
return (
|
|
593
|
+
<div>
|
|
594
|
+
<ProductHeader productId={params.id} /> {/* Streams immediately */}
|
|
595
|
+
<Suspense fallback={<ReviewsSkeleton />}>
|
|
596
|
+
<Reviews productId={params.id} /> {/* Streams when ready */}
|
|
597
|
+
</Suspense>
|
|
598
|
+
<Suspense fallback={<RecommendationsSkeleton />}>
|
|
599
|
+
<Recommendations productId={params.id} /> {/* Streams when ready */}
|
|
600
|
+
</Suspense>
|
|
601
|
+
</div>
|
|
602
|
+
);
|
|
603
|
+
}
|
|
604
|
+
```
|
|
605
|
+
|
|
606
|
+
**Measured improvement:** TTFB drops from 350-550ms to 40-90ms with progressive streaming architecture.
|
|
607
|
+
|
|
608
|
+
---
|
|
609
|
+
|
|
610
|
+
## 6. Anti-Patterns
|
|
611
|
+
|
|
612
|
+
### 6.1 Premature Memoization
|
|
613
|
+
|
|
614
|
+
**Anti-pattern:** Wrapping every component in `React.memo` and every value in `useMemo` "just in case."
|
|
615
|
+
|
|
616
|
+
**Why it hurts:** Memoization has overhead (~0.1ms per hook call). For cheap components (< 1ms render), the cost of comparing props can exceed the cost of re-rendering. React Compiler (v1.0, stable October 2025) makes manual memoization largely unnecessary by automatically adding it during compilation.
|
|
617
|
+
|
|
618
|
+
**Rule:** Profile first, memoize second. Only memoize components where the Profiler shows wasted renders > 2ms.
|
|
619
|
+
|
|
620
|
+
### 6.2 React.memo on Components That Always Receive New Props
|
|
621
|
+
|
|
622
|
+
**Anti-pattern:** Wrapping a component in `React.memo` while passing inline objects, arrays, or unstable callbacks as props.
|
|
623
|
+
|
|
624
|
+
```jsx
|
|
625
|
+
// USELESS: memo is defeated by new object every render
|
|
626
|
+
const Chart = React.memo(function Chart({ config }) { /* ... */ });
|
|
627
|
+
<Chart config={{ type: 'bar', animate: true }} /> // New object every render
|
|
628
|
+
```
|
|
629
|
+
|
|
630
|
+
**Fix:** Stabilize all props with `useMemo`/`useCallback` or extract constants.
|
|
631
|
+
|
|
632
|
+
### 6.3 Inline Object Props
|
|
633
|
+
|
|
634
|
+
**Anti-pattern:** `style={{ marginTop: 20 }}` or `options={{ verbose: true }}` in JSX creates a new reference every render.
|
|
635
|
+
|
|
636
|
+
**Impact:** Breaks `React.memo`, `useEffect` dependency comparisons, and `useMemo` memoization.
|
|
637
|
+
|
|
638
|
+
### 6.4 State in the Wrong Component
|
|
639
|
+
|
|
640
|
+
**Anti-pattern:** Lifting state higher than necessary "for convenience."
|
|
641
|
+
|
|
642
|
+
**Impact:** A search input state at the app root re-renders the entire component tree. Moving it to the search component eliminates re-renders in all sibling branches.
|
|
643
|
+
|
|
644
|
+
### 6.5 Overusing useEffect for Derived State
|
|
645
|
+
|
|
646
|
+
**Anti-pattern:** Using `useState` + `useEffect` to compute values from props or other state.
|
|
647
|
+
|
|
648
|
+
```jsx
|
|
649
|
+
// BAD: Extra render cycle, unnecessary complexity
|
|
650
|
+
function FullName({ firstName, lastName }) {
|
|
651
|
+
const [fullName, setFullName] = useState('');
|
|
652
|
+
useEffect(() => {
|
|
653
|
+
setFullName(`${firstName} ${lastName}`);
|
|
654
|
+
}, [firstName, lastName]);
|
|
655
|
+
return <span>{fullName}</span>;
|
|
656
|
+
}
|
|
657
|
+
|
|
658
|
+
// GOOD: Compute during render -- no extra cycle
|
|
659
|
+
function FullName({ firstName, lastName }) {
|
|
660
|
+
const fullName = `${firstName} ${lastName}`;
|
|
661
|
+
return <span>{fullName}</span>;
|
|
662
|
+
}
|
|
663
|
+
```
|
|
664
|
+
|
|
665
|
+
**Impact:** Each `useEffect` that calls `setState` causes an additional render cycle. Three such patterns in a component tree = 3 extra renders after the initial one.
|
|
666
|
+
|
|
667
|
+
### 6.6 useEffect as Event Handler
|
|
668
|
+
|
|
669
|
+
**Anti-pattern:** Using `useEffect` to respond to user actions instead of handling them in event callbacks.
|
|
670
|
+
|
|
671
|
+
```jsx
|
|
672
|
+
// BAD: Effect runs after render, causes extra cycle
|
|
673
|
+
const [submitted, setSubmitted] = useState(false);
|
|
674
|
+
useEffect(() => {
|
|
675
|
+
if (submitted) {
|
|
676
|
+
sendAnalytics('form_submit');
|
|
677
|
+
setSubmitted(false);
|
|
678
|
+
}
|
|
679
|
+
}, [submitted]);
|
|
680
|
+
|
|
681
|
+
// GOOD: Handle in the event itself
|
|
682
|
+
const handleSubmit = () => {
|
|
683
|
+
submitForm(data);
|
|
684
|
+
sendAnalytics('form_submit');
|
|
685
|
+
};
|
|
686
|
+
```
|
|
687
|
+
|
|
688
|
+
### 6.7 Context for High-frequency State
|
|
689
|
+
|
|
690
|
+
**Anti-pattern:** Putting frequently changing values (mouse position, scroll offset, search query) in React Context.
|
|
691
|
+
|
|
692
|
+
**Impact:** Every context consumer re-renders on every context value change. A mouse position context with 30 consumers at 60 updates/second = 1,800 component re-renders per second.
|
|
693
|
+
|
|
694
|
+
**Fix:** Use Zustand, Jotai, or signals for high-frequency state. Zustand updates only components that subscribe to the specific slice of state that changed. Jotai uses atoms to provide fine-grained subscriptions, eliminating the cascading re-render problem entirely.
|
|
695
|
+
|
|
696
|
+
### 6.8 Unnecessary key Changes
|
|
697
|
+
|
|
698
|
+
**Anti-pattern:** Using `key={Math.random()}` or changing keys unnecessarily, forcing React to unmount and remount components instead of updating them.
|
|
699
|
+
|
|
700
|
+
**Impact:** Unmount + remount is 2-10x more expensive than an update, and destroys all internal state and DOM nodes.
|
|
701
|
+
|
|
702
|
+
### 6.9 Fetching Data in useEffect Without Cleanup
|
|
703
|
+
|
|
704
|
+
**Anti-pattern:** Firing async requests in `useEffect` without cancellation, leading to race conditions and state updates on unmounted components.
|
|
705
|
+
|
|
706
|
+
```jsx
|
|
707
|
+
// BAD: Race condition if query changes rapidly
|
|
708
|
+
useEffect(() => {
|
|
709
|
+
fetch(`/api/search?q=${query}`)
|
|
710
|
+
.then(res => res.json())
|
|
711
|
+
.then(data => setResults(data));
|
|
712
|
+
}, [query]);
|
|
713
|
+
|
|
714
|
+
// GOOD: Abort previous request
|
|
715
|
+
useEffect(() => {
|
|
716
|
+
const controller = new AbortController();
|
|
717
|
+
fetch(`/api/search?q=${query}`, { signal: controller.signal })
|
|
718
|
+
.then(res => res.json())
|
|
719
|
+
.then(data => setResults(data))
|
|
720
|
+
.catch(err => { if (err.name !== 'AbortError') throw err; });
|
|
721
|
+
return () => controller.abort();
|
|
722
|
+
}, [query]);
|
|
723
|
+
```
|
|
724
|
+
|
|
725
|
+
### 6.10 Over-splitting Components
|
|
726
|
+
|
|
727
|
+
**Anti-pattern:** Creating dozens of tiny components for "separation of concerns" when they are always rendered together and never independently memoized.
|
|
728
|
+
|
|
729
|
+
**Impact:** Each component boundary adds function call overhead and reconciliation work. 50 trivial wrapper components add ~5ms of overhead that provides no optimization benefit.
|
|
730
|
+
|
|
731
|
+
### 6.11 Synchronous Imports of Heavy Libraries
|
|
732
|
+
|
|
733
|
+
**Anti-pattern:** `import moment from 'moment'` at the top of a component file that is only sometimes needed.
|
|
734
|
+
|
|
735
|
+
**Fix:** Dynamic import: `const moment = await import('moment')` or replace with lighter alternatives (date-fns: 2-3 KB per function vs. moment: 67 KB gzipped).
|
|
736
|
+
|
|
737
|
+
### 6.12 Not Using Production Builds
|
|
738
|
+
|
|
739
|
+
**Anti-pattern:** Running development builds in production. React's development mode includes extra warnings, checks, and the Profiler that add 2-5x overhead.
|
|
740
|
+
|
|
741
|
+
**Detection:** React DevTools shows a red icon for development builds. Lighthouse flags this as "Reduce unused JavaScript."
|
|
742
|
+
|
|
743
|
+
---
|
|
744
|
+
|
|
745
|
+
## 7. Architecture-Level Decisions
|
|
746
|
+
|
|
747
|
+
### RSC vs. Client Components Strategy
|
|
748
|
+
|
|
749
|
+
The default should be Server Components (in Next.js App Router, Remix). Add `'use client'` only when a component needs interactivity, browser APIs, or React hooks that use state/effects.
|
|
750
|
+
|
|
751
|
+
**Decision matrix:**
|
|
752
|
+
|
|
753
|
+
| Characteristic | Server Component | Client Component |
|
|
754
|
+
|---|---|---|
|
|
755
|
+
| Fetches data | Direct DB/API access, zero waterfall | Client-side fetch, potential waterfall |
|
|
756
|
+
| Uses state/effects | No | Yes |
|
|
757
|
+
| Uses browser APIs | No | Yes |
|
|
758
|
+
| Bundle impact | Zero JS shipped | Full component JS shipped |
|
|
759
|
+
| Interactivity | None (static HTML) | Full (event handlers, animations) |
|
|
760
|
+
| Rendering location | Server only | Server (SSR) + Client (hydration) |
|
|
761
|
+
|
|
762
|
+
**Real-world rule:** In a typical e-commerce page, 70-80% of components can be Server Components (product details, descriptions, reviews, breadcrumbs, footer). Only cart buttons, search inputs, and interactive filters need to be Client Components.
|
|
763
|
+
|
|
764
|
+
### State Management Performance Impact
|
|
765
|
+
|
|
766
|
+
| Library | Bundle Size (gzipped) | Re-render Behavior | Best For |
|
|
767
|
+
|---|---|---|---|
|
|
768
|
+
| React Context | 0 KB (built-in) | All consumers re-render on any change | Theme, locale, auth (infrequent changes) |
|
|
769
|
+
| Zustand | ~1.5 KB | Only subscribed components re-render | General state, medium complexity |
|
|
770
|
+
| Jotai | ~3.5 KB | Atom-level subscriptions, minimal re-renders | Complex interdependent state |
|
|
771
|
+
| Redux Toolkit | ~11 KB | Selector-based, components re-render on selected slice change | Large apps, complex middleware needs |
|
|
772
|
+
| Recoil | ~22 KB | Atom-level subscriptions | (Deprecated, migrate to Jotai) |
|
|
773
|
+
|
|
774
|
+
**Performance recommendation:** For apps with > 20 components sharing state, Zustand or Jotai provide 4-10x fewer re-renders compared to Context. Context is appropriate only for values that change infrequently (theme, locale, authentication status).
|
|
775
|
+
|
|
776
|
+
### Islands Architecture
|
|
777
|
+
|
|
778
|
+
Render most of the page as static HTML on the server. Hydrate only interactive "islands" independently.
|
|
779
|
+
|
|
780
|
+
**Frameworks:** Astro (React islands), Next.js (partial hydration with Server Components).
|
|
781
|
+
|
|
782
|
+
**Measured benefit:** Pages with 80% static content see 60-70% reduction in client-side JavaScript and 40-50% improvement in TTI compared to full-page hydration.
|
|
783
|
+
|
|
784
|
+
### Streaming SSR Architecture
|
|
785
|
+
|
|
786
|
+
Instead of waiting for the entire page to render on the server, stream HTML progressively as data becomes available.
|
|
787
|
+
|
|
788
|
+
**Key numbers:**
|
|
789
|
+
- Traditional SSR TTFB: 350-550ms (must wait for all data)
|
|
790
|
+
- Streaming SSR TTFB: 40-90ms (streams shell immediately)
|
|
791
|
+
- Progressive hydration with Suspense boundaries enables selective hydration of the most critical interactive elements first
|
|
792
|
+
|
|
793
|
+
---
|
|
794
|
+
|
|
795
|
+
## 8. React Compiler (v1.0)
|
|
796
|
+
|
|
797
|
+
Released stable in October 2025, the React Compiler automatically adds memoization during build time, eliminating most manual `useMemo`, `useCallback`, and `React.memo` calls.
|
|
798
|
+
|
|
799
|
+
**Production benchmarks (Meta Quest Store):**
|
|
800
|
+
- Initial loads and cross-page navigations: up to 12% faster
|
|
801
|
+
- Certain interactions: more than 2.5x faster
|
|
802
|
+
- Memory usage: neutral (no increase despite added memoization)
|
|
803
|
+
|
|
804
|
+
**Typical results by app optimization state:**
|
|
805
|
+
|
|
806
|
+
| Starting Point | Re-render Reduction | Interaction Latency Improvement |
|
|
807
|
+
|---|---|---|
|
|
808
|
+
| No manual optimization | 50-80% | 40-60% |
|
|
809
|
+
| Some optimization | 30-60% | 20-40% |
|
|
810
|
+
| Already well-optimized | 10-20% | 5-15% |
|
|
811
|
+
|
|
812
|
+
**Bundle impact:** Before Compiler: 250 KB (with memoization boilerplate) -> After Compiler: 180 KB (28% reduction in one reported case). Render times dropped from 8ms to 3ms average in early adopter reports.
|
|
813
|
+
|
|
814
|
+
**Adoption recommendation:** Enable the React Compiler for new projects. For existing projects, add incrementally using the `eslint-plugin-react-compiler` to identify code that violates the Rules of React (which the compiler requires).
|
|
815
|
+
|
|
816
|
+
---
|
|
817
|
+
|
|
818
|
+
## 9. Testing & Regression Prevention
|
|
819
|
+
|
|
820
|
+
### React Profiler in CI
|
|
821
|
+
|
|
822
|
+
Use the programmatic `<Profiler>` API to capture render durations in automated tests and fail builds that exceed thresholds.
|
|
823
|
+
|
|
824
|
+
```javascript
|
|
825
|
+
// jest test using React Testing Library + Profiler
|
|
826
|
+
import { Profiler } from 'react';
|
|
827
|
+
import { render } from '@testing-library/react';
|
|
828
|
+
|
|
829
|
+
test('Dashboard renders within budget', () => {
|
|
830
|
+
let renderTime;
|
|
831
|
+
const onRender = (id, phase, actualDuration) => {
|
|
832
|
+
renderTime = actualDuration;
|
|
833
|
+
};
|
|
834
|
+
|
|
835
|
+
render(
|
|
836
|
+
<Profiler id="Dashboard" onRender={onRender}>
|
|
837
|
+
<Dashboard data={testData} />
|
|
838
|
+
</Profiler>
|
|
839
|
+
);
|
|
840
|
+
|
|
841
|
+
expect(renderTime).toBeLessThan(16); // Must render within one frame
|
|
842
|
+
});
|
|
843
|
+
```
|
|
844
|
+
|
|
845
|
+
### Bundle Size Monitoring
|
|
846
|
+
|
|
847
|
+
**Tools:**
|
|
848
|
+
- `@next/bundle-analyzer` -- visualize Next.js bundle composition
|
|
849
|
+
- `source-map-explorer` -- analyze bundle for any React app
|
|
850
|
+
- `bundlesize` npm package -- fail CI if bundle exceeds budget
|
|
851
|
+
|
|
852
|
+
```json
|
|
853
|
+
// package.json -- bundlesize configuration
|
|
854
|
+
{
|
|
855
|
+
"bundlesize": [
|
|
856
|
+
{ "path": "build/static/js/main.*.js", "maxSize": "150 kB" },
|
|
857
|
+
{ "path": "build/static/js/vendor.*.js", "maxSize": "80 kB" },
|
|
858
|
+
{ "path": "build/static/css/main.*.css", "maxSize": "20 kB" }
|
|
859
|
+
]
|
|
860
|
+
}
|
|
861
|
+
```
|
|
862
|
+
|
|
863
|
+
### Lighthouse CI
|
|
864
|
+
|
|
865
|
+
Automate Lighthouse audits on every commit to prevent performance regressions.
|
|
866
|
+
|
|
867
|
+
```json
|
|
868
|
+
// lighthouserc.json
|
|
869
|
+
{
|
|
870
|
+
"ci": {
|
|
871
|
+
"collect": {
|
|
872
|
+
"startServerCommand": "npm run start",
|
|
873
|
+
"url": ["http://localhost:3000", "http://localhost:3000/dashboard"],
|
|
874
|
+
"numberOfRuns": 3
|
|
875
|
+
},
|
|
876
|
+
"assert": {
|
|
877
|
+
"assertions": {
|
|
878
|
+
"categories:performance": ["error", { "minScore": 0.9 }],
|
|
879
|
+
"first-contentful-paint": ["warn", { "maxNumericValue": 1800 }],
|
|
880
|
+
"interactive": ["error", { "maxNumericValue": 3500 }],
|
|
881
|
+
"largest-contentful-paint": ["error", { "maxNumericValue": 2500 }],
|
|
882
|
+
"total-blocking-time": ["warn", { "maxNumericValue": 200 }]
|
|
883
|
+
}
|
|
884
|
+
}
|
|
885
|
+
}
|
|
886
|
+
}
|
|
887
|
+
```
|
|
888
|
+
|
|
889
|
+
### Performance Regression Checklist
|
|
890
|
+
|
|
891
|
+
- [ ] Run `npx react-scan` or React DevTools Profiler on all changed views
|
|
892
|
+
- [ ] Verify no new components render > 16ms
|
|
893
|
+
- [ ] Check bundle size delta with `source-map-explorer` or `@next/bundle-analyzer`
|
|
894
|
+
- [ ] Run Lighthouse CI with performance score assertion >= 90
|
|
895
|
+
- [ ] Verify no new unnecessary re-renders with why-did-you-render
|
|
896
|
+
- [ ] Check that new Context providers do not trigger cascading re-renders
|
|
897
|
+
- [ ] Validate new lists with > 100 items use virtualization
|
|
898
|
+
|
|
899
|
+
---
|
|
900
|
+
|
|
901
|
+
## 10. Decision Trees
|
|
902
|
+
|
|
903
|
+
### "My React App Is Slow" Diagnostic Flowchart
|
|
904
|
+
|
|
905
|
+
```
|
|
906
|
+
START: App feels slow
|
|
907
|
+
|
|
|
908
|
+
+--> Is it slow on INITIAL LOAD?
|
|
909
|
+
| |
|
|
910
|
+
| +--> YES: Check bundle size
|
|
911
|
+
| | |
|
|
912
|
+
| | +--> Bundle > 200 KB gzipped?
|
|
913
|
+
| | | +--> Add code splitting with React.lazy at route boundaries
|
|
914
|
+
| | | +--> Tree-shake imports (lodash/debounce, not lodash)
|
|
915
|
+
| | | +--> Analyze with source-map-explorer for large dependencies
|
|
916
|
+
| | |
|
|
917
|
+
| | +--> Bundle is reasonable?
|
|
918
|
+
| | +--> Check SSR/hydration
|
|
919
|
+
| | | +--> Use streaming SSR with Suspense boundaries
|
|
920
|
+
| | | +--> Convert data-only components to Server Components
|
|
921
|
+
| | | +--> Implement selective/progressive hydration
|
|
922
|
+
| | |
|
|
923
|
+
| | +--> Check network waterfall
|
|
924
|
+
| | +--> Preload critical resources
|
|
925
|
+
| | +--> Colocate data fetching in Server Components
|
|
926
|
+
| |
|
|
927
|
+
| +--> NO: Check INTERACTION performance
|
|
928
|
+
| |
|
|
929
|
+
| +--> Open React DevTools Profiler, record the slow interaction
|
|
930
|
+
| |
|
|
931
|
+
| +--> Are many components re-rendering unnecessarily?
|
|
932
|
+
| | +--> YES: Move state closer to where it is used
|
|
933
|
+
| | | +--> Add React.memo to expensive pure components
|
|
934
|
+
| | | +--> Stabilize callbacks with useCallback
|
|
935
|
+
| | | +--> Replace Context with Zustand/Jotai for frequent updates
|
|
936
|
+
| | |
|
|
937
|
+
| | +--> NO: Are individual components slow?
|
|
938
|
+
| | +--> YES: Profile the component
|
|
939
|
+
| | | +--> Expensive computation? -> useMemo
|
|
940
|
+
| | | +--> Long list? -> Virtualize with react-window
|
|
941
|
+
| | | +--> Heavy sync work? -> useTransition
|
|
942
|
+
| | |
|
|
943
|
+
| | +--> NO: Check for layout thrashing or forced reflows
|
|
944
|
+
| | +--> Batch DOM reads, then batch DOM writes
|
|
945
|
+
| | +--> Use CSS containment on isolated sections
|
|
946
|
+
|
|
|
947
|
+
+--> Is it slow during SCROLLING?
|
|
948
|
+
|
|
|
949
|
+
+--> Long list without virtualization? -> Add react-window or @tanstack/react-virtual
|
|
950
|
+
+--> Heavy components in scroll view? -> Simplify or lazy-render off-screen items
|
|
951
|
+
+--> Layout recalculation on scroll? -> Use CSS will-change, contain, transform
|
|
952
|
+
```
|
|
953
|
+
|
|
954
|
+
### "Should I Use useMemo?" Decision Tree
|
|
955
|
+
|
|
956
|
+
```
|
|
957
|
+
Do you have a value computed from props/state?
|
|
958
|
+
|
|
|
959
|
+
+--> Is the computation measurably expensive (> 2ms in Profiler)?
|
|
960
|
+
| |
|
|
961
|
+
| +--> YES: Use useMemo.
|
|
962
|
+
| +--> NO: Is the result passed as a prop to a React.memo child?
|
|
963
|
+
| |
|
|
964
|
+
| +--> YES: Is it an object/array (not a primitive)?
|
|
965
|
+
| | +--> YES: Use useMemo (referential equality matters).
|
|
966
|
+
| | +--> NO: Skip useMemo (primitives compare by value).
|
|
967
|
+
| |
|
|
968
|
+
| +--> NO: Is the result used as a useEffect dependency?
|
|
969
|
+
| |
|
|
970
|
+
| +--> YES: Is it an object/array?
|
|
971
|
+
| | +--> YES: Use useMemo to prevent infinite loops.
|
|
972
|
+
| | +--> NO: Skip useMemo.
|
|
973
|
+
| |
|
|
974
|
+
| +--> NO: Skip useMemo. The overhead is not worth it.
|
|
975
|
+
|
|
|
976
|
+
+--> Are you using React Compiler (v1.0+)?
|
|
977
|
+
+--> YES: Remove manual useMemo -- the compiler handles it automatically.
|
|
978
|
+
+--> NO: Follow the decision tree above.
|
|
979
|
+
```
|
|
980
|
+
|
|
981
|
+
### "Should I Use React.memo?" Decision Tree
|
|
982
|
+
|
|
983
|
+
```
|
|
984
|
+
Does this component re-render frequently with the same props?
|
|
985
|
+
|
|
|
986
|
+
+--> YES: Does it take > 2ms to render (check React Profiler)?
|
|
987
|
+
| |
|
|
988
|
+
| +--> YES: Wrap in React.memo.
|
|
989
|
+
| | Ensure all props are stable (no inline objects/functions).
|
|
990
|
+
| | If passing callbacks, wrap them in useCallback.
|
|
991
|
+
| |
|
|
992
|
+
| +--> NO: Is it rendered in a list of 50+ items?
|
|
993
|
+
| +--> YES: Wrap in React.memo (aggregate savings matter).
|
|
994
|
+
| +--> NO: Probably skip. Profile to confirm.
|
|
995
|
+
|
|
|
996
|
+
+--> NO: Does it receive new props on almost every render?
|
|
997
|
+
+--> YES: Do NOT use React.memo (comparison cost without savings).
|
|
998
|
+
+--> NO: Consider using React.memo if the subtree is large.
|
|
999
|
+
```
|
|
1000
|
+
|
|
1001
|
+
---
|
|
1002
|
+
|
|
1003
|
+
## 11. Code Examples -- Before/After with Measurements
|
|
1004
|
+
|
|
1005
|
+
### Example 1: Context Splitting
|
|
1006
|
+
|
|
1007
|
+
```jsx
|
|
1008
|
+
// BEFORE: Single context, all consumers re-render on any change
|
|
1009
|
+
const AppContext = React.createContext();
|
|
1010
|
+
|
|
1011
|
+
function AppProvider({ children }) {
|
|
1012
|
+
const [user, setUser] = useState(null);
|
|
1013
|
+
const [theme, setTheme] = useState('light');
|
|
1014
|
+
const [cart, setCart] = useState([]);
|
|
1015
|
+
const [notifications, setNotifications] = useState([]);
|
|
1016
|
+
|
|
1017
|
+
return (
|
|
1018
|
+
<AppContext.Provider value={{ user, theme, cart, notifications, setUser, setTheme, setCart, setNotifications }}>
|
|
1019
|
+
{children}
|
|
1020
|
+
</AppContext.Provider>
|
|
1021
|
+
);
|
|
1022
|
+
}
|
|
1023
|
+
// Problem: Adding to cart re-renders every component using theme, user, or notifications
|
|
1024
|
+
// Measured: 847ms total re-render time for cart update (120 consumer components)
|
|
1025
|
+
|
|
1026
|
+
// AFTER: Split contexts by update frequency
|
|
1027
|
+
const UserContext = React.createContext();
|
|
1028
|
+
const ThemeContext = React.createContext();
|
|
1029
|
+
const CartContext = React.createContext();
|
|
1030
|
+
|
|
1031
|
+
function AppProviders({ children }) {
|
|
1032
|
+
return (
|
|
1033
|
+
<UserProvider>
|
|
1034
|
+
<ThemeProvider>
|
|
1035
|
+
<CartProvider>
|
|
1036
|
+
{children}
|
|
1037
|
+
</CartProvider>
|
|
1038
|
+
</ThemeProvider>
|
|
1039
|
+
</UserProvider>
|
|
1040
|
+
);
|
|
1041
|
+
}
|
|
1042
|
+
// Measured: Cart update re-renders only 8 cart consumers -- 23ms (96% reduction)
|
|
1043
|
+
```
|
|
1044
|
+
|
|
1045
|
+
### Example 2: Debounced Search with Transition
|
|
1046
|
+
|
|
1047
|
+
```jsx
|
|
1048
|
+
// BEFORE: Each keystroke filters 50,000 products synchronously
|
|
1049
|
+
function ProductSearch({ products }) {
|
|
1050
|
+
const [query, setQuery] = useState('');
|
|
1051
|
+
const filtered = products.filter(p =>
|
|
1052
|
+
p.name.toLowerCase().includes(query.toLowerCase())
|
|
1053
|
+
);
|
|
1054
|
+
return (
|
|
1055
|
+
<>
|
|
1056
|
+
<input value={query} onChange={e => setQuery(e.target.value)} />
|
|
1057
|
+
<ProductGrid products={filtered} />
|
|
1058
|
+
</>
|
|
1059
|
+
);
|
|
1060
|
+
}
|
|
1061
|
+
// Measured: 280ms input lag per keystroke, INP = 350ms
|
|
1062
|
+
|
|
1063
|
+
// AFTER: Immediate input response + deferred filtering
|
|
1064
|
+
function ProductSearch({ products }) {
|
|
1065
|
+
const [query, setQuery] = useState('');
|
|
1066
|
+
const deferredQuery = useDeferredValue(query);
|
|
1067
|
+
|
|
1068
|
+
const filtered = useMemo(
|
|
1069
|
+
() => products.filter(p =>
|
|
1070
|
+
p.name.toLowerCase().includes(deferredQuery.toLowerCase())
|
|
1071
|
+
),
|
|
1072
|
+
[products, deferredQuery]
|
|
1073
|
+
);
|
|
1074
|
+
|
|
1075
|
+
return (
|
|
1076
|
+
<>
|
|
1077
|
+
<input value={query} onChange={e => setQuery(e.target.value)} />
|
|
1078
|
+
<ProductGrid products={filtered} />
|
|
1079
|
+
</>
|
|
1080
|
+
);
|
|
1081
|
+
}
|
|
1082
|
+
// Measured: <16ms input response, list updates within 100ms, INP = 45ms
|
|
1083
|
+
```
|
|
1084
|
+
|
|
1085
|
+
### Example 3: Dynamic Import for Heavy Component
|
|
1086
|
+
|
|
1087
|
+
```jsx
|
|
1088
|
+
// BEFORE: Chart library loaded for all users (adds 180 KB to bundle)
|
|
1089
|
+
import { AreaChart, BarChart, LineChart } from 'recharts';
|
|
1090
|
+
|
|
1091
|
+
function AnalyticsDashboard({ data, chartType }) {
|
|
1092
|
+
const Chart = chartType === 'bar' ? BarChart : chartType === 'line' ? LineChart : AreaChart;
|
|
1093
|
+
return <Chart data={data} />;
|
|
1094
|
+
}
|
|
1095
|
+
// Initial bundle: 420 KB gzipped, TTI: 4.8s
|
|
1096
|
+
|
|
1097
|
+
// AFTER: Chart library loaded only when analytics page is visited
|
|
1098
|
+
const AnalyticsDashboard = React.lazy(() => import('./AnalyticsDashboard'));
|
|
1099
|
+
|
|
1100
|
+
function App() {
|
|
1101
|
+
return (
|
|
1102
|
+
<Suspense fallback={<DashboardSkeleton />}>
|
|
1103
|
+
<AnalyticsDashboard data={data} chartType={chartType} />
|
|
1104
|
+
</Suspense>
|
|
1105
|
+
);
|
|
1106
|
+
}
|
|
1107
|
+
// Initial bundle: 240 KB gzipped (-43%), TTI: 2.6s (-46%)
|
|
1108
|
+
```
|
|
1109
|
+
|
|
1110
|
+
### Example 4: Memoized List with Stable Callbacks
|
|
1111
|
+
|
|
1112
|
+
```jsx
|
|
1113
|
+
// BEFORE: Every filter change re-renders all 500 rows (500 x 4ms = 2000ms)
|
|
1114
|
+
function DataTable({ rows, onRowSelect }) {
|
|
1115
|
+
const [filter, setFilter] = useState('');
|
|
1116
|
+
const filtered = rows.filter(r => r.name.includes(filter));
|
|
1117
|
+
|
|
1118
|
+
return (
|
|
1119
|
+
<table>
|
|
1120
|
+
{filtered.map(row => (
|
|
1121
|
+
<TableRow
|
|
1122
|
+
key={row.id}
|
|
1123
|
+
row={row}
|
|
1124
|
+
onSelect={() => onRowSelect(row.id)} // New function every render
|
|
1125
|
+
/>
|
|
1126
|
+
))}
|
|
1127
|
+
</table>
|
|
1128
|
+
);
|
|
1129
|
+
}
|
|
1130
|
+
|
|
1131
|
+
// AFTER: Only changed rows re-render (typically 0-5 rows = 0-20ms)
|
|
1132
|
+
const MemoizedTableRow = React.memo(TableRow);
|
|
1133
|
+
|
|
1134
|
+
function DataTable({ rows, onRowSelect }) {
|
|
1135
|
+
const [filter, setFilter] = useState('');
|
|
1136
|
+
|
|
1137
|
+
const filtered = useMemo(
|
|
1138
|
+
() => rows.filter(r => r.name.includes(filter)),
|
|
1139
|
+
[rows, filter]
|
|
1140
|
+
);
|
|
1141
|
+
|
|
1142
|
+
const handleSelect = useCallback(
|
|
1143
|
+
(id) => onRowSelect(id),
|
|
1144
|
+
[onRowSelect]
|
|
1145
|
+
);
|
|
1146
|
+
|
|
1147
|
+
return (
|
|
1148
|
+
<table>
|
|
1149
|
+
{filtered.map(row => (
|
|
1150
|
+
<MemoizedTableRow
|
|
1151
|
+
key={row.id}
|
|
1152
|
+
row={row}
|
|
1153
|
+
onSelect={handleSelect}
|
|
1154
|
+
/>
|
|
1155
|
+
))}
|
|
1156
|
+
</table>
|
|
1157
|
+
);
|
|
1158
|
+
}
|
|
1159
|
+
// Measured: Filter interaction drops from 2000ms to <20ms (100x improvement)
|
|
1160
|
+
```
|
|
1161
|
+
|
|
1162
|
+
### Example 5: Image Optimization with Next.js
|
|
1163
|
+
|
|
1164
|
+
```jsx
|
|
1165
|
+
// BEFORE: Unoptimized images (2.4 MB total, LCP: 4.1s)
|
|
1166
|
+
function ProductGallery({ images }) {
|
|
1167
|
+
return (
|
|
1168
|
+
<div>
|
|
1169
|
+
{images.map(img => (
|
|
1170
|
+
<img key={img.id} src={img.url} />
|
|
1171
|
+
))}
|
|
1172
|
+
</div>
|
|
1173
|
+
);
|
|
1174
|
+
}
|
|
1175
|
+
|
|
1176
|
+
// AFTER: Optimized with lazy loading, sizing, and modern formats (320 KB total, LCP: 1.8s)
|
|
1177
|
+
import Image from 'next/image';
|
|
1178
|
+
|
|
1179
|
+
function ProductGallery({ images }) {
|
|
1180
|
+
return (
|
|
1181
|
+
<div>
|
|
1182
|
+
{images.map((img, index) => (
|
|
1183
|
+
<Image
|
|
1184
|
+
key={img.id}
|
|
1185
|
+
src={img.url}
|
|
1186
|
+
width={400}
|
|
1187
|
+
height={300}
|
|
1188
|
+
alt={img.alt}
|
|
1189
|
+
loading={index === 0 ? 'eager' : 'lazy'}
|
|
1190
|
+
sizes="(max-width: 768px) 100vw, 400px"
|
|
1191
|
+
placeholder="blur"
|
|
1192
|
+
blurDataURL={img.blurHash}
|
|
1193
|
+
/>
|
|
1194
|
+
))}
|
|
1195
|
+
</div>
|
|
1196
|
+
);
|
|
1197
|
+
}
|
|
1198
|
+
// Measured: Page weight from 2.4 MB to 320 KB (-87%), LCP from 4.1s to 1.8s (-56%)
|
|
1199
|
+
```
|
|
1200
|
+
|
|
1201
|
+
### Example 6: Replacing Context with Zustand
|
|
1202
|
+
|
|
1203
|
+
```jsx
|
|
1204
|
+
// BEFORE: Context causes 120 re-renders on cart update
|
|
1205
|
+
const CartContext = React.createContext();
|
|
1206
|
+
|
|
1207
|
+
function CartProvider({ children }) {
|
|
1208
|
+
const [items, setItems] = useState([]);
|
|
1209
|
+
const addItem = (item) => setItems(prev => [...prev, item]);
|
|
1210
|
+
const total = items.reduce((sum, i) => sum + i.price, 0);
|
|
1211
|
+
return (
|
|
1212
|
+
<CartContext.Provider value={{ items, addItem, total }}>
|
|
1213
|
+
{children}
|
|
1214
|
+
</CartContext.Provider>
|
|
1215
|
+
);
|
|
1216
|
+
}
|
|
1217
|
+
// Every component using CartContext re-renders when items change
|
|
1218
|
+
|
|
1219
|
+
// AFTER: Only cart-display components re-render
|
|
1220
|
+
import { create } from 'zustand';
|
|
1221
|
+
|
|
1222
|
+
const useCartStore = create((set, get) => ({
|
|
1223
|
+
items: [],
|
|
1224
|
+
addItem: (item) => set(state => ({ items: [...state.items, item] })),
|
|
1225
|
+
total: () => get().items.reduce((sum, i) => sum + i.price, 0),
|
|
1226
|
+
}));
|
|
1227
|
+
|
|
1228
|
+
// In components -- subscribe to only what you need
|
|
1229
|
+
function CartCount() {
|
|
1230
|
+
const count = useCartStore(state => state.items.length); // Only re-renders when count changes
|
|
1231
|
+
return <span>{count}</span>;
|
|
1232
|
+
}
|
|
1233
|
+
|
|
1234
|
+
function CartTotal() {
|
|
1235
|
+
const total = useCartStore(state => state.total()); // Only re-renders when total changes
|
|
1236
|
+
return <span>${total}</span>;
|
|
1237
|
+
}
|
|
1238
|
+
// Measured: Cart update re-renders drop from 120 components to 3 components (96% reduction)
|
|
1239
|
+
```
|
|
1240
|
+
|
|
1241
|
+
### Example 7: Server Component Data Fetching
|
|
1242
|
+
|
|
1243
|
+
```jsx
|
|
1244
|
+
// BEFORE: Client-side data fetching with loading states (TTI: 3.8s)
|
|
1245
|
+
'use client';
|
|
1246
|
+
function BlogPost({ postId }) {
|
|
1247
|
+
const [post, setPost] = useState(null);
|
|
1248
|
+
const [comments, setComments] = useState([]);
|
|
1249
|
+
const [loading, setLoading] = useState(true);
|
|
1250
|
+
|
|
1251
|
+
useEffect(() => {
|
|
1252
|
+
Promise.all([
|
|
1253
|
+
fetch(`/api/posts/${postId}`).then(r => r.json()),
|
|
1254
|
+
fetch(`/api/posts/${postId}/comments`).then(r => r.json()),
|
|
1255
|
+
]).then(([postData, commentsData]) => {
|
|
1256
|
+
setPost(postData);
|
|
1257
|
+
setComments(commentsData);
|
|
1258
|
+
setLoading(false);
|
|
1259
|
+
});
|
|
1260
|
+
}, [postId]);
|
|
1261
|
+
|
|
1262
|
+
if (loading) return <Skeleton />;
|
|
1263
|
+
return (
|
|
1264
|
+
<article>
|
|
1265
|
+
<h1>{post.title}</h1>
|
|
1266
|
+
<div>{post.content}</div>
|
|
1267
|
+
<CommentList comments={comments} />
|
|
1268
|
+
</article>
|
|
1269
|
+
);
|
|
1270
|
+
}
|
|
1271
|
+
|
|
1272
|
+
// AFTER: Server Component with zero client JS for data display (TTI: 1.2s)
|
|
1273
|
+
async function BlogPost({ postId }) {
|
|
1274
|
+
const [post, comments] = await Promise.all([
|
|
1275
|
+
db.posts.findById(postId),
|
|
1276
|
+
db.comments.findByPostId(postId),
|
|
1277
|
+
]);
|
|
1278
|
+
|
|
1279
|
+
return (
|
|
1280
|
+
<article>
|
|
1281
|
+
<h1>{post.title}</h1>
|
|
1282
|
+
<div>{post.content}</div>
|
|
1283
|
+
<Suspense fallback={<CommentsSkeleton />}>
|
|
1284
|
+
<CommentList comments={comments} />
|
|
1285
|
+
</Suspense>
|
|
1286
|
+
<AddCommentForm postId={postId} /> {/* 'use client' component */}
|
|
1287
|
+
</article>
|
|
1288
|
+
);
|
|
1289
|
+
}
|
|
1290
|
+
// Measured: JS bundle for page: -55%, TTI from 3.8s to 1.2s (-68%)
|
|
1291
|
+
```
|
|
1292
|
+
|
|
1293
|
+
### Example 8: Virtualized Data Grid
|
|
1294
|
+
|
|
1295
|
+
```jsx
|
|
1296
|
+
// BEFORE: Renders 5,000 rows with 10 columns = 50,000 cells (~4.5s initial render)
|
|
1297
|
+
function DataGrid({ data, columns }) {
|
|
1298
|
+
return (
|
|
1299
|
+
<table>
|
|
1300
|
+
<thead>
|
|
1301
|
+
<tr>{columns.map(col => <th key={col.key}>{col.label}</th>)}</tr>
|
|
1302
|
+
</thead>
|
|
1303
|
+
<tbody>
|
|
1304
|
+
{data.map(row => (
|
|
1305
|
+
<tr key={row.id}>
|
|
1306
|
+
{columns.map(col => <td key={col.key}>{row[col.key]}</td>)}
|
|
1307
|
+
</tr>
|
|
1308
|
+
))}
|
|
1309
|
+
</tbody>
|
|
1310
|
+
</table>
|
|
1311
|
+
);
|
|
1312
|
+
}
|
|
1313
|
+
|
|
1314
|
+
// AFTER: Only visible rows rendered (~25ms initial render)
|
|
1315
|
+
import { useVirtualizer } from '@tanstack/react-virtual';
|
|
1316
|
+
|
|
1317
|
+
function DataGrid({ data, columns }) {
|
|
1318
|
+
const parentRef = useRef(null);
|
|
1319
|
+
|
|
1320
|
+
const rowVirtualizer = useVirtualizer({
|
|
1321
|
+
count: data.length,
|
|
1322
|
+
getScrollElement: () => parentRef.current,
|
|
1323
|
+
estimateSize: () => 40,
|
|
1324
|
+
overscan: 5,
|
|
1325
|
+
});
|
|
1326
|
+
|
|
1327
|
+
return (
|
|
1328
|
+
<div ref={parentRef} style={{ height: '600px', overflow: 'auto' }}>
|
|
1329
|
+
<table>
|
|
1330
|
+
<thead>
|
|
1331
|
+
<tr>{columns.map(col => <th key={col.key}>{col.label}</th>)}</tr>
|
|
1332
|
+
</thead>
|
|
1333
|
+
<tbody style={{ height: `${rowVirtualizer.getTotalSize()}px`, position: 'relative' }}>
|
|
1334
|
+
{rowVirtualizer.getVirtualItems().map(virtualRow => {
|
|
1335
|
+
const row = data[virtualRow.index];
|
|
1336
|
+
return (
|
|
1337
|
+
<tr
|
|
1338
|
+
key={row.id}
|
|
1339
|
+
style={{
|
|
1340
|
+
position: 'absolute',
|
|
1341
|
+
top: 0,
|
|
1342
|
+
transform: `translateY(${virtualRow.start}px)`,
|
|
1343
|
+
height: `${virtualRow.size}px`,
|
|
1344
|
+
}}
|
|
1345
|
+
>
|
|
1346
|
+
{columns.map(col => <td key={col.key}>{row[col.key]}</td>)}
|
|
1347
|
+
</tr>
|
|
1348
|
+
);
|
|
1349
|
+
})}
|
|
1350
|
+
</tbody>
|
|
1351
|
+
</table>
|
|
1352
|
+
</div>
|
|
1353
|
+
);
|
|
1354
|
+
}
|
|
1355
|
+
// Measured: Initial render from 4500ms to 25ms (180x faster), DOM nodes from 50,000 to ~300
|
|
1356
|
+
```
|
|
1357
|
+
|
|
1358
|
+
---
|
|
1359
|
+
|
|
1360
|
+
## 12. Quick Reference
|
|
1361
|
+
|
|
1362
|
+
| Problem | Solution | Expected Improvement |
|
|
1363
|
+
|---|---|---|
|
|
1364
|
+
| Slow initial load | Code split with React.lazy at routes | 40-60% bundle reduction |
|
|
1365
|
+
| Unnecessary child re-renders | React.memo + useCallback for callbacks | 90-99% fewer re-renders in lists |
|
|
1366
|
+
| Expensive computation each render | useMemo with proper deps | Avoid 5-15ms per wasted render |
|
|
1367
|
+
| Long list (1,000+ items) | Virtualize with react-window or @tanstack/react-virtual | 100-200x faster render |
|
|
1368
|
+
| Input lag during filtering | useTransition or useDeferredValue | Input latency < 50ms |
|
|
1369
|
+
| Context causing mass re-renders | Split contexts or switch to Zustand/Jotai | 90-96% fewer re-renders |
|
|
1370
|
+
| Large data-display pages | React Server Components | 40-60% less client JS |
|
|
1371
|
+
| Slow SSR TTFB | Streaming SSR with Suspense | TTFB from 350-550ms to 40-90ms |
|
|
1372
|
+
| Heavy hydration blocking TTI | Selective/progressive hydration | 40-45% TTI improvement |
|
|
1373
|
+
| Manual memo boilerplate | React Compiler (v1.0) | Automatic, 12% faster loads |
|
|
1374
|
+
| Large images hurting LCP | next/image with lazy loading + sizing | 50-87% page weight reduction |
|
|
1375
|
+
| Unoptimized bundle composition | Tree-shaking + import analysis | 20-50 KB per library saved |
|
|
1376
|
+
| useEffect cascading state updates | Derive values during render | Eliminate 1-3 extra render cycles |
|
|
1377
|
+
| Layout thrashing | Batch DOM reads then writes | 10-40x faster DOM operations |
|
|
1378
|
+
| Third-party script bloat | Defer/async loading, budget < 50 KB | Unblock main thread during load |
|
|
1379
|
+
|
|
1380
|
+
---
|
|
1381
|
+
|
|
1382
|
+
## Sources
|
|
1383
|
+
|
|
1384
|
+
- [React Performance Optimization: 15 Best Practices for 2025](https://dev.to/alex_bobes/react-performance-optimization-15-best-practices-for-2025-17l9)
|
|
1385
|
+
- [Understanding useMemo and useCallback -- Josh W. Comeau](https://www.joshwcomeau.com/react/usememo-and-usecallback/)
|
|
1386
|
+
- [When to useMemo and useCallback -- Kent C. Dodds](https://kentcdodds.com/blog/usememo-and-usecallback)
|
|
1387
|
+
- [React Server Components: Do They Really Improve Performance? -- Nadia Makarevich](https://www.developerway.com/posts/react-server-components-performance)
|
|
1388
|
+
- [The Hidden Performance Costs of React Server Components](https://dev.to/rbobr/the-hidden-performance-costs-of-react-server-components-248f)
|
|
1389
|
+
- [React useTransition: Performance Game Changer or...?](https://www.developerway.com/posts/use-transition)
|
|
1390
|
+
- [React Concurrency, Explained -- 3perf](https://3perf.com/talks/react-concurrency/)
|
|
1391
|
+
- [How React 18 Improves Application Performance -- Vercel](https://vercel.com/blog/how-react-18-improves-application-performance)
|
|
1392
|
+
- [Virtualize Large Lists with react-window -- web.dev](https://web.dev/virtualize-long-lists-react-window/)
|
|
1393
|
+
- [React Compiler v1.0 -- React Blog](https://react.dev/blog/2025/10/07/react-compiler-1)
|
|
1394
|
+
- [React Compiler Deep Dive: Automatic Memoization](https://dev.to/pockit_tools/react-compiler-deep-dive-how-automatic-memoization-eliminates-90-of-performance-optimization-work-1351)
|
|
1395
|
+
- [40% Faster Interaction: How Wix Solved React's Hydration Problem](https://www.wix.engineering/post/40-faster-interaction-how-wix-solved-react-s-hydration-problem-with-selective-hydration-and-suspen)
|
|
1396
|
+
- [How to Measure and Monitor React Render Performance -- 3perf](https://3perf.com/blog/react-monitoring/)
|
|
1397
|
+
- [You Might Not Need an Effect -- React Docs](https://react.dev/learn/you-might-not-need-an-effect)
|
|
1398
|
+
- [React Server Components Streaming Performance Guide 2026](https://www.sitepoint.com/react-server-components-streaming-performance-2026/)
|
|
1399
|
+
- [Lighthouse CI -- GoogleChrome](https://github.com/GoogleChrome/lighthouse-ci)
|
|
1400
|
+
- [Debugging React Performance Issues with Why Did You Render -- LogRocket](https://blog.logrocket.com/debugging-react-performance-issues-with-why-did-you-render/)
|
|
1401
|
+
- [Fixing useContext Performance Issues](https://dev.to/jherr/fixing-usecontext-performance-issues-60h)
|
|
1402
|
+
- [Introducing React Best Practices -- Vercel](https://vercel.com/blog/introducing-react-best-practices)
|
|
1403
|
+
- [Optimizing Bundle Sizes in React Applications -- Coditation](https://www.coditation.com/blog/optimizing-bundle-sizes-in-react-applications-a-deep-dive-into-code-splitting-and-lazy-loading)
|