@ivannxbt/ai-config 0.1.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/AGENTS.md +49 -0
- package/.agents/skills/ab-test-setup/SKILL.md +266 -0
- package/.agents/skills/ab-test-setup/references/sample-size-guide.md +263 -0
- package/.agents/skills/ab-test-setup/references/test-templates.md +277 -0
- package/.agents/skills/ad-creative/SKILL.md +362 -0
- package/.agents/skills/ad-creative/references/generative-tools.md +637 -0
- package/.agents/skills/ad-creative/references/platform-specs.md +213 -0
- package/.agents/skills/agent-browser/SKILL.md +347 -0
- package/.agents/skills/agent-browser/references/authentication.md +188 -0
- package/.agents/skills/agent-browser/references/proxy-support.md +175 -0
- package/.agents/skills/agent-browser/references/session-management.md +181 -0
- package/.agents/skills/agent-browser/references/snapshot-refs.md +186 -0
- package/.agents/skills/agent-browser/references/video-recording.md +162 -0
- package/.agents/skills/agent-browser/templates/authenticated-session.sh +91 -0
- package/.agents/skills/agent-browser/templates/capture-workflow.sh +68 -0
- package/.agents/skills/agent-browser/templates/form-automation.sh +64 -0
- package/.agents/skills/ai-seo/SKILL.md +398 -0
- package/.agents/skills/ai-seo/references/content-patterns.md +285 -0
- package/.agents/skills/ai-seo/references/platform-ranking-factors.md +152 -0
- package/.agents/skills/analytics-tracking/SKILL.md +309 -0
- package/.agents/skills/analytics-tracking/references/event-library.md +260 -0
- package/.agents/skills/analytics-tracking/references/ga4-implementation.md +300 -0
- package/.agents/skills/analytics-tracking/references/gtm-implementation.md +390 -0
- package/.agents/skills/appinsights-instrumentation/LICENSE.txt +21 -0
- package/.agents/skills/appinsights-instrumentation/SKILL.md +72 -0
- package/.agents/skills/appinsights-instrumentation/examples/appinsights.bicep +30 -0
- package/.agents/skills/appinsights-instrumentation/references/aspnetcore.md +29 -0
- package/.agents/skills/appinsights-instrumentation/references/auto.md +13 -0
- package/.agents/skills/appinsights-instrumentation/references/nodejs.md +28 -0
- package/.agents/skills/appinsights-instrumentation/references/python.md +48 -0
- package/.agents/skills/appinsights-instrumentation/references/sdk/azure-monitor-opentelemetry-exporter-java.md +31 -0
- package/.agents/skills/appinsights-instrumentation/references/sdk/azure-monitor-opentelemetry-exporter-py.md +22 -0
- package/.agents/skills/appinsights-instrumentation/references/sdk/azure-monitor-opentelemetry-py.md +23 -0
- package/.agents/skills/appinsights-instrumentation/references/sdk/azure-monitor-opentelemetry-ts.md +26 -0
- package/.agents/skills/appinsights-instrumentation/scripts/appinsights.ps1 +20 -0
- package/.agents/skills/audit-website/README.md +20 -0
- package/.agents/skills/audit-website/SKILL.md +470 -0
- package/.agents/skills/audit-website/agents/openai.yaml +6 -0
- package/.agents/skills/audit-website/assets/icon-large.png +0 -0
- package/.agents/skills/audit-website/assets/icon-small.svg +41 -0
- package/.agents/skills/audit-website/references/OUTPUT-FORMAT.md +250 -0
- package/.agents/skills/better-auth/SKILL.md +204 -0
- package/.agents/skills/better-auth/references/advanced-features.md +553 -0
- package/.agents/skills/better-auth/references/database-integration.md +577 -0
- package/.agents/skills/better-auth/references/email-password-auth.md +416 -0
- package/.agents/skills/better-auth/references/oauth-providers.md +430 -0
- package/.agents/skills/better-auth/scripts/.coverage +0 -0
- package/.agents/skills/better-auth/scripts/better_auth_init.py +521 -0
- package/.agents/skills/better-auth/scripts/requirements.txt +15 -0
- package/.agents/skills/better-auth/scripts/tests/.coverage +0 -0
- package/.agents/skills/better-auth/scripts/tests/test_better_auth_init.py +421 -0
- package/.agents/skills/better-auth-best-practices/SKILL.md +166 -0
- package/.agents/skills/building-native-ui/SKILL.md +321 -0
- package/.agents/skills/building-native-ui/references/animations.md +220 -0
- package/.agents/skills/building-native-ui/references/controls.md +270 -0
- package/.agents/skills/building-native-ui/references/form-sheet.md +227 -0
- package/.agents/skills/building-native-ui/references/gradients.md +106 -0
- package/.agents/skills/building-native-ui/references/icons.md +213 -0
- package/.agents/skills/building-native-ui/references/media.md +198 -0
- package/.agents/skills/building-native-ui/references/route-structure.md +229 -0
- package/.agents/skills/building-native-ui/references/search.md +248 -0
- package/.agents/skills/building-native-ui/references/storage.md +121 -0
- package/.agents/skills/building-native-ui/references/tabs.md +433 -0
- package/.agents/skills/building-native-ui/references/toolbar-and-headers.md +284 -0
- package/.agents/skills/building-native-ui/references/visual-effects.md +197 -0
- package/.agents/skills/building-native-ui/references/webgpu-three.md +605 -0
- package/.agents/skills/building-native-ui/references/zoom-transitions.md +158 -0
- package/.agents/skills/churn-prevention/SKILL.md +424 -0
- package/.agents/skills/churn-prevention/references/cancel-flow-patterns.md +316 -0
- package/.agents/skills/churn-prevention/references/dunning-playbook.md +408 -0
- package/.agents/skills/cold-email/SKILL.md +158 -0
- package/.agents/skills/cold-email/references/benchmarks.md +83 -0
- package/.agents/skills/cold-email/references/follow-up-sequences.md +81 -0
- package/.agents/skills/cold-email/references/frameworks.md +90 -0
- package/.agents/skills/cold-email/references/personalization.md +79 -0
- package/.agents/skills/cold-email/references/subject-lines.md +53 -0
- package/.agents/skills/competitor-alternatives/SKILL.md +256 -0
- package/.agents/skills/competitor-alternatives/references/content-architecture.md +271 -0
- package/.agents/skills/competitor-alternatives/references/templates.md +223 -0
- package/.agents/skills/content-strategy/SKILL.md +359 -0
- package/.agents/skills/copy-editing/SKILL.md +447 -0
- package/.agents/skills/copy-editing/references/plain-english-alternatives.md +394 -0
- package/.agents/skills/copywriting/SKILL.md +252 -0
- package/.agents/skills/copywriting/references/copy-frameworks.md +344 -0
- package/.agents/skills/copywriting/references/natural-transitions.md +272 -0
- package/.agents/skills/deep-agents-core/SKILL.md +423 -0
- package/.agents/skills/deep-agents-memory/SKILL.md +301 -0
- package/.agents/skills/deep-agents-orchestration/SKILL.md +471 -0
- package/.agents/skills/design-md/SKILL.md +172 -0
- package/.agents/skills/design-md/examples/DESIGN.md +154 -0
- package/.agents/skills/email-best-practices/README.md +63 -0
- package/.agents/skills/email-best-practices/SKILL.md +59 -0
- package/.agents/skills/email-best-practices/resources/compliance.md +125 -0
- package/.agents/skills/email-best-practices/resources/deliverability.md +121 -0
- package/.agents/skills/email-best-practices/resources/email-capture.md +129 -0
- package/.agents/skills/email-best-practices/resources/email-types.md +173 -0
- package/.agents/skills/email-best-practices/resources/list-management.md +157 -0
- package/.agents/skills/email-best-practices/resources/marketing-emails.md +115 -0
- package/.agents/skills/email-best-practices/resources/sending-reliability.md +155 -0
- package/.agents/skills/email-best-practices/resources/transactional-email-catalog.md +418 -0
- package/.agents/skills/email-best-practices/resources/transactional-emails.md +92 -0
- package/.agents/skills/email-best-practices/resources/webhooks-events.md +167 -0
- package/.agents/skills/email-best-practices/tests/README.md +35 -0
- package/.agents/skills/email-best-practices/tests/scenarios/01-spam-deliverability.md +46 -0
- package/.agents/skills/email-best-practices/tests/scenarios/02-multi-region-compliance.md +48 -0
- package/.agents/skills/email-best-practices/tests/scenarios/03-retry-idempotency.md +36 -0
- package/.agents/skills/email-best-practices/tests/scenarios/04-webhook-bounce-handling.md +52 -0
- package/.agents/skills/email-best-practices/tests/scenarios/05-new-saas-email-plan.md +51 -0
- package/.agents/skills/email-sequence/SKILL.md +309 -0
- package/.agents/skills/email-sequence/references/copy-guidelines.md +113 -0
- package/.agents/skills/email-sequence/references/email-types.md +515 -0
- package/.agents/skills/email-sequence/references/sequence-templates.md +168 -0
- package/.agents/skills/entra-app-registration/SKILL.md +191 -0
- package/.agents/skills/entra-app-registration/references/BICEP-EXAMPLE.bicep +199 -0
- package/.agents/skills/entra-app-registration/references/api-permissions.md +341 -0
- package/.agents/skills/entra-app-registration/references/auth-best-practices.md +128 -0
- package/.agents/skills/entra-app-registration/references/cli-commands.md +409 -0
- package/.agents/skills/entra-app-registration/references/console-app-example.md +392 -0
- package/.agents/skills/entra-app-registration/references/first-app-registration.md +243 -0
- package/.agents/skills/entra-app-registration/references/oauth-flows.md +398 -0
- package/.agents/skills/entra-app-registration/references/sdk/azure-identity-dotnet.md +22 -0
- package/.agents/skills/entra-app-registration/references/sdk/azure-identity-java.md +30 -0
- package/.agents/skills/entra-app-registration/references/sdk/azure-identity-py.md +27 -0
- package/.agents/skills/entra-app-registration/references/sdk/azure-identity-rust.md +21 -0
- package/.agents/skills/entra-app-registration/references/sdk/azure-identity-ts.md +24 -0
- package/.agents/skills/entra-app-registration/references/sdk/azure-keyvault-py.md +25 -0
- package/.agents/skills/entra-app-registration/references/sdk/azure-keyvault-secrets-ts.md +23 -0
- package/.agents/skills/entra-app-registration/references/sdk/microsoft-azure-webjobs-extensions-authentication-events-dotnet.md +37 -0
- package/.agents/skills/entra-app-registration/references/troubleshooting.md +269 -0
- package/.agents/skills/expo-api-routes/SKILL.md +368 -0
- package/.agents/skills/expo-cicd-workflows/SKILL.md +92 -0
- package/.agents/skills/expo-cicd-workflows/scripts/fetch.js +109 -0
- package/.agents/skills/expo-cicd-workflows/scripts/package.json +11 -0
- package/.agents/skills/expo-cicd-workflows/scripts/validate.js +84 -0
- package/.agents/skills/expo-deployment/SKILL.md +190 -0
- package/.agents/skills/expo-deployment/references/app-store-metadata.md +479 -0
- package/.agents/skills/expo-deployment/references/ios-app-store.md +355 -0
- package/.agents/skills/expo-deployment/references/play-store.md +246 -0
- package/.agents/skills/expo-deployment/references/testflight.md +58 -0
- package/.agents/skills/expo-deployment/references/workflows.md +200 -0
- package/.agents/skills/expo-dev-client/SKILL.md +164 -0
- package/.agents/skills/expo-react-native-performance/AGENTS.md +2515 -0
- package/.agents/skills/expo-react-native-performance/SKILL.md +118 -0
- package/.agents/skills/expo-react-native-performance/references/anim-interaction-manager.md +63 -0
- package/.agents/skills/expo-react-native-performance/references/anim-layout-animation.md +75 -0
- package/.agents/skills/expo-react-native-performance/references/anim-transform-not-dimensions.md +67 -0
- package/.agents/skills/expo-react-native-performance/references/anim-use-native-driver.md +55 -0
- package/.agents/skills/expo-react-native-performance/references/anim-use-reanimated.md +53 -0
- package/.agents/skills/expo-react-native-performance/references/asset-optimize-image-size.md +60 -0
- package/.agents/skills/expo-react-native-performance/references/asset-prefetch-images.md +60 -0
- package/.agents/skills/expo-react-native-performance/references/asset-recycling-key.md +57 -0
- package/.agents/skills/expo-react-native-performance/references/asset-use-expo-image.md +58 -0
- package/.agents/skills/expo-react-native-performance/references/asset-use-webp-format.md +50 -0
- package/.agents/skills/expo-react-native-performance/references/async-batch-api-calls.md +65 -0
- package/.agents/skills/expo-react-native-performance/references/async-cache-responses.md +54 -0
- package/.agents/skills/expo-react-native-performance/references/async-defer-await.md +56 -0
- package/.agents/skills/expo-react-native-performance/references/async-parallel-fetching.md +60 -0
- package/.agents/skills/expo-react-native-performance/references/async-refetch-on-focus.md +68 -0
- package/.agents/skills/expo-react-native-performance/references/list-estimated-item-size.md +52 -0
- package/.agents/skills/expo-react-native-performance/references/list-get-item-layout.md +64 -0
- package/.agents/skills/expo-react-native-performance/references/list-get-item-type.md +59 -0
- package/.agents/skills/expo-react-native-performance/references/list-memoize-items.md +59 -0
- package/.agents/skills/expo-react-native-performance/references/list-stable-render-item.md +61 -0
- package/.agents/skills/expo-react-native-performance/references/list-use-flashlist.md +56 -0
- package/.agents/skills/expo-react-native-performance/references/mem-abort-fetch.md +78 -0
- package/.agents/skills/expo-react-native-performance/references/mem-avoid-inline-objects.md +61 -0
- package/.agents/skills/expo-react-native-performance/references/mem-cleanup-subscriptions.md +63 -0
- package/.agents/skills/expo-react-native-performance/references/mem-clear-timers.md +63 -0
- package/.agents/skills/expo-react-native-performance/references/mem-limit-list-data.md +69 -0
- package/.agents/skills/expo-react-native-performance/references/platform-android-overdraw.md +63 -0
- package/.agents/skills/expo-react-native-performance/references/platform-android-proguard.md +63 -0
- package/.agents/skills/expo-react-native-performance/references/platform-conditional-render.md +81 -0
- package/.agents/skills/expo-react-native-performance/references/platform-ios-text-rendering.md +57 -0
- package/.agents/skills/expo-react-native-performance/references/rerender-derive-state.md +58 -0
- package/.agents/skills/expo-react-native-performance/references/rerender-functional-setstate.md +54 -0
- package/.agents/skills/expo-react-native-performance/references/rerender-lazy-state-init.md +55 -0
- package/.agents/skills/expo-react-native-performance/references/rerender-split-context.md +69 -0
- package/.agents/skills/expo-react-native-performance/references/rerender-use-callback-handlers.md +63 -0
- package/.agents/skills/expo-react-native-performance/references/rerender-use-memo-expensive.md +54 -0
- package/.agents/skills/expo-react-native-performance/references/startup-async-routes.md +63 -0
- package/.agents/skills/expo-react-native-performance/references/startup-cherry-pick-imports.md +45 -0
- package/.agents/skills/expo-react-native-performance/references/startup-enable-hermes.md +39 -0
- package/.agents/skills/expo-react-native-performance/references/startup-preload-assets.md +68 -0
- package/.agents/skills/expo-react-native-performance/references/startup-remove-console-logs.md +47 -0
- package/.agents/skills/expo-react-native-performance/references/startup-splash-screen-control.md +58 -0
- package/.agents/skills/expo-tailwind-setup/SKILL.md +480 -0
- package/.agents/skills/express-rest-api/SKILL.md +249 -0
- package/.agents/skills/express-rest-api/assets/config.yaml +1 -0
- package/.agents/skills/express-rest-api/references/GUIDE.md +1 -0
- package/.agents/skills/express-rest-api/scripts/helper.py +3 -0
- package/.agents/skills/find-skills/SKILL.md +133 -0
- package/.agents/skills/form-cro/SKILL.md +429 -0
- package/.agents/skills/framer-code-components-overrides/SKILL.md +297 -0
- package/.agents/skills/framer-code-components-overrides/references/patterns.md +338 -0
- package/.agents/skills/framer-code-components-overrides/references/property-controls.md +241 -0
- package/.agents/skills/framer-code-components-overrides/references/webgl-shaders.md +207 -0
- package/.agents/skills/framework-selection/SKILL.md +163 -0
- package/.agents/skills/free-tool-strategy/SKILL.md +178 -0
- package/.agents/skills/free-tool-strategy/references/tool-types.md +217 -0
- package/.agents/skills/frontend-design/LICENSE.txt +177 -0
- package/.agents/skills/frontend-design/SKILL.md +42 -0
- package/.agents/skills/gws-calendar/SKILL.md +108 -0
- package/.agents/skills/gws-calendar-agenda/SKILL.md +52 -0
- package/.agents/skills/gws-calendar-insert/SKILL.md +55 -0
- package/.agents/skills/gws-docs/SKILL.md +48 -0
- package/.agents/skills/gws-docs-write/SKILL.md +49 -0
- package/.agents/skills/gws-drive/SKILL.md +137 -0
- package/.agents/skills/gws-drive-upload/SKILL.md +52 -0
- package/.agents/skills/gws-gmail/SKILL.md +59 -0
- package/.agents/skills/gws-gmail-forward/SKILL.md +52 -0
- package/.agents/skills/gws-gmail-reply/SKILL.md +51 -0
- package/.agents/skills/gws-gmail-reply-all/SKILL.md +54 -0
- package/.agents/skills/gws-gmail-send/SKILL.md +52 -0
- package/.agents/skills/gws-gmail-triage/SKILL.md +50 -0
- package/.agents/skills/gws-gmail-watch/SKILL.md +58 -0
- package/.agents/skills/gws-meet/SKILL.md +51 -0
- package/.agents/skills/gws-shared/SKILL.md +66 -0
- package/.agents/skills/gws-sheets/SKILL.md +53 -0
- package/.agents/skills/gws-sheets-append/SKILL.md +51 -0
- package/.agents/skills/gws-sheets-read/SKILL.md +47 -0
- package/.agents/skills/gws-slides/SKILL.md +43 -0
- package/.agents/skills/gws-workflow/SKILL.md +44 -0
- package/.agents/skills/gws-workflow-email-to-task/SKILL.md +47 -0
- package/.agents/skills/gws-workflow-file-announce/SKILL.md +50 -0
- package/.agents/skills/gws-workflow-meeting-prep/SKILL.md +47 -0
- package/.agents/skills/gws-workflow-standup-report/SKILL.md +46 -0
- package/.agents/skills/gws-workflow-weekly-digest/SKILL.md +46 -0
- package/.agents/skills/langchain-dependencies/SKILL.md +419 -0
- package/.agents/skills/langchain-fundamentals/SKILL.md +392 -0
- package/.agents/skills/langchain-middleware/SKILL.md +302 -0
- package/.agents/skills/langchain-rag/SKILL.md +517 -0
- package/.agents/skills/langgraph-fundamentals/SKILL.md +811 -0
- package/.agents/skills/langgraph-human-in-the-loop/SKILL.md +532 -0
- package/.agents/skills/langgraph-persistence/SKILL.md +560 -0
- package/.agents/skills/launch-strategy/SKILL.md +353 -0
- package/.agents/skills/marketing-ideas/SKILL.md +167 -0
- package/.agents/skills/marketing-ideas/references/ideas-by-category.md +366 -0
- package/.agents/skills/marketing-psychology/SKILL.md +455 -0
- package/.agents/skills/microsoft-foundry/SKILL.md +102 -0
- package/.agents/skills/microsoft-foundry/foundry-agent/create/create-prompt.md +89 -0
- package/.agents/skills/microsoft-foundry/foundry-agent/create/create.md +239 -0
- package/.agents/skills/microsoft-foundry/foundry-agent/create/references/agent-tools.md +45 -0
- package/.agents/skills/microsoft-foundry/foundry-agent/create/references/agentframework.md +92 -0
- package/.agents/skills/microsoft-foundry/foundry-agent/create/references/sdk-operations.md +47 -0
- package/.agents/skills/microsoft-foundry/foundry-agent/create/references/tool-azure-ai-search.md +69 -0
- package/.agents/skills/microsoft-foundry/foundry-agent/create/references/tool-bing-grounding.md +50 -0
- package/.agents/skills/microsoft-foundry/foundry-agent/create/references/tool-file-search.md +60 -0
- package/.agents/skills/microsoft-foundry/foundry-agent/create/references/tool-mcp.md +66 -0
- package/.agents/skills/microsoft-foundry/foundry-agent/create/references/tool-memory.md +109 -0
- package/.agents/skills/microsoft-foundry/foundry-agent/create/references/tool-web-search.md +57 -0
- package/.agents/skills/microsoft-foundry/foundry-agent/deploy/deploy.md +319 -0
- package/.agents/skills/microsoft-foundry/foundry-agent/invoke/invoke.md +98 -0
- package/.agents/skills/microsoft-foundry/foundry-agent/observe/observe.md +51 -0
- package/.agents/skills/microsoft-foundry/foundry-agent/observe/references/analyze-results.md +47 -0
- package/.agents/skills/microsoft-foundry/foundry-agent/observe/references/cicd-monitoring.md +35 -0
- package/.agents/skills/microsoft-foundry/foundry-agent/observe/references/compare-iterate.md +48 -0
- package/.agents/skills/microsoft-foundry/foundry-agent/observe/references/deploy-and-setup.md +76 -0
- package/.agents/skills/microsoft-foundry/foundry-agent/observe/references/evaluate-step.md +51 -0
- package/.agents/skills/microsoft-foundry/foundry-agent/observe/references/optimize-deploy.md +30 -0
- package/.agents/skills/microsoft-foundry/foundry-agent/trace/references/analyze-failures.md +83 -0
- package/.agents/skills/microsoft-foundry/foundry-agent/trace/references/analyze-latency.md +90 -0
- package/.agents/skills/microsoft-foundry/foundry-agent/trace/references/conversation-detail.md +84 -0
- package/.agents/skills/microsoft-foundry/foundry-agent/trace/references/eval-correlation.md +57 -0
- package/.agents/skills/microsoft-foundry/foundry-agent/trace/references/kql-templates.md +173 -0
- package/.agents/skills/microsoft-foundry/foundry-agent/trace/references/search-traces.md +141 -0
- package/.agents/skills/microsoft-foundry/foundry-agent/trace/trace.md +57 -0
- package/.agents/skills/microsoft-foundry/foundry-agent/troubleshoot/troubleshoot.md +93 -0
- package/.agents/skills/microsoft-foundry/models/deploy-model/SKILL.md +144 -0
- package/.agents/skills/microsoft-foundry/models/deploy-model/TEST_PROMPTS.md +78 -0
- package/.agents/skills/microsoft-foundry/models/deploy-model/capacity/SKILL.md +146 -0
- package/.agents/skills/microsoft-foundry/models/deploy-model/capacity/scripts/discover_and_rank.ps1 +113 -0
- package/.agents/skills/microsoft-foundry/models/deploy-model/capacity/scripts/discover_and_rank.sh +113 -0
- package/.agents/skills/microsoft-foundry/models/deploy-model/capacity/scripts/query_capacity.ps1 +80 -0
- package/.agents/skills/microsoft-foundry/models/deploy-model/capacity/scripts/query_capacity.sh +75 -0
- package/.agents/skills/microsoft-foundry/models/deploy-model/customize/EXAMPLES.md +90 -0
- package/.agents/skills/microsoft-foundry/models/deploy-model/customize/SKILL.md +168 -0
- package/.agents/skills/microsoft-foundry/models/deploy-model/customize/references/customize-guides.md +124 -0
- package/.agents/skills/microsoft-foundry/models/deploy-model/customize/references/customize-workflow.md +410 -0
- package/.agents/skills/microsoft-foundry/models/deploy-model/preset/EXAMPLES.md +68 -0
- package/.agents/skills/microsoft-foundry/models/deploy-model/preset/SKILL.md +103 -0
- package/.agents/skills/microsoft-foundry/models/deploy-model/preset/references/preset-workflow.md +694 -0
- package/.agents/skills/microsoft-foundry/models/deploy-model/preset/references/workflow.md +172 -0
- package/.agents/skills/microsoft-foundry/models/deploy-model/scripts/generate_deployment_url.ps1 +73 -0
- package/.agents/skills/microsoft-foundry/models/deploy-model/scripts/generate_deployment_url.sh +90 -0
- package/.agents/skills/microsoft-foundry/project/connections.md +58 -0
- package/.agents/skills/microsoft-foundry/project/create/create-foundry-project.md +132 -0
- package/.agents/skills/microsoft-foundry/quota/quota.md +186 -0
- package/.agents/skills/microsoft-foundry/quota/references/capacity-planning.md +124 -0
- package/.agents/skills/microsoft-foundry/quota/references/error-resolution.md +143 -0
- package/.agents/skills/microsoft-foundry/quota/references/optimization.md +166 -0
- package/.agents/skills/microsoft-foundry/quota/references/ptu-guide.md +150 -0
- package/.agents/skills/microsoft-foundry/quota/references/troubleshooting.md +209 -0
- package/.agents/skills/microsoft-foundry/quota/references/workflows.md +174 -0
- package/.agents/skills/microsoft-foundry/rbac/rbac.md +156 -0
- package/.agents/skills/microsoft-foundry/references/auth-best-practices.md +128 -0
- package/.agents/skills/microsoft-foundry/references/sdk/foundry-sdk-py.md +263 -0
- package/.agents/skills/microsoft-foundry/resource/create/create-foundry-resource.md +150 -0
- package/.agents/skills/microsoft-foundry/resource/create/references/patterns.md +134 -0
- package/.agents/skills/microsoft-foundry/resource/create/references/troubleshooting.md +92 -0
- package/.agents/skills/microsoft-foundry/resource/create/references/workflows.md +235 -0
- package/.agents/skills/native-data-fetching/SKILL.md +491 -0
- package/.agents/skills/next-best-practices/SKILL.md +153 -0
- package/.agents/skills/next-best-practices/async-patterns.md +87 -0
- package/.agents/skills/next-best-practices/bundling.md +180 -0
- package/.agents/skills/next-best-practices/data-patterns.md +297 -0
- package/.agents/skills/next-best-practices/debug-tricks.md +105 -0
- package/.agents/skills/next-best-practices/directives.md +73 -0
- package/.agents/skills/next-best-practices/error-handling.md +227 -0
- package/.agents/skills/next-best-practices/file-conventions.md +140 -0
- package/.agents/skills/next-best-practices/font.md +245 -0
- package/.agents/skills/next-best-practices/functions.md +108 -0
- package/.agents/skills/next-best-practices/hydration-error.md +91 -0
- package/.agents/skills/next-best-practices/image.md +173 -0
- package/.agents/skills/next-best-practices/metadata.md +301 -0
- package/.agents/skills/next-best-practices/parallel-routes.md +287 -0
- package/.agents/skills/next-best-practices/route-handlers.md +146 -0
- package/.agents/skills/next-best-practices/rsc-boundaries.md +159 -0
- package/.agents/skills/next-best-practices/runtime-selection.md +39 -0
- package/.agents/skills/next-best-practices/scripts.md +141 -0
- package/.agents/skills/next-best-practices/self-hosting.md +371 -0
- package/.agents/skills/next-best-practices/suspense-boundaries.md +67 -0
- package/.agents/skills/onboarding-cro/SKILL.md +220 -0
- package/.agents/skills/onboarding-cro/references/experiments.md +258 -0
- package/.agents/skills/page-cro/SKILL.md +182 -0
- package/.agents/skills/page-cro/references/experiments.md +248 -0
- package/.agents/skills/paid-ads/SKILL.md +315 -0
- package/.agents/skills/paid-ads/references/ad-copy-templates.md +207 -0
- package/.agents/skills/paid-ads/references/audience-targeting.md +243 -0
- package/.agents/skills/paid-ads/references/platform-setup-checklists.md +277 -0
- package/.agents/skills/paywall-upgrade-cro/SKILL.md +227 -0
- package/.agents/skills/paywall-upgrade-cro/references/experiments.md +164 -0
- package/.agents/skills/persona-content-creator/SKILL.md +33 -0
- package/.agents/skills/persona-customer-support/SKILL.md +34 -0
- package/.agents/skills/persona-event-coordinator/SKILL.md +35 -0
- package/.agents/skills/persona-exec-assistant/SKILL.md +35 -0
- package/.agents/skills/persona-hr-coordinator/SKILL.md +33 -0
- package/.agents/skills/persona-it-admin/SKILL.md +30 -0
- package/.agents/skills/persona-project-manager/SKILL.md +35 -0
- package/.agents/skills/persona-researcher/SKILL.md +33 -0
- package/.agents/skills/persona-sales-ops/SKILL.md +35 -0
- package/.agents/skills/persona-team-lead/SKILL.md +36 -0
- package/.agents/skills/popup-cro/SKILL.md +453 -0
- package/.agents/skills/pricing-strategy/SKILL.md +231 -0
- package/.agents/skills/pricing-strategy/references/research-methods.md +152 -0
- package/.agents/skills/pricing-strategy/references/tier-structure.md +232 -0
- package/.agents/skills/prisma-cli/SKILL.md +243 -0
- package/.agents/skills/prisma-cli/references/db-execute.md +75 -0
- package/.agents/skills/prisma-cli/references/db-pull.md +185 -0
- package/.agents/skills/prisma-cli/references/db-push.md +148 -0
- package/.agents/skills/prisma-cli/references/db-seed.md +191 -0
- package/.agents/skills/prisma-cli/references/debug.md +46 -0
- package/.agents/skills/prisma-cli/references/dev.md +157 -0
- package/.agents/skills/prisma-cli/references/format.md +48 -0
- package/.agents/skills/prisma-cli/references/generate.md +147 -0
- package/.agents/skills/prisma-cli/references/init.md +118 -0
- package/.agents/skills/prisma-cli/references/migrate-deploy.md +127 -0
- package/.agents/skills/prisma-cli/references/migrate-dev.md +143 -0
- package/.agents/skills/prisma-cli/references/migrate-diff.md +89 -0
- package/.agents/skills/prisma-cli/references/migrate-reset.md +72 -0
- package/.agents/skills/prisma-cli/references/migrate-resolve.md +57 -0
- package/.agents/skills/prisma-cli/references/migrate-status.md +65 -0
- package/.agents/skills/prisma-cli/references/studio.md +122 -0
- package/.agents/skills/prisma-cli/references/validate.md +53 -0
- package/.agents/skills/prisma-client-api/SKILL.md +216 -0
- package/.agents/skills/prisma-client-api/references/client-methods.md +221 -0
- package/.agents/skills/prisma-client-api/references/constructor.md +185 -0
- package/.agents/skills/prisma-client-api/references/filters.md +256 -0
- package/.agents/skills/prisma-client-api/references/model-queries.md +281 -0
- package/.agents/skills/prisma-client-api/references/query-options.md +276 -0
- package/.agents/skills/prisma-client-api/references/raw-queries.md +194 -0
- package/.agents/skills/prisma-client-api/references/relations.md +308 -0
- package/.agents/skills/prisma-client-api/references/transactions.md +184 -0
- package/.agents/skills/prisma-database-setup/SKILL.md +186 -0
- package/.agents/skills/prisma-database-setup/references/cockroachdb.md +89 -0
- package/.agents/skills/prisma-database-setup/references/mongodb.md +72 -0
- package/.agents/skills/prisma-database-setup/references/mysql.md +109 -0
- package/.agents/skills/prisma-database-setup/references/postgresql.md +92 -0
- package/.agents/skills/prisma-database-setup/references/prisma-client-setup.md +47 -0
- package/.agents/skills/prisma-database-setup/references/prisma-postgres.md +90 -0
- package/.agents/skills/prisma-database-setup/references/sqlite.md +106 -0
- package/.agents/skills/prisma-database-setup/references/sqlserver.md +94 -0
- package/.agents/skills/prisma-driver-adapter-implementation/SKILL.md +638 -0
- package/.agents/skills/prisma-postgres/SKILL.md +110 -0
- package/.agents/skills/prisma-postgres/references/console-and-connections.md +40 -0
- package/.agents/skills/prisma-postgres/references/create-db-cli.md +123 -0
- package/.agents/skills/prisma-postgres/references/management-api-sdk.md +56 -0
- package/.agents/skills/prisma-postgres/references/management-api.md +61 -0
- package/.agents/skills/prisma-upgrade-v7/SKILL.md +228 -0
- package/.agents/skills/prisma-upgrade-v7/references/accelerate-users.md +151 -0
- package/.agents/skills/prisma-upgrade-v7/references/driver-adapters.md +251 -0
- package/.agents/skills/prisma-upgrade-v7/references/env-variables.md +161 -0
- package/.agents/skills/prisma-upgrade-v7/references/esm-support.md +160 -0
- package/.agents/skills/prisma-upgrade-v7/references/prisma-config.md +203 -0
- package/.agents/skills/prisma-upgrade-v7/references/removed-features.md +215 -0
- package/.agents/skills/prisma-upgrade-v7/references/schema-changes.md +111 -0
- package/.agents/skills/product-marketing-context/SKILL.md +241 -0
- package/.agents/skills/programmatic-seo/SKILL.md +238 -0
- package/.agents/skills/programmatic-seo/references/playbooks.md +308 -0
- package/.agents/skills/react-doctor/SKILL.md +41 -0
- package/.agents/skills/react-native-best-practices/POWER.md +145 -0
- package/.agents/skills/react-native-best-practices/SKILL.md +215 -0
- package/.agents/skills/react-native-best-practices/agents/openai.yaml +4 -0
- package/.agents/skills/react-native-best-practices/references/bundle-analyze-app.md +211 -0
- package/.agents/skills/react-native-best-practices/references/bundle-analyze-js.md +262 -0
- package/.agents/skills/react-native-best-practices/references/bundle-barrel-exports.md +248 -0
- package/.agents/skills/react-native-best-practices/references/bundle-code-splitting.md +224 -0
- package/.agents/skills/react-native-best-practices/references/bundle-hermes-mmap.md +167 -0
- package/.agents/skills/react-native-best-practices/references/bundle-library-size.md +177 -0
- package/.agents/skills/react-native-best-practices/references/bundle-native-assets.md +214 -0
- package/.agents/skills/react-native-best-practices/references/bundle-r8-android.md +225 -0
- package/.agents/skills/react-native-best-practices/references/bundle-tree-shaking.md +214 -0
- package/.agents/skills/react-native-best-practices/references/images/bundle-treemap-source-map-explorer.png +0 -0
- package/.agents/skills/react-native-best-practices/references/images/controlled-textinput-pingpong.png +0 -0
- package/.agents/skills/react-native-best-practices/references/images/devtools-flamegraph.png +0 -0
- package/.agents/skills/react-native-best-practices/references/images/emerge-xray-ios.png +0 -0
- package/.agents/skills/react-native-best-practices/references/images/expo-atlas-treemap.png +0 -0
- package/.agents/skills/react-native-best-practices/references/images/flashlight-flatlist-vs-flashlist.png +0 -0
- package/.agents/skills/react-native-best-practices/references/images/fps-drop-graph.png +0 -0
- package/.agents/skills/react-native-best-practices/references/images/memory-heap-snapshot.png +0 -0
- package/.agents/skills/react-native-best-practices/references/images/tti-warm-start-diagram.png +0 -0
- package/.agents/skills/react-native-best-practices/references/images/view-hierarchy-flattening.png +0 -0
- package/.agents/skills/react-native-best-practices/references/images/xcode-instruments-templates.png +0 -0
- package/.agents/skills/react-native-best-practices/references/images/xcode-thread-view.png +0 -0
- package/.agents/skills/react-native-best-practices/references/js-animations-reanimated.md +254 -0
- package/.agents/skills/react-native-best-practices/references/js-atomic-state.md +245 -0
- package/.agents/skills/react-native-best-practices/references/js-concurrent-react.md +236 -0
- package/.agents/skills/react-native-best-practices/references/js-lists-flatlist-flashlist.md +236 -0
- package/.agents/skills/react-native-best-practices/references/js-measure-fps.md +180 -0
- package/.agents/skills/react-native-best-practices/references/js-memory-leaks.md +205 -0
- package/.agents/skills/react-native-best-practices/references/js-profile-react.md +161 -0
- package/.agents/skills/react-native-best-practices/references/js-react-compiler.md +368 -0
- package/.agents/skills/react-native-best-practices/references/js-uncontrolled-components.md +216 -0
- package/.agents/skills/react-native-best-practices/references/native-android-16kb-alignment.md +113 -0
- package/.agents/skills/react-native-best-practices/references/native-measure-tti.md +262 -0
- package/.agents/skills/react-native-best-practices/references/native-memory-leaks.md +240 -0
- package/.agents/skills/react-native-best-practices/references/native-memory-patterns.md +274 -0
- package/.agents/skills/react-native-best-practices/references/native-platform-setup.md +110 -0
- package/.agents/skills/react-native-best-practices/references/native-profiling.md +176 -0
- package/.agents/skills/react-native-best-practices/references/native-sdks-over-polyfills.md +183 -0
- package/.agents/skills/react-native-best-practices/references/native-threading-model.md +234 -0
- package/.agents/skills/react-native-best-practices/references/native-turbo-modules.md +292 -0
- package/.agents/skills/react-native-best-practices/references/native-view-flattening.md +201 -0
- package/.agents/skills/react-native-expo/.claude-plugin/plugin.json +13 -0
- package/.agents/skills/react-native-expo/SKILL.md +1077 -0
- package/.agents/skills/react-native-expo/agents/expo-build.md +207 -0
- package/.agents/skills/react-native-expo/assets/css-features-cheatsheet.md +488 -0
- package/.agents/skills/react-native-expo/assets/example-template.txt +14 -0
- package/.agents/skills/react-native-expo/assets/new-arch-decision-tree.md +312 -0
- package/.agents/skills/react-native-expo/references/example-reference.md +26 -0
- package/.agents/skills/react-native-expo/references/expo-sdk-52-breaking.md +409 -0
- package/.agents/skills/react-native-expo/references/new-architecture-errors.md +501 -0
- package/.agents/skills/react-native-expo/references/react-19-migration.md +365 -0
- package/.agents/skills/react-native-expo/scripts/check-rn-version.sh +121 -0
- package/.agents/skills/react-native-expo/scripts/example-script.sh +15 -0
- package/.agents/skills/react-native-testing/SKILL.md +161 -0
- package/.agents/skills/react-native-testing/references/anti-patterns.md +287 -0
- package/.agents/skills/react-native-testing/references/api-reference-v13.md +572 -0
- package/.agents/skills/react-native-testing/references/api-reference-v14.md +574 -0
- package/.agents/skills/recipe-audit-external-sharing/SKILL.md +28 -0
- package/.agents/skills/recipe-backup-sheet-as-csv/SKILL.md +25 -0
- package/.agents/skills/recipe-batch-invite-to-event/SKILL.md +25 -0
- package/.agents/skills/recipe-batch-rename-files/SKILL.md +25 -0
- package/.agents/skills/recipe-batch-reply-to-emails/SKILL.md +26 -0
- package/.agents/skills/recipe-block-focus-time/SKILL.md +24 -0
- package/.agents/skills/recipe-bulk-download-folder/SKILL.md +25 -0
- package/.agents/skills/recipe-cancel-and-notify/SKILL.md +28 -0
- package/.agents/skills/recipe-collect-form-responses/SKILL.md +25 -0
- package/.agents/skills/recipe-compare-sheet-tabs/SKILL.md +25 -0
- package/.agents/skills/recipe-copy-sheet-for-new-month/SKILL.md +25 -0
- package/.agents/skills/recipe-create-classroom-course/SKILL.md +25 -0
- package/.agents/skills/recipe-create-doc-from-template/SKILL.md +29 -0
- package/.agents/skills/recipe-create-events-from-sheet/SKILL.md +24 -0
- package/.agents/skills/recipe-create-expense-tracker/SKILL.md +26 -0
- package/.agents/skills/recipe-create-feedback-form/SKILL.md +25 -0
- package/.agents/skills/recipe-create-gmail-filter/SKILL.md +26 -0
- package/.agents/skills/recipe-create-meet-space/SKILL.md +25 -0
- package/.agents/skills/recipe-create-presentation/SKILL.md +25 -0
- package/.agents/skills/recipe-create-shared-drive/SKILL.md +25 -0
- package/.agents/skills/recipe-create-task-list/SKILL.md +26 -0
- package/.agents/skills/recipe-create-vacation-responder/SKILL.md +25 -0
- package/.agents/skills/recipe-deploy-apps-script/SKILL.md +26 -0
- package/.agents/skills/recipe-draft-email-from-doc/SKILL.md +25 -0
- package/.agents/skills/recipe-email-drive-link/SKILL.md +25 -0
- package/.agents/skills/recipe-find-free-time/SKILL.md +25 -0
- package/.agents/skills/recipe-find-large-files/SKILL.md +24 -0
- package/.agents/skills/recipe-forward-labeled-emails/SKILL.md +27 -0
- package/.agents/skills/recipe-generate-report-from-sheet/SKILL.md +34 -0
- package/.agents/skills/recipe-label-and-archive-emails/SKILL.md +25 -0
- package/.agents/skills/recipe-log-deal-update/SKILL.md +25 -0
- package/.agents/skills/recipe-organize-drive-folder/SKILL.md +26 -0
- package/.agents/skills/recipe-plan-weekly-schedule/SKILL.md +26 -0
- package/.agents/skills/recipe-post-mortem-setup/SKILL.md +25 -0
- package/.agents/skills/recipe-reschedule-meeting/SKILL.md +25 -0
- package/.agents/skills/recipe-review-meet-participants/SKILL.md +25 -0
- package/.agents/skills/recipe-review-overdue-tasks/SKILL.md +25 -0
- package/.agents/skills/recipe-save-email-attachments/SKILL.md +26 -0
- package/.agents/skills/recipe-save-email-to-doc/SKILL.md +29 -0
- package/.agents/skills/recipe-schedule-recurring-event/SKILL.md +24 -0
- package/.agents/skills/recipe-search-and-export-emails/SKILL.md +25 -0
- package/.agents/skills/recipe-send-personalized-emails/SKILL.md +24 -0
- package/.agents/skills/recipe-send-team-announcement/SKILL.md +24 -0
- package/.agents/skills/recipe-share-doc-and-notify/SKILL.md +25 -0
- package/.agents/skills/recipe-share-event-materials/SKILL.md +25 -0
- package/.agents/skills/recipe-share-folder-with-team/SKILL.md +26 -0
- package/.agents/skills/recipe-sync-contacts-to-sheet/SKILL.md +25 -0
- package/.agents/skills/recipe-transfer-file-ownership/SKILL.md +27 -0
- package/.agents/skills/recipe-triage-security-alerts/SKILL.md +25 -0
- package/.agents/skills/recipe-watch-drive-changes/SKILL.md +25 -0
- package/.agents/skills/referral-program/SKILL.md +255 -0
- package/.agents/skills/referral-program/references/affiliate-programs.md +164 -0
- package/.agents/skills/referral-program/references/program-examples.md +143 -0
- package/.agents/skills/remotion-best-practices/SKILL.md +43 -0
- package/.agents/skills/remotion-best-practices/rules/3d.md +86 -0
- package/.agents/skills/remotion-best-practices/rules/animations.md +29 -0
- package/.agents/skills/remotion-best-practices/rules/assets/charts-bar-chart.tsx +173 -0
- package/.agents/skills/remotion-best-practices/rules/assets/text-animations-typewriter.tsx +100 -0
- package/.agents/skills/remotion-best-practices/rules/assets/text-animations-word-highlight.tsx +108 -0
- package/.agents/skills/remotion-best-practices/rules/assets.md +78 -0
- package/.agents/skills/remotion-best-practices/rules/audio.md +172 -0
- package/.agents/skills/remotion-best-practices/rules/calculate-metadata.md +104 -0
- package/.agents/skills/remotion-best-practices/rules/can-decode.md +75 -0
- package/.agents/skills/remotion-best-practices/rules/charts.md +58 -0
- package/.agents/skills/remotion-best-practices/rules/compositions.md +146 -0
- package/.agents/skills/remotion-best-practices/rules/display-captions.md +126 -0
- package/.agents/skills/remotion-best-practices/rules/extract-frames.md +229 -0
- package/.agents/skills/remotion-best-practices/rules/fonts.md +152 -0
- package/.agents/skills/remotion-best-practices/rules/get-audio-duration.md +58 -0
- package/.agents/skills/remotion-best-practices/rules/get-video-dimensions.md +68 -0
- package/.agents/skills/remotion-best-practices/rules/get-video-duration.md +58 -0
- package/.agents/skills/remotion-best-practices/rules/gifs.md +138 -0
- package/.agents/skills/remotion-best-practices/rules/images.md +130 -0
- package/.agents/skills/remotion-best-practices/rules/import-srt-captions.md +67 -0
- package/.agents/skills/remotion-best-practices/rules/lottie.md +68 -0
- package/.agents/skills/remotion-best-practices/rules/measuring-dom-nodes.md +35 -0
- package/.agents/skills/remotion-best-practices/rules/measuring-text.md +143 -0
- package/.agents/skills/remotion-best-practices/rules/sequencing.md +106 -0
- package/.agents/skills/remotion-best-practices/rules/tailwind.md +11 -0
- package/.agents/skills/remotion-best-practices/rules/text-animations.md +20 -0
- package/.agents/skills/remotion-best-practices/rules/timing.md +179 -0
- package/.agents/skills/remotion-best-practices/rules/transcribe-captions.md +19 -0
- package/.agents/skills/remotion-best-practices/rules/transitions.md +122 -0
- package/.agents/skills/remotion-best-practices/rules/trimming.md +53 -0
- package/.agents/skills/remotion-best-practices/rules/videos.md +171 -0
- package/.agents/skills/resend/LICENSE +21 -0
- package/.agents/skills/resend/README.md +59 -0
- package/.agents/skills/resend/SKILL.md +86 -0
- package/.agents/skills/resend/agent-email-inbox/SKILL.md +1147 -0
- package/.agents/skills/resend/package.json +11 -0
- package/.agents/skills/resend/pnpm-lock.yaml +68 -0
- package/.agents/skills/resend/resend-inbound/SKILL.md +279 -0
- package/.agents/skills/resend/send-email/SKILL.md +405 -0
- package/.agents/skills/resend/send-email/references/batch-email-examples.md +807 -0
- package/.agents/skills/resend/send-email/references/best-practices.md +453 -0
- package/.agents/skills/resend/send-email/references/installation.md +142 -0
- package/.agents/skills/resend/send-email/references/single-email-examples.md +470 -0
- package/.agents/skills/resend/send-email/references/webhooks.md +221 -0
- package/.agents/skills/resend/templates/SKILL.md +105 -0
- package/.agents/skills/resend/templates/fetch-all-templates.mjs +36 -0
- package/.agents/skills/resend/templates/reference.md +114 -0
- package/.agents/skills/resend/tests/agent-email-inbox.test.md +371 -0
- package/.agents/skills/resend/tests/resend-inbound.test.md +280 -0
- package/.agents/skills/resend/tests/resend-router.test.md +200 -0
- package/.agents/skills/resend/tests/send-email.test.md +337 -0
- package/.agents/skills/resend/tests/templates.test.md +295 -0
- package/.agents/skills/revops/SKILL.md +343 -0
- package/.agents/skills/revops/references/automation-playbooks.md +290 -0
- package/.agents/skills/revops/references/lifecycle-definitions.md +278 -0
- package/.agents/skills/revops/references/routing-rules.md +203 -0
- package/.agents/skills/revops/references/scoring-models.md +247 -0
- package/.agents/skills/sales-enablement/SKILL.md +349 -0
- package/.agents/skills/sales-enablement/references/deck-frameworks.md +263 -0
- package/.agents/skills/sales-enablement/references/demo-scripts.md +355 -0
- package/.agents/skills/sales-enablement/references/objection-library.md +270 -0
- package/.agents/skills/sales-enablement/references/one-pager-templates.md +208 -0
- package/.agents/skills/schema-markup/SKILL.md +179 -0
- package/.agents/skills/schema-markup/references/schema-examples.md +398 -0
- package/.agents/skills/seo-audit/SKILL.md +412 -0
- package/.agents/skills/seo-audit/references/ai-writing-detection.md +200 -0
- package/.agents/skills/shadcn/SKILL.md +240 -0
- package/.agents/skills/shadcn/agents/openai.yml +5 -0
- package/.agents/skills/shadcn/assets/shadcn-small.png +0 -0
- package/.agents/skills/shadcn/assets/shadcn.png +0 -0
- package/.agents/skills/shadcn/cli.md +255 -0
- package/.agents/skills/shadcn/customization.md +202 -0
- package/.agents/skills/shadcn/evals/evals.json +47 -0
- package/.agents/skills/shadcn/mcp.md +94 -0
- package/.agents/skills/shadcn/rules/base-vs-radix.md +306 -0
- package/.agents/skills/shadcn/rules/composition.md +195 -0
- package/.agents/skills/shadcn/rules/forms.md +192 -0
- package/.agents/skills/shadcn/rules/icons.md +101 -0
- package/.agents/skills/shadcn/rules/styling.md +162 -0
- package/.agents/skills/signup-flow-cro/SKILL.md +359 -0
- package/.agents/skills/site-architecture/SKILL.md +357 -0
- package/.agents/skills/site-architecture/references/mermaid-templates.md +216 -0
- package/.agents/skills/site-architecture/references/navigation-patterns.md +305 -0
- package/.agents/skills/site-architecture/references/site-type-templates.md +293 -0
- package/.agents/skills/skill-creator/LICENSE.txt +202 -0
- package/.agents/skills/skill-creator/SKILL.md +356 -0
- package/.agents/skills/skill-creator/references/output-patterns.md +82 -0
- package/.agents/skills/skill-creator/references/workflows.md +28 -0
- package/.agents/skills/skill-creator/scripts/init_skill.py +303 -0
- package/.agents/skills/skill-creator/scripts/package_skill.py +110 -0
- package/.agents/skills/skill-creator/scripts/quick_validate.py +95 -0
- package/.agents/skills/social-content/SKILL.md +278 -0
- package/.agents/skills/social-content/references/platforms.md +170 -0
- package/.agents/skills/social-content/references/post-templates.md +177 -0
- package/.agents/skills/social-content/references/reverse-engineering.md +195 -0
- package/.agents/skills/supabase-postgres-best-practices/AGENTS.md +68 -0
- package/.agents/skills/supabase-postgres-best-practices/CLAUDE.md +68 -0
- package/.agents/skills/supabase-postgres-best-practices/SKILL.md +64 -0
- package/.agents/skills/supabase-postgres-best-practices/references/advanced-full-text-search.md +55 -0
- package/.agents/skills/supabase-postgres-best-practices/references/advanced-jsonb-indexing.md +49 -0
- package/.agents/skills/supabase-postgres-best-practices/references/conn-idle-timeout.md +46 -0
- package/.agents/skills/supabase-postgres-best-practices/references/conn-limits.md +44 -0
- package/.agents/skills/supabase-postgres-best-practices/references/conn-pooling.md +41 -0
- package/.agents/skills/supabase-postgres-best-practices/references/conn-prepared-statements.md +46 -0
- package/.agents/skills/supabase-postgres-best-practices/references/data-batch-inserts.md +54 -0
- package/.agents/skills/supabase-postgres-best-practices/references/data-n-plus-one.md +53 -0
- package/.agents/skills/supabase-postgres-best-practices/references/data-pagination.md +50 -0
- package/.agents/skills/supabase-postgres-best-practices/references/data-upsert.md +50 -0
- package/.agents/skills/supabase-postgres-best-practices/references/lock-advisory.md +56 -0
- package/.agents/skills/supabase-postgres-best-practices/references/lock-deadlock-prevention.md +68 -0
- package/.agents/skills/supabase-postgres-best-practices/references/lock-short-transactions.md +50 -0
- package/.agents/skills/supabase-postgres-best-practices/references/lock-skip-locked.md +54 -0
- package/.agents/skills/supabase-postgres-best-practices/references/monitor-explain-analyze.md +45 -0
- package/.agents/skills/supabase-postgres-best-practices/references/monitor-pg-stat-statements.md +55 -0
- package/.agents/skills/supabase-postgres-best-practices/references/monitor-vacuum-analyze.md +55 -0
- package/.agents/skills/supabase-postgres-best-practices/references/query-composite-indexes.md +44 -0
- package/.agents/skills/supabase-postgres-best-practices/references/query-covering-indexes.md +40 -0
- package/.agents/skills/supabase-postgres-best-practices/references/query-index-types.md +48 -0
- package/.agents/skills/supabase-postgres-best-practices/references/query-missing-indexes.md +43 -0
- package/.agents/skills/supabase-postgres-best-practices/references/query-partial-indexes.md +45 -0
- package/.agents/skills/supabase-postgres-best-practices/references/schema-constraints.md +80 -0
- package/.agents/skills/supabase-postgres-best-practices/references/schema-data-types.md +46 -0
- package/.agents/skills/supabase-postgres-best-practices/references/schema-foreign-key-indexes.md +59 -0
- package/.agents/skills/supabase-postgres-best-practices/references/schema-lowercase-identifiers.md +55 -0
- package/.agents/skills/supabase-postgres-best-practices/references/schema-partitioning.md +55 -0
- package/.agents/skills/supabase-postgres-best-practices/references/schema-primary-keys.md +61 -0
- package/.agents/skills/supabase-postgres-best-practices/references/security-privileges.md +54 -0
- package/.agents/skills/supabase-postgres-best-practices/references/security-rls-basics.md +50 -0
- package/.agents/skills/supabase-postgres-best-practices/references/security-rls-performance.md +57 -0
- package/.agents/skills/ui-skills/SKILL.md +85 -0
- package/.agents/skills/ui-ux-pro-max/SKILL.md +386 -0
- package/.agents/skills/ui-ux-pro-max/data/charts.csv +26 -0
- package/.agents/skills/ui-ux-pro-max/data/colors.csv +97 -0
- package/.agents/skills/ui-ux-pro-max/data/icons.csv +101 -0
- package/.agents/skills/ui-ux-pro-max/data/landing.csv +31 -0
- package/.agents/skills/ui-ux-pro-max/data/products.csv +97 -0
- package/.agents/skills/ui-ux-pro-max/data/react-performance.csv +45 -0
- package/.agents/skills/ui-ux-pro-max/data/stacks/astro.csv +54 -0
- package/.agents/skills/ui-ux-pro-max/data/stacks/flutter.csv +53 -0
- package/.agents/skills/ui-ux-pro-max/data/stacks/html-tailwind.csv +56 -0
- package/.agents/skills/ui-ux-pro-max/data/stacks/jetpack-compose.csv +53 -0
- package/.agents/skills/ui-ux-pro-max/data/stacks/nextjs.csv +53 -0
- package/.agents/skills/ui-ux-pro-max/data/stacks/nuxt-ui.csv +51 -0
- package/.agents/skills/ui-ux-pro-max/data/stacks/nuxtjs.csv +59 -0
- package/.agents/skills/ui-ux-pro-max/data/stacks/react-native.csv +52 -0
- package/.agents/skills/ui-ux-pro-max/data/stacks/react.csv +54 -0
- package/.agents/skills/ui-ux-pro-max/data/stacks/shadcn.csv +61 -0
- package/.agents/skills/ui-ux-pro-max/data/stacks/svelte.csv +54 -0
- package/.agents/skills/ui-ux-pro-max/data/stacks/swiftui.csv +51 -0
- package/.agents/skills/ui-ux-pro-max/data/stacks/vue.csv +50 -0
- package/.agents/skills/ui-ux-pro-max/data/styles.csv +68 -0
- package/.agents/skills/ui-ux-pro-max/data/typography.csv +58 -0
- package/.agents/skills/ui-ux-pro-max/data/ui-reasoning.csv +101 -0
- package/.agents/skills/ui-ux-pro-max/data/ux-guidelines.csv +100 -0
- package/.agents/skills/ui-ux-pro-max/data/web-interface.csv +31 -0
- package/.agents/skills/ui-ux-pro-max/scripts/core.py +253 -0
- package/.agents/skills/ui-ux-pro-max/scripts/design_system.py +1067 -0
- package/.agents/skills/ui-ux-pro-max/scripts/search.py +114 -0
- package/.agents/skills/upgrading-expo/SKILL.md +125 -0
- package/.agents/skills/upgrading-expo/references/expo-av-to-audio.md +132 -0
- package/.agents/skills/upgrading-expo/references/expo-av-to-video.md +160 -0
- package/.agents/skills/upgrading-expo/references/native-tabs.md +124 -0
- package/.agents/skills/upgrading-expo/references/new-architecture.md +79 -0
- package/.agents/skills/upgrading-expo/references/react-19.md +79 -0
- package/.agents/skills/upgrading-expo/references/react-compiler.md +59 -0
- package/.agents/skills/use-dom/SKILL.md +417 -0
- package/.agents/skills/use-railway/SKILL.md +124 -0
- package/.agents/skills/use-railway/references/configure.md +283 -0
- package/.agents/skills/use-railway/references/deploy.md +184 -0
- package/.agents/skills/use-railway/references/operate.md +159 -0
- package/.agents/skills/use-railway/references/request.md +238 -0
- package/.agents/skills/use-railway/references/setup.md +195 -0
- package/.agents/skills/use-railway/scripts/railway-api.sh +41 -0
- package/.agents/skills/vercel-composition-patterns/AGENTS.md +946 -0
- package/.agents/skills/vercel-composition-patterns/README.md +60 -0
- package/.agents/skills/vercel-composition-patterns/SKILL.md +89 -0
- package/.agents/skills/vercel-composition-patterns/rules/architecture-avoid-boolean-props.md +100 -0
- package/.agents/skills/vercel-composition-patterns/rules/architecture-compound-components.md +112 -0
- package/.agents/skills/vercel-composition-patterns/rules/patterns-children-over-render-props.md +87 -0
- package/.agents/skills/vercel-composition-patterns/rules/patterns-explicit-variants.md +100 -0
- package/.agents/skills/vercel-composition-patterns/rules/react19-no-forwardref.md +42 -0
- package/.agents/skills/vercel-composition-patterns/rules/state-context-interface.md +191 -0
- package/.agents/skills/vercel-composition-patterns/rules/state-decouple-implementation.md +113 -0
- package/.agents/skills/vercel-composition-patterns/rules/state-lift-state.md +125 -0
- package/.agents/skills/vercel-react-best-practices/AGENTS.md +2934 -0
- package/.agents/skills/vercel-react-best-practices/SKILL.md +136 -0
- package/.agents/skills/vercel-react-best-practices/rules/advanced-event-handler-refs.md +55 -0
- package/.agents/skills/vercel-react-best-practices/rules/advanced-init-once.md +42 -0
- package/.agents/skills/vercel-react-best-practices/rules/advanced-use-latest.md +39 -0
- package/.agents/skills/vercel-react-best-practices/rules/async-api-routes.md +38 -0
- package/.agents/skills/vercel-react-best-practices/rules/async-defer-await.md +80 -0
- package/.agents/skills/vercel-react-best-practices/rules/async-dependencies.md +51 -0
- package/.agents/skills/vercel-react-best-practices/rules/async-parallel.md +28 -0
- package/.agents/skills/vercel-react-best-practices/rules/async-suspense-boundaries.md +99 -0
- package/.agents/skills/vercel-react-best-practices/rules/bundle-barrel-imports.md +59 -0
- package/.agents/skills/vercel-react-best-practices/rules/bundle-conditional.md +31 -0
- package/.agents/skills/vercel-react-best-practices/rules/bundle-defer-third-party.md +49 -0
- package/.agents/skills/vercel-react-best-practices/rules/bundle-dynamic-imports.md +35 -0
- package/.agents/skills/vercel-react-best-practices/rules/bundle-preload.md +50 -0
- package/.agents/skills/vercel-react-best-practices/rules/client-event-listeners.md +74 -0
- package/.agents/skills/vercel-react-best-practices/rules/client-localstorage-schema.md +71 -0
- package/.agents/skills/vercel-react-best-practices/rules/client-passive-event-listeners.md +48 -0
- package/.agents/skills/vercel-react-best-practices/rules/client-swr-dedup.md +56 -0
- package/.agents/skills/vercel-react-best-practices/rules/js-batch-dom-css.md +107 -0
- package/.agents/skills/vercel-react-best-practices/rules/js-cache-function-results.md +80 -0
- package/.agents/skills/vercel-react-best-practices/rules/js-cache-property-access.md +28 -0
- package/.agents/skills/vercel-react-best-practices/rules/js-cache-storage.md +70 -0
- package/.agents/skills/vercel-react-best-practices/rules/js-combine-iterations.md +32 -0
- package/.agents/skills/vercel-react-best-practices/rules/js-early-exit.md +50 -0
- package/.agents/skills/vercel-react-best-practices/rules/js-hoist-regexp.md +45 -0
- package/.agents/skills/vercel-react-best-practices/rules/js-index-maps.md +37 -0
- package/.agents/skills/vercel-react-best-practices/rules/js-length-check-first.md +49 -0
- package/.agents/skills/vercel-react-best-practices/rules/js-min-max-loop.md +82 -0
- package/.agents/skills/vercel-react-best-practices/rules/js-set-map-lookups.md +24 -0
- package/.agents/skills/vercel-react-best-practices/rules/js-tosorted-immutable.md +57 -0
- package/.agents/skills/vercel-react-best-practices/rules/rendering-activity.md +26 -0
- package/.agents/skills/vercel-react-best-practices/rules/rendering-animate-svg-wrapper.md +47 -0
- package/.agents/skills/vercel-react-best-practices/rules/rendering-conditional-render.md +40 -0
- package/.agents/skills/vercel-react-best-practices/rules/rendering-content-visibility.md +38 -0
- package/.agents/skills/vercel-react-best-practices/rules/rendering-hoist-jsx.md +46 -0
- package/.agents/skills/vercel-react-best-practices/rules/rendering-hydration-no-flicker.md +82 -0
- package/.agents/skills/vercel-react-best-practices/rules/rendering-hydration-suppress-warning.md +30 -0
- package/.agents/skills/vercel-react-best-practices/rules/rendering-svg-precision.md +28 -0
- package/.agents/skills/vercel-react-best-practices/rules/rendering-usetransition-loading.md +75 -0
- package/.agents/skills/vercel-react-best-practices/rules/rerender-defer-reads.md +39 -0
- package/.agents/skills/vercel-react-best-practices/rules/rerender-dependencies.md +45 -0
- package/.agents/skills/vercel-react-best-practices/rules/rerender-derived-state-no-effect.md +40 -0
- package/.agents/skills/vercel-react-best-practices/rules/rerender-derived-state.md +29 -0
- package/.agents/skills/vercel-react-best-practices/rules/rerender-functional-setstate.md +74 -0
- package/.agents/skills/vercel-react-best-practices/rules/rerender-lazy-state-init.md +58 -0
- package/.agents/skills/vercel-react-best-practices/rules/rerender-memo-with-default-value.md +38 -0
- package/.agents/skills/vercel-react-best-practices/rules/rerender-memo.md +44 -0
- package/.agents/skills/vercel-react-best-practices/rules/rerender-move-effect-to-event.md +45 -0
- package/.agents/skills/vercel-react-best-practices/rules/rerender-simple-expression-in-memo.md +35 -0
- package/.agents/skills/vercel-react-best-practices/rules/rerender-transitions.md +40 -0
- package/.agents/skills/vercel-react-best-practices/rules/rerender-use-ref-transient-values.md +73 -0
- package/.agents/skills/vercel-react-best-practices/rules/server-after-nonblocking.md +73 -0
- package/.agents/skills/vercel-react-best-practices/rules/server-auth-actions.md +96 -0
- package/.agents/skills/vercel-react-best-practices/rules/server-cache-lru.md +41 -0
- package/.agents/skills/vercel-react-best-practices/rules/server-cache-react.md +76 -0
- package/.agents/skills/vercel-react-best-practices/rules/server-dedup-props.md +65 -0
- package/.agents/skills/vercel-react-best-practices/rules/server-parallel-fetching.md +83 -0
- package/.agents/skills/vercel-react-best-practices/rules/server-serialization.md +38 -0
- package/.agents/skills/vercel-react-native-skills/AGENTS.md +2897 -0
- package/.agents/skills/vercel-react-native-skills/SKILL.md +121 -0
- package/.agents/skills/vercel-react-native-skills/rules/animation-derived-value.md +53 -0
- package/.agents/skills/vercel-react-native-skills/rules/animation-gesture-detector-press.md +95 -0
- package/.agents/skills/vercel-react-native-skills/rules/animation-gpu-properties.md +65 -0
- package/.agents/skills/vercel-react-native-skills/rules/design-system-compound-components.md +66 -0
- package/.agents/skills/vercel-react-native-skills/rules/fonts-config-plugin.md +71 -0
- package/.agents/skills/vercel-react-native-skills/rules/imports-design-system-folder.md +68 -0
- package/.agents/skills/vercel-react-native-skills/rules/js-hoist-intl.md +61 -0
- package/.agents/skills/vercel-react-native-skills/rules/list-performance-callbacks.md +44 -0
- package/.agents/skills/vercel-react-native-skills/rules/list-performance-function-references.md +132 -0
- package/.agents/skills/vercel-react-native-skills/rules/list-performance-images.md +53 -0
- package/.agents/skills/vercel-react-native-skills/rules/list-performance-inline-objects.md +97 -0
- package/.agents/skills/vercel-react-native-skills/rules/list-performance-item-expensive.md +94 -0
- package/.agents/skills/vercel-react-native-skills/rules/list-performance-item-memo.md +82 -0
- package/.agents/skills/vercel-react-native-skills/rules/list-performance-item-types.md +104 -0
- package/.agents/skills/vercel-react-native-skills/rules/list-performance-virtualize.md +67 -0
- package/.agents/skills/vercel-react-native-skills/rules/monorepo-native-deps-in-app.md +46 -0
- package/.agents/skills/vercel-react-native-skills/rules/monorepo-single-dependency-versions.md +63 -0
- package/.agents/skills/vercel-react-native-skills/rules/navigation-native-navigators.md +188 -0
- package/.agents/skills/vercel-react-native-skills/rules/react-compiler-destructure-functions.md +50 -0
- package/.agents/skills/vercel-react-native-skills/rules/react-compiler-reanimated-shared-values.md +48 -0
- package/.agents/skills/vercel-react-native-skills/rules/react-state-dispatcher.md +91 -0
- package/.agents/skills/vercel-react-native-skills/rules/react-state-fallback.md +56 -0
- package/.agents/skills/vercel-react-native-skills/rules/react-state-minimize.md +65 -0
- package/.agents/skills/vercel-react-native-skills/rules/rendering-no-falsy-and.md +74 -0
- package/.agents/skills/vercel-react-native-skills/rules/rendering-text-in-text-component.md +36 -0
- package/.agents/skills/vercel-react-native-skills/rules/scroll-position-no-state.md +82 -0
- package/.agents/skills/vercel-react-native-skills/rules/state-ground-truth.md +80 -0
- package/.agents/skills/vercel-react-native-skills/rules/ui-expo-image.md +66 -0
- package/.agents/skills/vercel-react-native-skills/rules/ui-image-gallery.md +104 -0
- package/.agents/skills/vercel-react-native-skills/rules/ui-measure-views.md +78 -0
- package/.agents/skills/vercel-react-native-skills/rules/ui-menus.md +174 -0
- package/.agents/skills/vercel-react-native-skills/rules/ui-native-modals.md +77 -0
- package/.agents/skills/vercel-react-native-skills/rules/ui-pressable.md +61 -0
- package/.agents/skills/vercel-react-native-skills/rules/ui-safe-area-scroll.md +65 -0
- package/.agents/skills/vercel-react-native-skills/rules/ui-scrollview-content-inset.md +45 -0
- package/.agents/skills/vercel-react-native-skills/rules/ui-styling.md +87 -0
- package/.agents/skills/web-design-guidelines/SKILL.md +39 -0
- package/.claude/CLAUDE.md +48 -0
- package/.claude/settings.local.json +7 -0
- package/.claude/skills/agent-browser/SKILL.md +347 -0
- package/.claude/skills/agent-browser/references/authentication.md +188 -0
- package/.claude/skills/agent-browser/references/proxy-support.md +175 -0
- package/.claude/skills/agent-browser/references/session-management.md +181 -0
- package/.claude/skills/agent-browser/references/snapshot-refs.md +186 -0
- package/.claude/skills/agent-browser/references/video-recording.md +162 -0
- package/.claude/skills/agent-browser/templates/authenticated-session.sh +91 -0
- package/.claude/skills/agent-browser/templates/capture-workflow.sh +68 -0
- package/.claude/skills/agent-browser/templates/form-automation.sh +64 -0
- package/.claude/skills/better-auth-best-practices/SKILL.md +166 -0
- package/.claude/skills/create-auth-skill/SKILL.md +214 -0
- package/.claude/skills/design-md/SKILL.md +172 -0
- package/.claude/skills/design-md/examples/DESIGN.md +154 -0
- package/.claude/skills/frontend-design/LICENSE.txt +177 -0
- package/.claude/skills/frontend-design/SKILL.md +42 -0
- package/.claude/skills/init-nexxo/SKILL.md +61 -0
- package/.claude/skills/init-nexxo/references/templates.md +166 -0
- package/.claude/skills/kickoff/SKILL.md +90 -0
- package/.claude/skills/planning-with-files/SKILL.md +160 -0
- package/.claude/skills/planning-with-files/examples.md +202 -0
- package/.claude/skills/planning-with-files/reference.md +110 -0
- package/.claude/skills/react-useeffect/SKILL.md +53 -0
- package/.claude/skills/react-useeffect/alternatives.md +258 -0
- package/.claude/skills/react-useeffect/anti-patterns.md +290 -0
- package/.claude/skills/remotion-best-practices/SKILL.md +43 -0
- package/.claude/skills/remotion-best-practices/rules/3d.md +86 -0
- package/.claude/skills/remotion-best-practices/rules/animations.md +29 -0
- package/.claude/skills/remotion-best-practices/rules/assets/charts-bar-chart.tsx +173 -0
- package/.claude/skills/remotion-best-practices/rules/assets/text-animations-typewriter.tsx +100 -0
- package/.claude/skills/remotion-best-practices/rules/assets/text-animations-word-highlight.tsx +108 -0
- package/.claude/skills/remotion-best-practices/rules/assets.md +78 -0
- package/.claude/skills/remotion-best-practices/rules/audio.md +172 -0
- package/.claude/skills/remotion-best-practices/rules/calculate-metadata.md +104 -0
- package/.claude/skills/remotion-best-practices/rules/can-decode.md +75 -0
- package/.claude/skills/remotion-best-practices/rules/charts.md +58 -0
- package/.claude/skills/remotion-best-practices/rules/compositions.md +146 -0
- package/.claude/skills/remotion-best-practices/rules/display-captions.md +126 -0
- package/.claude/skills/remotion-best-practices/rules/extract-frames.md +229 -0
- package/.claude/skills/remotion-best-practices/rules/fonts.md +152 -0
- package/.claude/skills/remotion-best-practices/rules/get-audio-duration.md +58 -0
- package/.claude/skills/remotion-best-practices/rules/get-video-dimensions.md +68 -0
- package/.claude/skills/remotion-best-practices/rules/get-video-duration.md +58 -0
- package/.claude/skills/remotion-best-practices/rules/gifs.md +138 -0
- package/.claude/skills/remotion-best-practices/rules/images.md +130 -0
- package/.claude/skills/remotion-best-practices/rules/import-srt-captions.md +67 -0
- package/.claude/skills/remotion-best-practices/rules/lottie.md +68 -0
- package/.claude/skills/remotion-best-practices/rules/measuring-dom-nodes.md +35 -0
- package/.claude/skills/remotion-best-practices/rules/measuring-text.md +143 -0
- package/.claude/skills/remotion-best-practices/rules/sequencing.md +106 -0
- package/.claude/skills/remotion-best-practices/rules/tailwind.md +11 -0
- package/.claude/skills/remotion-best-practices/rules/text-animations.md +20 -0
- package/.claude/skills/remotion-best-practices/rules/timing.md +179 -0
- package/.claude/skills/remotion-best-practices/rules/transcribe-captions.md +19 -0
- package/.claude/skills/remotion-best-practices/rules/transitions.md +122 -0
- package/.claude/skills/remotion-best-practices/rules/trimming.md +53 -0
- package/.claude/skills/remotion-best-practices/rules/videos.md +171 -0
- package/.claude/skills/skill-creator/LICENSE.txt +202 -0
- package/.claude/skills/skill-creator/SKILL.md +356 -0
- package/.claude/skills/skill-creator/references/output-patterns.md +82 -0
- package/.claude/skills/skill-creator/references/workflows.md +28 -0
- package/.claude/skills/skill-creator/scripts/init_skill.py +303 -0
- package/.claude/skills/skill-creator/scripts/package_skill.py +110 -0
- package/.claude/skills/skill-creator/scripts/quick_validate.py +95 -0
- package/.claude/skills/ui-skills/SKILL.md +85 -0
- package/.claude/skills/uifix/SKILL.md +46 -0
- package/.claude/skills/web-design-guidelines/SKILL.md +39 -0
- package/.codex/config.toml.template +158 -0
- package/.cursor/CURSOR.md +0 -0
- package/.cursor/hooks/state/continual-learning.json +8 -0
- package/.cursor/skills/agent-browser/SKILL.md +347 -0
- package/.cursor/skills/agent-browser/references/authentication.md +188 -0
- package/.cursor/skills/agent-browser/references/proxy-support.md +175 -0
- package/.cursor/skills/agent-browser/references/session-management.md +181 -0
- package/.cursor/skills/agent-browser/references/snapshot-refs.md +186 -0
- package/.cursor/skills/agent-browser/references/video-recording.md +162 -0
- package/.cursor/skills/agent-browser/templates/authenticated-session.sh +91 -0
- package/.cursor/skills/agent-browser/templates/capture-workflow.sh +68 -0
- package/.cursor/skills/agent-browser/templates/form-automation.sh +64 -0
- package/.cursor/skills/better-auth-best-practices/SKILL.md +166 -0
- package/.cursor/skills/create-auth-skill/SKILL.md +214 -0
- package/.cursor/skills/design-md/SKILL.md +172 -0
- package/.cursor/skills/design-md/examples/DESIGN.md +154 -0
- package/.cursor/skills/frontend-design/LICENSE.txt +177 -0
- package/.cursor/skills/frontend-design/SKILL.md +42 -0
- package/.cursor/skills/remotion-best-practices/SKILL.md +43 -0
- package/.cursor/skills/remotion-best-practices/rules/3d.md +86 -0
- package/.cursor/skills/remotion-best-practices/rules/animations.md +29 -0
- package/.cursor/skills/remotion-best-practices/rules/assets/charts-bar-chart.tsx +173 -0
- package/.cursor/skills/remotion-best-practices/rules/assets/text-animations-typewriter.tsx +100 -0
- package/.cursor/skills/remotion-best-practices/rules/assets/text-animations-word-highlight.tsx +108 -0
- package/.cursor/skills/remotion-best-practices/rules/assets.md +78 -0
- package/.cursor/skills/remotion-best-practices/rules/audio.md +172 -0
- package/.cursor/skills/remotion-best-practices/rules/calculate-metadata.md +104 -0
- package/.cursor/skills/remotion-best-practices/rules/can-decode.md +75 -0
- package/.cursor/skills/remotion-best-practices/rules/charts.md +58 -0
- package/.cursor/skills/remotion-best-practices/rules/compositions.md +146 -0
- package/.cursor/skills/remotion-best-practices/rules/display-captions.md +126 -0
- package/.cursor/skills/remotion-best-practices/rules/extract-frames.md +229 -0
- package/.cursor/skills/remotion-best-practices/rules/fonts.md +152 -0
- package/.cursor/skills/remotion-best-practices/rules/get-audio-duration.md +58 -0
- package/.cursor/skills/remotion-best-practices/rules/get-video-dimensions.md +68 -0
- package/.cursor/skills/remotion-best-practices/rules/get-video-duration.md +58 -0
- package/.cursor/skills/remotion-best-practices/rules/gifs.md +138 -0
- package/.cursor/skills/remotion-best-practices/rules/images.md +130 -0
- package/.cursor/skills/remotion-best-practices/rules/import-srt-captions.md +67 -0
- package/.cursor/skills/remotion-best-practices/rules/lottie.md +68 -0
- package/.cursor/skills/remotion-best-practices/rules/measuring-dom-nodes.md +35 -0
- package/.cursor/skills/remotion-best-practices/rules/measuring-text.md +143 -0
- package/.cursor/skills/remotion-best-practices/rules/sequencing.md +106 -0
- package/.cursor/skills/remotion-best-practices/rules/tailwind.md +11 -0
- package/.cursor/skills/remotion-best-practices/rules/text-animations.md +20 -0
- package/.cursor/skills/remotion-best-practices/rules/timing.md +179 -0
- package/.cursor/skills/remotion-best-practices/rules/transcribe-captions.md +19 -0
- package/.cursor/skills/remotion-best-practices/rules/transitions.md +122 -0
- package/.cursor/skills/remotion-best-practices/rules/trimming.md +53 -0
- package/.cursor/skills/remotion-best-practices/rules/videos.md +171 -0
- package/.cursor/skills/skill-creator/LICENSE.txt +202 -0
- package/.cursor/skills/skill-creator/SKILL.md +356 -0
- package/.cursor/skills/skill-creator/references/output-patterns.md +82 -0
- package/.cursor/skills/skill-creator/references/workflows.md +28 -0
- package/.cursor/skills/skill-creator/scripts/init_skill.py +303 -0
- package/.cursor/skills/skill-creator/scripts/package_skill.py +110 -0
- package/.cursor/skills/skill-creator/scripts/quick_validate.py +95 -0
- package/.cursor/skills/ui-skills/SKILL.md +85 -0
- package/.cursor/skills/use-railway/SKILL.md +124 -0
- package/.cursor/skills/use-railway/references/configure.md +283 -0
- package/.cursor/skills/use-railway/references/deploy.md +184 -0
- package/.cursor/skills/use-railway/references/operate.md +159 -0
- package/.cursor/skills/use-railway/references/request.md +238 -0
- package/.cursor/skills/use-railway/references/setup.md +195 -0
- package/.cursor/skills/use-railway/scripts/railway-api.sh +41 -0
- package/.cursor/skills/web-design-guidelines/SKILL.md +39 -0
- package/.gemini/GEMINI.md +75 -0
- package/.gemini/antigravity/skills/better-auth/SKILL.md +204 -0
- package/.gemini/antigravity/skills/better-auth/references/advanced-features.md +553 -0
- package/.gemini/antigravity/skills/better-auth/references/database-integration.md +577 -0
- package/.gemini/antigravity/skills/better-auth/references/email-password-auth.md +416 -0
- package/.gemini/antigravity/skills/better-auth/references/oauth-providers.md +430 -0
- package/.gemini/antigravity/skills/better-auth/scripts/.coverage +0 -0
- package/.gemini/antigravity/skills/better-auth/scripts/better_auth_init.py +521 -0
- package/.gemini/antigravity/skills/better-auth/scripts/requirements.txt +15 -0
- package/.gemini/antigravity/skills/better-auth/scripts/tests/.coverage +0 -0
- package/.gemini/antigravity/skills/better-auth/scripts/tests/test_better_auth_init.py +421 -0
- package/.gemini/antigravity/skills/better-auth-best-practices/SKILL.md +166 -0
- package/.gemini/antigravity/skills/create-auth-skill/SKILL.md +214 -0
- package/.gemini/antigravity/skills/expo-react-native-performance/AGENTS.md +2515 -0
- package/.gemini/antigravity/skills/expo-react-native-performance/SKILL.md +118 -0
- package/.gemini/antigravity/skills/expo-react-native-performance/references/anim-interaction-manager.md +63 -0
- package/.gemini/antigravity/skills/expo-react-native-performance/references/anim-layout-animation.md +75 -0
- package/.gemini/antigravity/skills/expo-react-native-performance/references/anim-transform-not-dimensions.md +67 -0
- package/.gemini/antigravity/skills/expo-react-native-performance/references/anim-use-native-driver.md +55 -0
- package/.gemini/antigravity/skills/expo-react-native-performance/references/anim-use-reanimated.md +53 -0
- package/.gemini/antigravity/skills/expo-react-native-performance/references/asset-optimize-image-size.md +60 -0
- package/.gemini/antigravity/skills/expo-react-native-performance/references/asset-prefetch-images.md +60 -0
- package/.gemini/antigravity/skills/expo-react-native-performance/references/asset-recycling-key.md +57 -0
- package/.gemini/antigravity/skills/expo-react-native-performance/references/asset-use-expo-image.md +58 -0
- package/.gemini/antigravity/skills/expo-react-native-performance/references/asset-use-webp-format.md +50 -0
- package/.gemini/antigravity/skills/expo-react-native-performance/references/async-batch-api-calls.md +65 -0
- package/.gemini/antigravity/skills/expo-react-native-performance/references/async-cache-responses.md +54 -0
- package/.gemini/antigravity/skills/expo-react-native-performance/references/async-defer-await.md +56 -0
- package/.gemini/antigravity/skills/expo-react-native-performance/references/async-parallel-fetching.md +60 -0
- package/.gemini/antigravity/skills/expo-react-native-performance/references/async-refetch-on-focus.md +68 -0
- package/.gemini/antigravity/skills/expo-react-native-performance/references/list-estimated-item-size.md +52 -0
- package/.gemini/antigravity/skills/expo-react-native-performance/references/list-get-item-layout.md +64 -0
- package/.gemini/antigravity/skills/expo-react-native-performance/references/list-get-item-type.md +59 -0
- package/.gemini/antigravity/skills/expo-react-native-performance/references/list-memoize-items.md +59 -0
- package/.gemini/antigravity/skills/expo-react-native-performance/references/list-stable-render-item.md +61 -0
- package/.gemini/antigravity/skills/expo-react-native-performance/references/list-use-flashlist.md +56 -0
- package/.gemini/antigravity/skills/expo-react-native-performance/references/mem-abort-fetch.md +78 -0
- package/.gemini/antigravity/skills/expo-react-native-performance/references/mem-avoid-inline-objects.md +61 -0
- package/.gemini/antigravity/skills/expo-react-native-performance/references/mem-cleanup-subscriptions.md +63 -0
- package/.gemini/antigravity/skills/expo-react-native-performance/references/mem-clear-timers.md +63 -0
- package/.gemini/antigravity/skills/expo-react-native-performance/references/mem-limit-list-data.md +69 -0
- package/.gemini/antigravity/skills/expo-react-native-performance/references/platform-android-overdraw.md +63 -0
- package/.gemini/antigravity/skills/expo-react-native-performance/references/platform-android-proguard.md +63 -0
- package/.gemini/antigravity/skills/expo-react-native-performance/references/platform-conditional-render.md +81 -0
- package/.gemini/antigravity/skills/expo-react-native-performance/references/platform-ios-text-rendering.md +57 -0
- package/.gemini/antigravity/skills/expo-react-native-performance/references/rerender-derive-state.md +58 -0
- package/.gemini/antigravity/skills/expo-react-native-performance/references/rerender-functional-setstate.md +54 -0
- package/.gemini/antigravity/skills/expo-react-native-performance/references/rerender-lazy-state-init.md +55 -0
- package/.gemini/antigravity/skills/expo-react-native-performance/references/rerender-split-context.md +69 -0
- package/.gemini/antigravity/skills/expo-react-native-performance/references/rerender-use-callback-handlers.md +63 -0
- package/.gemini/antigravity/skills/expo-react-native-performance/references/rerender-use-memo-expensive.md +54 -0
- package/.gemini/antigravity/skills/expo-react-native-performance/references/startup-async-routes.md +63 -0
- package/.gemini/antigravity/skills/expo-react-native-performance/references/startup-cherry-pick-imports.md +45 -0
- package/.gemini/antigravity/skills/expo-react-native-performance/references/startup-enable-hermes.md +39 -0
- package/.gemini/antigravity/skills/expo-react-native-performance/references/startup-preload-assets.md +68 -0
- package/.gemini/antigravity/skills/expo-react-native-performance/references/startup-remove-console-logs.md +47 -0
- package/.gemini/antigravity/skills/expo-react-native-performance/references/startup-splash-screen-control.md +58 -0
- package/.gemini/antigravity/skills/express-rest-api/SKILL.md +249 -0
- package/.gemini/antigravity/skills/express-rest-api/assets/config.yaml +1 -0
- package/.gemini/antigravity/skills/express-rest-api/references/GUIDE.md +1 -0
- package/.gemini/antigravity/skills/express-rest-api/scripts/helper.py +3 -0
- package/.gemini/antigravity/skills/react-doctor/SKILL.md +41 -0
- package/.gemini/antigravity/skills/react-native-best-practices/POWER.md +145 -0
- package/.gemini/antigravity/skills/react-native-best-practices/SKILL.md +215 -0
- package/.gemini/antigravity/skills/react-native-best-practices/agents/openai.yaml +4 -0
- package/.gemini/antigravity/skills/react-native-best-practices/references/bundle-analyze-app.md +211 -0
- package/.gemini/antigravity/skills/react-native-best-practices/references/bundle-analyze-js.md +262 -0
- package/.gemini/antigravity/skills/react-native-best-practices/references/bundle-barrel-exports.md +248 -0
- package/.gemini/antigravity/skills/react-native-best-practices/references/bundle-code-splitting.md +224 -0
- package/.gemini/antigravity/skills/react-native-best-practices/references/bundle-hermes-mmap.md +167 -0
- package/.gemini/antigravity/skills/react-native-best-practices/references/bundle-library-size.md +177 -0
- package/.gemini/antigravity/skills/react-native-best-practices/references/bundle-native-assets.md +214 -0
- package/.gemini/antigravity/skills/react-native-best-practices/references/bundle-r8-android.md +225 -0
- package/.gemini/antigravity/skills/react-native-best-practices/references/bundle-tree-shaking.md +214 -0
- package/.gemini/antigravity/skills/react-native-best-practices/references/images/bundle-treemap-source-map-explorer.png +0 -0
- package/.gemini/antigravity/skills/react-native-best-practices/references/images/controlled-textinput-pingpong.png +0 -0
- package/.gemini/antigravity/skills/react-native-best-practices/references/images/devtools-flamegraph.png +0 -0
- package/.gemini/antigravity/skills/react-native-best-practices/references/images/emerge-xray-ios.png +0 -0
- package/.gemini/antigravity/skills/react-native-best-practices/references/images/expo-atlas-treemap.png +0 -0
- package/.gemini/antigravity/skills/react-native-best-practices/references/images/flashlight-flatlist-vs-flashlist.png +0 -0
- package/.gemini/antigravity/skills/react-native-best-practices/references/images/fps-drop-graph.png +0 -0
- package/.gemini/antigravity/skills/react-native-best-practices/references/images/memory-heap-snapshot.png +0 -0
- package/.gemini/antigravity/skills/react-native-best-practices/references/images/tti-warm-start-diagram.png +0 -0
- package/.gemini/antigravity/skills/react-native-best-practices/references/images/view-hierarchy-flattening.png +0 -0
- package/.gemini/antigravity/skills/react-native-best-practices/references/images/xcode-instruments-templates.png +0 -0
- package/.gemini/antigravity/skills/react-native-best-practices/references/images/xcode-thread-view.png +0 -0
- package/.gemini/antigravity/skills/react-native-best-practices/references/js-animations-reanimated.md +254 -0
- package/.gemini/antigravity/skills/react-native-best-practices/references/js-atomic-state.md +245 -0
- package/.gemini/antigravity/skills/react-native-best-practices/references/js-concurrent-react.md +236 -0
- package/.gemini/antigravity/skills/react-native-best-practices/references/js-lists-flatlist-flashlist.md +236 -0
- package/.gemini/antigravity/skills/react-native-best-practices/references/js-measure-fps.md +180 -0
- package/.gemini/antigravity/skills/react-native-best-practices/references/js-memory-leaks.md +205 -0
- package/.gemini/antigravity/skills/react-native-best-practices/references/js-profile-react.md +161 -0
- package/.gemini/antigravity/skills/react-native-best-practices/references/js-react-compiler.md +368 -0
- package/.gemini/antigravity/skills/react-native-best-practices/references/js-uncontrolled-components.md +216 -0
- package/.gemini/antigravity/skills/react-native-best-practices/references/native-android-16kb-alignment.md +113 -0
- package/.gemini/antigravity/skills/react-native-best-practices/references/native-measure-tti.md +262 -0
- package/.gemini/antigravity/skills/react-native-best-practices/references/native-memory-leaks.md +240 -0
- package/.gemini/antigravity/skills/react-native-best-practices/references/native-memory-patterns.md +274 -0
- package/.gemini/antigravity/skills/react-native-best-practices/references/native-platform-setup.md +110 -0
- package/.gemini/antigravity/skills/react-native-best-practices/references/native-profiling.md +176 -0
- package/.gemini/antigravity/skills/react-native-best-practices/references/native-sdks-over-polyfills.md +183 -0
- package/.gemini/antigravity/skills/react-native-best-practices/references/native-threading-model.md +234 -0
- package/.gemini/antigravity/skills/react-native-best-practices/references/native-turbo-modules.md +292 -0
- package/.gemini/antigravity/skills/react-native-best-practices/references/native-view-flattening.md +201 -0
- package/.gemini/antigravity/skills/react-native-expo/.claude-plugin/plugin.json +13 -0
- package/.gemini/antigravity/skills/react-native-expo/SKILL.md +1077 -0
- package/.gemini/antigravity/skills/react-native-expo/agents/expo-build.md +207 -0
- package/.gemini/antigravity/skills/react-native-expo/assets/css-features-cheatsheet.md +488 -0
- package/.gemini/antigravity/skills/react-native-expo/assets/example-template.txt +14 -0
- package/.gemini/antigravity/skills/react-native-expo/assets/new-arch-decision-tree.md +312 -0
- package/.gemini/antigravity/skills/react-native-expo/references/example-reference.md +26 -0
- package/.gemini/antigravity/skills/react-native-expo/references/expo-sdk-52-breaking.md +409 -0
- package/.gemini/antigravity/skills/react-native-expo/references/new-architecture-errors.md +501 -0
- package/.gemini/antigravity/skills/react-native-expo/references/react-19-migration.md +365 -0
- package/.gemini/antigravity/skills/react-native-expo/scripts/check-rn-version.sh +121 -0
- package/.gemini/antigravity/skills/react-native-expo/scripts/example-script.sh +15 -0
- package/.gemini/antigravity/skills/react-native-testing/SKILL.md +161 -0
- package/.gemini/antigravity/skills/react-native-testing/references/anti-patterns.md +287 -0
- package/.gemini/antigravity/skills/react-native-testing/references/api-reference-v13.md +572 -0
- package/.gemini/antigravity/skills/react-native-testing/references/api-reference-v14.md +574 -0
- package/.gemini/antigravity/skills/supabase-postgres-best-practices/AGENTS.md +68 -0
- package/.gemini/antigravity/skills/supabase-postgres-best-practices/CLAUDE.md +68 -0
- package/.gemini/antigravity/skills/supabase-postgres-best-practices/SKILL.md +64 -0
- package/.gemini/antigravity/skills/supabase-postgres-best-practices/references/advanced-full-text-search.md +55 -0
- package/.gemini/antigravity/skills/supabase-postgres-best-practices/references/advanced-jsonb-indexing.md +49 -0
- package/.gemini/antigravity/skills/supabase-postgres-best-practices/references/conn-idle-timeout.md +46 -0
- package/.gemini/antigravity/skills/supabase-postgres-best-practices/references/conn-limits.md +44 -0
- package/.gemini/antigravity/skills/supabase-postgres-best-practices/references/conn-pooling.md +41 -0
- package/.gemini/antigravity/skills/supabase-postgres-best-practices/references/conn-prepared-statements.md +46 -0
- package/.gemini/antigravity/skills/supabase-postgres-best-practices/references/data-batch-inserts.md +54 -0
- package/.gemini/antigravity/skills/supabase-postgres-best-practices/references/data-n-plus-one.md +53 -0
- package/.gemini/antigravity/skills/supabase-postgres-best-practices/references/data-pagination.md +50 -0
- package/.gemini/antigravity/skills/supabase-postgres-best-practices/references/data-upsert.md +50 -0
- package/.gemini/antigravity/skills/supabase-postgres-best-practices/references/lock-advisory.md +56 -0
- package/.gemini/antigravity/skills/supabase-postgres-best-practices/references/lock-deadlock-prevention.md +68 -0
- package/.gemini/antigravity/skills/supabase-postgres-best-practices/references/lock-short-transactions.md +50 -0
- package/.gemini/antigravity/skills/supabase-postgres-best-practices/references/lock-skip-locked.md +54 -0
- package/.gemini/antigravity/skills/supabase-postgres-best-practices/references/monitor-explain-analyze.md +45 -0
- package/.gemini/antigravity/skills/supabase-postgres-best-practices/references/monitor-pg-stat-statements.md +55 -0
- package/.gemini/antigravity/skills/supabase-postgres-best-practices/references/monitor-vacuum-analyze.md +55 -0
- package/.gemini/antigravity/skills/supabase-postgres-best-practices/references/query-composite-indexes.md +44 -0
- package/.gemini/antigravity/skills/supabase-postgres-best-practices/references/query-covering-indexes.md +40 -0
- package/.gemini/antigravity/skills/supabase-postgres-best-practices/references/query-index-types.md +48 -0
- package/.gemini/antigravity/skills/supabase-postgres-best-practices/references/query-missing-indexes.md +43 -0
- package/.gemini/antigravity/skills/supabase-postgres-best-practices/references/query-partial-indexes.md +45 -0
- package/.gemini/antigravity/skills/supabase-postgres-best-practices/references/schema-constraints.md +80 -0
- package/.gemini/antigravity/skills/supabase-postgres-best-practices/references/schema-data-types.md +46 -0
- package/.gemini/antigravity/skills/supabase-postgres-best-practices/references/schema-foreign-key-indexes.md +59 -0
- package/.gemini/antigravity/skills/supabase-postgres-best-practices/references/schema-lowercase-identifiers.md +55 -0
- package/.gemini/antigravity/skills/supabase-postgres-best-practices/references/schema-partitioning.md +55 -0
- package/.gemini/antigravity/skills/supabase-postgres-best-practices/references/schema-primary-keys.md +61 -0
- package/.gemini/antigravity/skills/supabase-postgres-best-practices/references/security-privileges.md +54 -0
- package/.gemini/antigravity/skills/supabase-postgres-best-practices/references/security-rls-basics.md +50 -0
- package/.gemini/antigravity/skills/supabase-postgres-best-practices/references/security-rls-performance.md +57 -0
- package/.gemini/antigravity/skills/ui-skills/SKILL.md +85 -0
- package/.gemini/antigravity/skills/vercel-react-best-practices/AGENTS.md +2934 -0
- package/.gemini/antigravity/skills/vercel-react-best-practices/SKILL.md +136 -0
- package/.gemini/antigravity/skills/vercel-react-best-practices/rules/advanced-event-handler-refs.md +55 -0
- package/.gemini/antigravity/skills/vercel-react-best-practices/rules/advanced-init-once.md +42 -0
- package/.gemini/antigravity/skills/vercel-react-best-practices/rules/advanced-use-latest.md +39 -0
- package/.gemini/antigravity/skills/vercel-react-best-practices/rules/async-api-routes.md +38 -0
- package/.gemini/antigravity/skills/vercel-react-best-practices/rules/async-defer-await.md +80 -0
- package/.gemini/antigravity/skills/vercel-react-best-practices/rules/async-dependencies.md +51 -0
- package/.gemini/antigravity/skills/vercel-react-best-practices/rules/async-parallel.md +28 -0
- package/.gemini/antigravity/skills/vercel-react-best-practices/rules/async-suspense-boundaries.md +99 -0
- package/.gemini/antigravity/skills/vercel-react-best-practices/rules/bundle-barrel-imports.md +59 -0
- package/.gemini/antigravity/skills/vercel-react-best-practices/rules/bundle-conditional.md +31 -0
- package/.gemini/antigravity/skills/vercel-react-best-practices/rules/bundle-defer-third-party.md +49 -0
- package/.gemini/antigravity/skills/vercel-react-best-practices/rules/bundle-dynamic-imports.md +35 -0
- package/.gemini/antigravity/skills/vercel-react-best-practices/rules/bundle-preload.md +50 -0
- package/.gemini/antigravity/skills/vercel-react-best-practices/rules/client-event-listeners.md +74 -0
- package/.gemini/antigravity/skills/vercel-react-best-practices/rules/client-localstorage-schema.md +71 -0
- package/.gemini/antigravity/skills/vercel-react-best-practices/rules/client-passive-event-listeners.md +48 -0
- package/.gemini/antigravity/skills/vercel-react-best-practices/rules/client-swr-dedup.md +56 -0
- package/.gemini/antigravity/skills/vercel-react-best-practices/rules/js-batch-dom-css.md +107 -0
- package/.gemini/antigravity/skills/vercel-react-best-practices/rules/js-cache-function-results.md +80 -0
- package/.gemini/antigravity/skills/vercel-react-best-practices/rules/js-cache-property-access.md +28 -0
- package/.gemini/antigravity/skills/vercel-react-best-practices/rules/js-cache-storage.md +70 -0
- package/.gemini/antigravity/skills/vercel-react-best-practices/rules/js-combine-iterations.md +32 -0
- package/.gemini/antigravity/skills/vercel-react-best-practices/rules/js-early-exit.md +50 -0
- package/.gemini/antigravity/skills/vercel-react-best-practices/rules/js-hoist-regexp.md +45 -0
- package/.gemini/antigravity/skills/vercel-react-best-practices/rules/js-index-maps.md +37 -0
- package/.gemini/antigravity/skills/vercel-react-best-practices/rules/js-length-check-first.md +49 -0
- package/.gemini/antigravity/skills/vercel-react-best-practices/rules/js-min-max-loop.md +82 -0
- package/.gemini/antigravity/skills/vercel-react-best-practices/rules/js-set-map-lookups.md +24 -0
- package/.gemini/antigravity/skills/vercel-react-best-practices/rules/js-tosorted-immutable.md +57 -0
- package/.gemini/antigravity/skills/vercel-react-best-practices/rules/rendering-activity.md +26 -0
- package/.gemini/antigravity/skills/vercel-react-best-practices/rules/rendering-animate-svg-wrapper.md +47 -0
- package/.gemini/antigravity/skills/vercel-react-best-practices/rules/rendering-conditional-render.md +40 -0
- package/.gemini/antigravity/skills/vercel-react-best-practices/rules/rendering-content-visibility.md +38 -0
- package/.gemini/antigravity/skills/vercel-react-best-practices/rules/rendering-hoist-jsx.md +46 -0
- package/.gemini/antigravity/skills/vercel-react-best-practices/rules/rendering-hydration-no-flicker.md +82 -0
- package/.gemini/antigravity/skills/vercel-react-best-practices/rules/rendering-hydration-suppress-warning.md +30 -0
- package/.gemini/antigravity/skills/vercel-react-best-practices/rules/rendering-svg-precision.md +28 -0
- package/.gemini/antigravity/skills/vercel-react-best-practices/rules/rendering-usetransition-loading.md +75 -0
- package/.gemini/antigravity/skills/vercel-react-best-practices/rules/rerender-defer-reads.md +39 -0
- package/.gemini/antigravity/skills/vercel-react-best-practices/rules/rerender-dependencies.md +45 -0
- package/.gemini/antigravity/skills/vercel-react-best-practices/rules/rerender-derived-state-no-effect.md +40 -0
- package/.gemini/antigravity/skills/vercel-react-best-practices/rules/rerender-derived-state.md +29 -0
- package/.gemini/antigravity/skills/vercel-react-best-practices/rules/rerender-functional-setstate.md +74 -0
- package/.gemini/antigravity/skills/vercel-react-best-practices/rules/rerender-lazy-state-init.md +58 -0
- package/.gemini/antigravity/skills/vercel-react-best-practices/rules/rerender-memo-with-default-value.md +38 -0
- package/.gemini/antigravity/skills/vercel-react-best-practices/rules/rerender-memo.md +44 -0
- package/.gemini/antigravity/skills/vercel-react-best-practices/rules/rerender-move-effect-to-event.md +45 -0
- package/.gemini/antigravity/skills/vercel-react-best-practices/rules/rerender-simple-expression-in-memo.md +35 -0
- package/.gemini/antigravity/skills/vercel-react-best-practices/rules/rerender-transitions.md +40 -0
- package/.gemini/antigravity/skills/vercel-react-best-practices/rules/rerender-use-ref-transient-values.md +73 -0
- package/.gemini/antigravity/skills/vercel-react-best-practices/rules/server-after-nonblocking.md +73 -0
- package/.gemini/antigravity/skills/vercel-react-best-practices/rules/server-auth-actions.md +96 -0
- package/.gemini/antigravity/skills/vercel-react-best-practices/rules/server-cache-lru.md +41 -0
- package/.gemini/antigravity/skills/vercel-react-best-practices/rules/server-cache-react.md +76 -0
- package/.gemini/antigravity/skills/vercel-react-best-practices/rules/server-dedup-props.md +65 -0
- package/.gemini/antigravity/skills/vercel-react-best-practices/rules/server-parallel-fetching.md +83 -0
- package/.gemini/antigravity/skills/vercel-react-best-practices/rules/server-serialization.md +38 -0
- package/.gemini/antigravity/skills/vercel-react-native-skills/AGENTS.md +2897 -0
- package/.gemini/antigravity/skills/vercel-react-native-skills/SKILL.md +121 -0
- package/.gemini/antigravity/skills/vercel-react-native-skills/rules/animation-derived-value.md +53 -0
- package/.gemini/antigravity/skills/vercel-react-native-skills/rules/animation-gesture-detector-press.md +95 -0
- package/.gemini/antigravity/skills/vercel-react-native-skills/rules/animation-gpu-properties.md +65 -0
- package/.gemini/antigravity/skills/vercel-react-native-skills/rules/design-system-compound-components.md +66 -0
- package/.gemini/antigravity/skills/vercel-react-native-skills/rules/fonts-config-plugin.md +71 -0
- package/.gemini/antigravity/skills/vercel-react-native-skills/rules/imports-design-system-folder.md +68 -0
- package/.gemini/antigravity/skills/vercel-react-native-skills/rules/js-hoist-intl.md +61 -0
- package/.gemini/antigravity/skills/vercel-react-native-skills/rules/list-performance-callbacks.md +44 -0
- package/.gemini/antigravity/skills/vercel-react-native-skills/rules/list-performance-function-references.md +132 -0
- package/.gemini/antigravity/skills/vercel-react-native-skills/rules/list-performance-images.md +53 -0
- package/.gemini/antigravity/skills/vercel-react-native-skills/rules/list-performance-inline-objects.md +97 -0
- package/.gemini/antigravity/skills/vercel-react-native-skills/rules/list-performance-item-expensive.md +94 -0
- package/.gemini/antigravity/skills/vercel-react-native-skills/rules/list-performance-item-memo.md +82 -0
- package/.gemini/antigravity/skills/vercel-react-native-skills/rules/list-performance-item-types.md +104 -0
- package/.gemini/antigravity/skills/vercel-react-native-skills/rules/list-performance-virtualize.md +67 -0
- package/.gemini/antigravity/skills/vercel-react-native-skills/rules/monorepo-native-deps-in-app.md +46 -0
- package/.gemini/antigravity/skills/vercel-react-native-skills/rules/monorepo-single-dependency-versions.md +63 -0
- package/.gemini/antigravity/skills/vercel-react-native-skills/rules/navigation-native-navigators.md +188 -0
- package/.gemini/antigravity/skills/vercel-react-native-skills/rules/react-compiler-destructure-functions.md +50 -0
- package/.gemini/antigravity/skills/vercel-react-native-skills/rules/react-compiler-reanimated-shared-values.md +48 -0
- package/.gemini/antigravity/skills/vercel-react-native-skills/rules/react-state-dispatcher.md +91 -0
- package/.gemini/antigravity/skills/vercel-react-native-skills/rules/react-state-fallback.md +56 -0
- package/.gemini/antigravity/skills/vercel-react-native-skills/rules/react-state-minimize.md +65 -0
- package/.gemini/antigravity/skills/vercel-react-native-skills/rules/rendering-no-falsy-and.md +74 -0
- package/.gemini/antigravity/skills/vercel-react-native-skills/rules/rendering-text-in-text-component.md +36 -0
- package/.gemini/antigravity/skills/vercel-react-native-skills/rules/scroll-position-no-state.md +82 -0
- package/.gemini/antigravity/skills/vercel-react-native-skills/rules/state-ground-truth.md +80 -0
- package/.gemini/antigravity/skills/vercel-react-native-skills/rules/ui-expo-image.md +66 -0
- package/.gemini/antigravity/skills/vercel-react-native-skills/rules/ui-image-gallery.md +104 -0
- package/.gemini/antigravity/skills/vercel-react-native-skills/rules/ui-measure-views.md +78 -0
- package/.gemini/antigravity/skills/vercel-react-native-skills/rules/ui-menus.md +174 -0
- package/.gemini/antigravity/skills/vercel-react-native-skills/rules/ui-native-modals.md +77 -0
- package/.gemini/antigravity/skills/vercel-react-native-skills/rules/ui-pressable.md +61 -0
- package/.gemini/antigravity/skills/vercel-react-native-skills/rules/ui-safe-area-scroll.md +65 -0
- package/.gemini/antigravity/skills/vercel-react-native-skills/rules/ui-scrollview-content-inset.md +45 -0
- package/.gemini/antigravity/skills/vercel-react-native-skills/rules/ui-styling.md +87 -0
- package/.gemini/antigravity/skills/web-design-guidelines/SKILL.md +39 -0
- package/.gemini/antigravity/skills/web-interface-guidelines/SKILL.md +179 -0
- package/.gemini/settings.json +5 -0
- package/.gemini/skills/agent-browser/SKILL.md +347 -0
- package/.gemini/skills/agent-browser/references/authentication.md +188 -0
- package/.gemini/skills/agent-browser/references/proxy-support.md +175 -0
- package/.gemini/skills/agent-browser/references/session-management.md +181 -0
- package/.gemini/skills/agent-browser/references/snapshot-refs.md +186 -0
- package/.gemini/skills/agent-browser/references/video-recording.md +162 -0
- package/.gemini/skills/agent-browser/templates/authenticated-session.sh +91 -0
- package/.gemini/skills/agent-browser/templates/capture-workflow.sh +68 -0
- package/.gemini/skills/agent-browser/templates/form-automation.sh +64 -0
- package/.gemini/skills/better-auth-best-practices/SKILL.md +166 -0
- package/.gemini/skills/create-auth-skill/SKILL.md +214 -0
- package/.gemini/skills/design-md/SKILL.md +172 -0
- package/.gemini/skills/design-md/examples/DESIGN.md +154 -0
- package/.gemini/skills/frontend-design/LICENSE.txt +177 -0
- package/.gemini/skills/frontend-design/SKILL.md +42 -0
- package/.gemini/skills/remotion-best-practices/SKILL.md +43 -0
- package/.gemini/skills/remotion-best-practices/rules/3d.md +86 -0
- package/.gemini/skills/remotion-best-practices/rules/animations.md +29 -0
- package/.gemini/skills/remotion-best-practices/rules/assets/charts-bar-chart.tsx +173 -0
- package/.gemini/skills/remotion-best-practices/rules/assets/text-animations-typewriter.tsx +100 -0
- package/.gemini/skills/remotion-best-practices/rules/assets/text-animations-word-highlight.tsx +108 -0
- package/.gemini/skills/remotion-best-practices/rules/assets.md +78 -0
- package/.gemini/skills/remotion-best-practices/rules/audio.md +172 -0
- package/.gemini/skills/remotion-best-practices/rules/calculate-metadata.md +104 -0
- package/.gemini/skills/remotion-best-practices/rules/can-decode.md +75 -0
- package/.gemini/skills/remotion-best-practices/rules/charts.md +58 -0
- package/.gemini/skills/remotion-best-practices/rules/compositions.md +146 -0
- package/.gemini/skills/remotion-best-practices/rules/display-captions.md +126 -0
- package/.gemini/skills/remotion-best-practices/rules/extract-frames.md +229 -0
- package/.gemini/skills/remotion-best-practices/rules/fonts.md +152 -0
- package/.gemini/skills/remotion-best-practices/rules/get-audio-duration.md +58 -0
- package/.gemini/skills/remotion-best-practices/rules/get-video-dimensions.md +68 -0
- package/.gemini/skills/remotion-best-practices/rules/get-video-duration.md +58 -0
- package/.gemini/skills/remotion-best-practices/rules/gifs.md +138 -0
- package/.gemini/skills/remotion-best-practices/rules/images.md +130 -0
- package/.gemini/skills/remotion-best-practices/rules/import-srt-captions.md +67 -0
- package/.gemini/skills/remotion-best-practices/rules/lottie.md +68 -0
- package/.gemini/skills/remotion-best-practices/rules/measuring-dom-nodes.md +35 -0
- package/.gemini/skills/remotion-best-practices/rules/measuring-text.md +143 -0
- package/.gemini/skills/remotion-best-practices/rules/sequencing.md +106 -0
- package/.gemini/skills/remotion-best-practices/rules/tailwind.md +11 -0
- package/.gemini/skills/remotion-best-practices/rules/text-animations.md +20 -0
- package/.gemini/skills/remotion-best-practices/rules/timing.md +179 -0
- package/.gemini/skills/remotion-best-practices/rules/transcribe-captions.md +19 -0
- package/.gemini/skills/remotion-best-practices/rules/transitions.md +122 -0
- package/.gemini/skills/remotion-best-practices/rules/trimming.md +53 -0
- package/.gemini/skills/remotion-best-practices/rules/videos.md +171 -0
- package/.gemini/skills/skill-creator/LICENSE.txt +202 -0
- package/.gemini/skills/skill-creator/SKILL.md +356 -0
- package/.gemini/skills/skill-creator/references/output-patterns.md +82 -0
- package/.gemini/skills/skill-creator/references/workflows.md +28 -0
- package/.gemini/skills/skill-creator/scripts/init_skill.py +303 -0
- package/.gemini/skills/skill-creator/scripts/package_skill.py +110 -0
- package/.gemini/skills/skill-creator/scripts/quick_validate.py +95 -0
- package/.gemini/skills/supabase-postgres-best-practices/AGENTS.md +1490 -0
- package/.gemini/skills/supabase-postgres-best-practices/SKILL.md +57 -0
- package/.gemini/skills/supabase-postgres-best-practices/rules/advanced-full-text-search.md +55 -0
- package/.gemini/skills/supabase-postgres-best-practices/rules/advanced-jsonb-indexing.md +49 -0
- package/.gemini/skills/supabase-postgres-best-practices/rules/conn-idle-timeout.md +46 -0
- package/.gemini/skills/supabase-postgres-best-practices/rules/conn-limits.md +44 -0
- package/.gemini/skills/supabase-postgres-best-practices/rules/conn-pooling.md +41 -0
- package/.gemini/skills/supabase-postgres-best-practices/rules/conn-prepared-statements.md +46 -0
- package/.gemini/skills/supabase-postgres-best-practices/rules/data-batch-inserts.md +54 -0
- package/.gemini/skills/supabase-postgres-best-practices/rules/data-n-plus-one.md +53 -0
- package/.gemini/skills/supabase-postgres-best-practices/rules/data-pagination.md +50 -0
- package/.gemini/skills/supabase-postgres-best-practices/rules/data-upsert.md +50 -0
- package/.gemini/skills/supabase-postgres-best-practices/rules/lock-advisory.md +56 -0
- package/.gemini/skills/supabase-postgres-best-practices/rules/lock-deadlock-prevention.md +68 -0
- package/.gemini/skills/supabase-postgres-best-practices/rules/lock-short-transactions.md +50 -0
- package/.gemini/skills/supabase-postgres-best-practices/rules/lock-skip-locked.md +54 -0
- package/.gemini/skills/supabase-postgres-best-practices/rules/monitor-explain-analyze.md +45 -0
- package/.gemini/skills/supabase-postgres-best-practices/rules/monitor-pg-stat-statements.md +55 -0
- package/.gemini/skills/supabase-postgres-best-practices/rules/monitor-vacuum-analyze.md +55 -0
- package/.gemini/skills/supabase-postgres-best-practices/rules/query-composite-indexes.md +44 -0
- package/.gemini/skills/supabase-postgres-best-practices/rules/query-covering-indexes.md +40 -0
- package/.gemini/skills/supabase-postgres-best-practices/rules/query-index-types.md +45 -0
- package/.gemini/skills/supabase-postgres-best-practices/rules/query-missing-indexes.md +43 -0
- package/.gemini/skills/supabase-postgres-best-practices/rules/query-partial-indexes.md +45 -0
- package/.gemini/skills/supabase-postgres-best-practices/rules/schema-data-types.md +46 -0
- package/.gemini/skills/supabase-postgres-best-practices/rules/schema-foreign-key-indexes.md +59 -0
- package/.gemini/skills/supabase-postgres-best-practices/rules/schema-lowercase-identifiers.md +55 -0
- package/.gemini/skills/supabase-postgres-best-practices/rules/schema-partitioning.md +55 -0
- package/.gemini/skills/supabase-postgres-best-practices/rules/schema-primary-keys.md +61 -0
- package/.gemini/skills/supabase-postgres-best-practices/rules/security-privileges.md +54 -0
- package/.gemini/skills/supabase-postgres-best-practices/rules/security-rls-basics.md +50 -0
- package/.gemini/skills/supabase-postgres-best-practices/rules/security-rls-performance.md +57 -0
- package/.gemini/skills/ui-skills/SKILL.md +85 -0
- package/.gemini/skills/web-design-guidelines/SKILL.md +39 -0
- package/.github/ISSUE_TEMPLATE.md +37 -0
- package/.github/PULL_REQUEST_TEMPLATE.md +33 -0
- package/.github/skills/agent-browser/SKILL.md +347 -0
- package/.github/skills/agent-browser/references/authentication.md +188 -0
- package/.github/skills/agent-browser/references/proxy-support.md +175 -0
- package/.github/skills/agent-browser/references/session-management.md +181 -0
- package/.github/skills/agent-browser/references/snapshot-refs.md +186 -0
- package/.github/skills/agent-browser/references/video-recording.md +162 -0
- package/.github/skills/agent-browser/templates/authenticated-session.sh +91 -0
- package/.github/skills/agent-browser/templates/capture-workflow.sh +68 -0
- package/.github/skills/agent-browser/templates/form-automation.sh +64 -0
- package/.github/skills/better-auth-best-practices/SKILL.md +166 -0
- package/.github/skills/design-md/SKILL.md +172 -0
- package/.github/skills/design-md/examples/DESIGN.md +154 -0
- package/.github/skills/frontend-design/LICENSE.txt +177 -0
- package/.github/skills/frontend-design/SKILL.md +42 -0
- package/.github/skills/remotion-best-practices/SKILL.md +43 -0
- package/.github/skills/remotion-best-practices/rules/3d.md +86 -0
- package/.github/skills/remotion-best-practices/rules/animations.md +29 -0
- package/.github/skills/remotion-best-practices/rules/assets/charts-bar-chart.tsx +173 -0
- package/.github/skills/remotion-best-practices/rules/assets/text-animations-typewriter.tsx +100 -0
- package/.github/skills/remotion-best-practices/rules/assets/text-animations-word-highlight.tsx +108 -0
- package/.github/skills/remotion-best-practices/rules/assets.md +78 -0
- package/.github/skills/remotion-best-practices/rules/audio.md +172 -0
- package/.github/skills/remotion-best-practices/rules/calculate-metadata.md +104 -0
- package/.github/skills/remotion-best-practices/rules/can-decode.md +75 -0
- package/.github/skills/remotion-best-practices/rules/charts.md +58 -0
- package/.github/skills/remotion-best-practices/rules/compositions.md +146 -0
- package/.github/skills/remotion-best-practices/rules/display-captions.md +126 -0
- package/.github/skills/remotion-best-practices/rules/extract-frames.md +229 -0
- package/.github/skills/remotion-best-practices/rules/fonts.md +152 -0
- package/.github/skills/remotion-best-practices/rules/get-audio-duration.md +58 -0
- package/.github/skills/remotion-best-practices/rules/get-video-dimensions.md +68 -0
- package/.github/skills/remotion-best-practices/rules/get-video-duration.md +58 -0
- package/.github/skills/remotion-best-practices/rules/gifs.md +138 -0
- package/.github/skills/remotion-best-practices/rules/images.md +130 -0
- package/.github/skills/remotion-best-practices/rules/import-srt-captions.md +67 -0
- package/.github/skills/remotion-best-practices/rules/lottie.md +68 -0
- package/.github/skills/remotion-best-practices/rules/measuring-dom-nodes.md +35 -0
- package/.github/skills/remotion-best-practices/rules/measuring-text.md +143 -0
- package/.github/skills/remotion-best-practices/rules/sequencing.md +106 -0
- package/.github/skills/remotion-best-practices/rules/tailwind.md +11 -0
- package/.github/skills/remotion-best-practices/rules/text-animations.md +20 -0
- package/.github/skills/remotion-best-practices/rules/timing.md +179 -0
- package/.github/skills/remotion-best-practices/rules/transcribe-captions.md +19 -0
- package/.github/skills/remotion-best-practices/rules/transitions.md +122 -0
- package/.github/skills/remotion-best-practices/rules/trimming.md +53 -0
- package/.github/skills/remotion-best-practices/rules/videos.md +171 -0
- package/.github/skills/skill-creator/LICENSE.txt +202 -0
- package/.github/skills/skill-creator/SKILL.md +356 -0
- package/.github/skills/skill-creator/references/output-patterns.md +82 -0
- package/.github/skills/skill-creator/references/workflows.md +28 -0
- package/.github/skills/skill-creator/scripts/init_skill.py +303 -0
- package/.github/skills/skill-creator/scripts/package_skill.py +110 -0
- package/.github/skills/skill-creator/scripts/quick_validate.py +95 -0
- package/.github/skills/supabase-postgres-best-practices/AGENTS.md +1490 -0
- package/.github/skills/supabase-postgres-best-practices/SKILL.md +57 -0
- package/.github/skills/supabase-postgres-best-practices/rules/advanced-full-text-search.md +55 -0
- package/.github/skills/supabase-postgres-best-practices/rules/advanced-jsonb-indexing.md +49 -0
- package/.github/skills/supabase-postgres-best-practices/rules/conn-idle-timeout.md +46 -0
- package/.github/skills/supabase-postgres-best-practices/rules/conn-limits.md +44 -0
- package/.github/skills/supabase-postgres-best-practices/rules/conn-pooling.md +41 -0
- package/.github/skills/supabase-postgres-best-practices/rules/conn-prepared-statements.md +46 -0
- package/.github/skills/supabase-postgres-best-practices/rules/data-batch-inserts.md +54 -0
- package/.github/skills/supabase-postgres-best-practices/rules/data-n-plus-one.md +53 -0
- package/.github/skills/supabase-postgres-best-practices/rules/data-pagination.md +50 -0
- package/.github/skills/supabase-postgres-best-practices/rules/data-upsert.md +50 -0
- package/.github/skills/supabase-postgres-best-practices/rules/lock-advisory.md +56 -0
- package/.github/skills/supabase-postgres-best-practices/rules/lock-deadlock-prevention.md +68 -0
- package/.github/skills/supabase-postgres-best-practices/rules/lock-short-transactions.md +50 -0
- package/.github/skills/supabase-postgres-best-practices/rules/lock-skip-locked.md +54 -0
- package/.github/skills/supabase-postgres-best-practices/rules/monitor-explain-analyze.md +45 -0
- package/.github/skills/supabase-postgres-best-practices/rules/monitor-pg-stat-statements.md +55 -0
- package/.github/skills/supabase-postgres-best-practices/rules/monitor-vacuum-analyze.md +55 -0
- package/.github/skills/supabase-postgres-best-practices/rules/query-composite-indexes.md +44 -0
- package/.github/skills/supabase-postgres-best-practices/rules/query-covering-indexes.md +40 -0
- package/.github/skills/supabase-postgres-best-practices/rules/query-index-types.md +45 -0
- package/.github/skills/supabase-postgres-best-practices/rules/query-missing-indexes.md +43 -0
- package/.github/skills/supabase-postgres-best-practices/rules/query-partial-indexes.md +45 -0
- package/.github/skills/supabase-postgres-best-practices/rules/schema-data-types.md +46 -0
- package/.github/skills/supabase-postgres-best-practices/rules/schema-foreign-key-indexes.md +59 -0
- package/.github/skills/supabase-postgres-best-practices/rules/schema-lowercase-identifiers.md +55 -0
- package/.github/skills/supabase-postgres-best-practices/rules/schema-partitioning.md +55 -0
- package/.github/skills/supabase-postgres-best-practices/rules/schema-primary-keys.md +61 -0
- package/.github/skills/supabase-postgres-best-practices/rules/security-privileges.md +54 -0
- package/.github/skills/supabase-postgres-best-practices/rules/security-rls-basics.md +50 -0
- package/.github/skills/supabase-postgres-best-practices/rules/security-rls-performance.md +57 -0
- package/.github/skills/ui-skills/SKILL.md +85 -0
- package/.github/skills/vercel-react-best-practices/AGENTS.md +2719 -0
- package/.github/skills/vercel-react-best-practices/SKILL.md +125 -0
- package/.github/skills/vercel-react-best-practices/rules/advanced-event-handler-refs.md +55 -0
- package/.github/skills/vercel-react-best-practices/rules/advanced-use-latest.md +39 -0
- package/.github/skills/vercel-react-best-practices/rules/async-api-routes.md +38 -0
- package/.github/skills/vercel-react-best-practices/rules/async-defer-await.md +80 -0
- package/.github/skills/vercel-react-best-practices/rules/async-dependencies.md +51 -0
- package/.github/skills/vercel-react-best-practices/rules/async-parallel.md +28 -0
- package/.github/skills/vercel-react-best-practices/rules/async-suspense-boundaries.md +99 -0
- package/.github/skills/vercel-react-best-practices/rules/bundle-barrel-imports.md +59 -0
- package/.github/skills/vercel-react-best-practices/rules/bundle-conditional.md +31 -0
- package/.github/skills/vercel-react-best-practices/rules/bundle-defer-third-party.md +49 -0
- package/.github/skills/vercel-react-best-practices/rules/bundle-dynamic-imports.md +35 -0
- package/.github/skills/vercel-react-best-practices/rules/bundle-preload.md +50 -0
- package/.github/skills/vercel-react-best-practices/rules/client-event-listeners.md +74 -0
- package/.github/skills/vercel-react-best-practices/rules/client-localstorage-schema.md +71 -0
- package/.github/skills/vercel-react-best-practices/rules/client-passive-event-listeners.md +48 -0
- package/.github/skills/vercel-react-best-practices/rules/client-swr-dedup.md +56 -0
- package/.github/skills/vercel-react-best-practices/rules/js-batch-dom-css.md +107 -0
- package/.github/skills/vercel-react-best-practices/rules/js-cache-function-results.md +80 -0
- package/.github/skills/vercel-react-best-practices/rules/js-cache-property-access.md +28 -0
- package/.github/skills/vercel-react-best-practices/rules/js-cache-storage.md +70 -0
- package/.github/skills/vercel-react-best-practices/rules/js-combine-iterations.md +32 -0
- package/.github/skills/vercel-react-best-practices/rules/js-early-exit.md +50 -0
- package/.github/skills/vercel-react-best-practices/rules/js-hoist-regexp.md +45 -0
- package/.github/skills/vercel-react-best-practices/rules/js-index-maps.md +37 -0
- package/.github/skills/vercel-react-best-practices/rules/js-length-check-first.md +49 -0
- package/.github/skills/vercel-react-best-practices/rules/js-min-max-loop.md +82 -0
- package/.github/skills/vercel-react-best-practices/rules/js-set-map-lookups.md +24 -0
- package/.github/skills/vercel-react-best-practices/rules/js-tosorted-immutable.md +57 -0
- package/.github/skills/vercel-react-best-practices/rules/rendering-activity.md +26 -0
- package/.github/skills/vercel-react-best-practices/rules/rendering-animate-svg-wrapper.md +47 -0
- package/.github/skills/vercel-react-best-practices/rules/rendering-conditional-render.md +40 -0
- package/.github/skills/vercel-react-best-practices/rules/rendering-content-visibility.md +38 -0
- package/.github/skills/vercel-react-best-practices/rules/rendering-hoist-jsx.md +46 -0
- package/.github/skills/vercel-react-best-practices/rules/rendering-hydration-no-flicker.md +82 -0
- package/.github/skills/vercel-react-best-practices/rules/rendering-svg-precision.md +28 -0
- package/.github/skills/vercel-react-best-practices/rules/rendering-usetransition-loading.md +75 -0
- package/.github/skills/vercel-react-best-practices/rules/rerender-defer-reads.md +39 -0
- package/.github/skills/vercel-react-best-practices/rules/rerender-dependencies.md +45 -0
- package/.github/skills/vercel-react-best-practices/rules/rerender-derived-state.md +29 -0
- package/.github/skills/vercel-react-best-practices/rules/rerender-functional-setstate.md +74 -0
- package/.github/skills/vercel-react-best-practices/rules/rerender-lazy-state-init.md +58 -0
- package/.github/skills/vercel-react-best-practices/rules/rerender-memo-with-default-value.md +38 -0
- package/.github/skills/vercel-react-best-practices/rules/rerender-memo.md +44 -0
- package/.github/skills/vercel-react-best-practices/rules/rerender-simple-expression-in-memo.md +35 -0
- package/.github/skills/vercel-react-best-practices/rules/rerender-transitions.md +40 -0
- package/.github/skills/vercel-react-best-practices/rules/server-after-nonblocking.md +73 -0
- package/.github/skills/vercel-react-best-practices/rules/server-auth-actions.md +96 -0
- package/.github/skills/vercel-react-best-practices/rules/server-cache-lru.md +41 -0
- package/.github/skills/vercel-react-best-practices/rules/server-cache-react.md +76 -0
- package/.github/skills/vercel-react-best-practices/rules/server-dedup-props.md +65 -0
- package/.github/skills/vercel-react-best-practices/rules/server-parallel-fetching.md +83 -0
- package/.github/skills/vercel-react-best-practices/rules/server-serialization.md +38 -0
- package/.github/skills/web-design-guidelines/SKILL.md +39 -0
- package/.github/workflows/dependency-review.yml +25 -0
- package/.github/workflows/npm-publish.yml +33 -0
- package/.github/workflows/validate.yml +48 -0
- package/LICENSE +21 -0
- package/README.md +160 -0
- package/bin/ai-config.mjs +9 -0
- package/configs.manifest.json +30 -0
- package/install.ps1 +18 -0
- package/install.sh +14 -0
- package/package.json +46 -0
- package/scripts/install.mjs +506 -0
- package/scripts/skills-catalog.mjs +31 -0
- package/templates/avvale-env.example +13 -0
|
@@ -0,0 +1,2515 @@
|
|
|
1
|
+
# Expo React Native
|
|
2
|
+
|
|
3
|
+
**Version 0.1.0**
|
|
4
|
+
Expo
|
|
5
|
+
February 2026
|
|
6
|
+
|
|
7
|
+
> **Note:** This document is for agents and LLMs working on Expo React Native codebases.
|
|
8
|
+
> Humans may also find it useful, but guidance here is optimized for automation
|
|
9
|
+
> and consistency by AI-assisted workflows.
|
|
10
|
+
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
## Abstract
|
|
14
|
+
|
|
15
|
+
Comprehensive performance optimization guide for Expo React Native applications, designed for AI agents and LLMs. Contains 42 rules across 8 categories, prioritized by impact from critical (app startup, list virtualization) to incremental (platform optimizations). Each rule includes detailed explanations, real-world examples comparing incorrect vs. correct implementations, and specific impact metrics to guide automated refactoring and code generation.
|
|
16
|
+
|
|
17
|
+
---
|
|
18
|
+
|
|
19
|
+
## Table of Contents
|
|
20
|
+
|
|
21
|
+
1. [App Startup & Bundle Size](#1-app-startup-bundle-size) — **CRITICAL**
|
|
22
|
+
- 1.1 [Control Splash Screen Visibility](#11-control-splash-screen-visibility)
|
|
23
|
+
- 1.2 [Enable Hermes JavaScript Engine](#12-enable-hermes-javascript-engine)
|
|
24
|
+
- 1.3 [Preload Critical Assets During Splash](#13-preload-critical-assets-during-splash)
|
|
25
|
+
- 1.4 [Remove Console Logs in Production](#14-remove-console-logs-in-production)
|
|
26
|
+
- 1.5 [Use Async Routes for Code Splitting](#15-use-async-routes-for-code-splitting)
|
|
27
|
+
- 1.6 [Use Direct Imports Instead of Barrel Files](#16-use-direct-imports-instead-of-barrel-files)
|
|
28
|
+
2. [List Virtualization](#2-list-virtualization) — **CRITICAL**
|
|
29
|
+
- 2.1 [Memoize List Item Components](#21-memoize-list-item-components)
|
|
30
|
+
- 2.2 [Provide Accurate estimatedItemSize](#22-provide-accurate-estimateditemsize)
|
|
31
|
+
- 2.3 [Provide getItemLayout for Fixed Heights](#23-provide-getitemlayout-for-fixed-heights)
|
|
32
|
+
- 2.4 [Stabilize renderItem with useCallback](#24-stabilize-renderitem-with-usecallback)
|
|
33
|
+
- 2.5 [Use FlashList Instead of FlatList](#25-use-flashlist-instead-of-flatlist)
|
|
34
|
+
- 2.6 [Use getItemType for Mixed Lists](#26-use-getitemtype-for-mixed-lists)
|
|
35
|
+
3. [Re-render Optimization](#3-re-render-optimization) — **HIGH**
|
|
36
|
+
- 3.1 [Derive State Instead of Syncing](#31-derive-state-instead-of-syncing)
|
|
37
|
+
- 3.2 [Memoize Expensive Computations with useMemo](#32-memoize-expensive-computations-with-usememo)
|
|
38
|
+
- 3.3 [Split Context by Update Frequency](#33-split-context-by-update-frequency)
|
|
39
|
+
- 3.4 [Stabilize Callbacks with useCallback](#34-stabilize-callbacks-with-usecallback)
|
|
40
|
+
- 3.5 [Use Functional setState Updates](#35-use-functional-setstate-updates)
|
|
41
|
+
- 3.6 [Use Lazy State Initialization](#36-use-lazy-state-initialization)
|
|
42
|
+
4. [Animation Performance](#4-animation-performance) — **HIGH**
|
|
43
|
+
- 4.1 [Animate Transform Instead of Dimensions](#41-animate-transform-instead-of-dimensions)
|
|
44
|
+
- 4.2 [Defer Heavy Work During Animations](#42-defer-heavy-work-during-animations)
|
|
45
|
+
- 4.3 [Enable Native Driver for Animations](#43-enable-native-driver-for-animations)
|
|
46
|
+
- 4.4 [Use LayoutAnimation for Simple Transitions](#44-use-layoutanimation-for-simple-transitions)
|
|
47
|
+
- 4.5 [Use Reanimated for Complex Animations](#45-use-reanimated-for-complex-animations)
|
|
48
|
+
5. [Image & Asset Loading](#5-image-asset-loading) — **MEDIUM-HIGH**
|
|
49
|
+
- 5.1 [Prefetch Images Before Display](#51-prefetch-images-before-display)
|
|
50
|
+
- 5.2 [Request Appropriately Sized Images](#52-request-appropriately-sized-images)
|
|
51
|
+
- 5.3 [Use expo-image for Image Loading](#53-use-expo-image-for-image-loading)
|
|
52
|
+
- 5.4 [Use recyclingKey in FlashList Images](#54-use-recyclingkey-in-flashlist-images)
|
|
53
|
+
- 5.5 [Use WebP Format for Images](#55-use-webp-format-for-images)
|
|
54
|
+
6. [Memory Management](#6-memory-management) — **MEDIUM**
|
|
55
|
+
- 6.1 [Abort Fetch Requests on Unmount](#61-abort-fetch-requests-on-unmount)
|
|
56
|
+
- 6.2 [Avoid Inline Objects and Arrays in Props](#62-avoid-inline-objects-and-arrays-in-props)
|
|
57
|
+
- 6.3 [Clean Up Subscriptions in useEffect](#63-clean-up-subscriptions-in-useeffect)
|
|
58
|
+
- 6.4 [Clear Timers on Unmount](#64-clear-timers-on-unmount)
|
|
59
|
+
- 6.5 [Limit List Data in Memory](#65-limit-list-data-in-memory)
|
|
60
|
+
7. [Async & Data Fetching](#7-async-data-fetching) — **MEDIUM**
|
|
61
|
+
- 7.1 [Batch Related API Calls](#71-batch-related-api-calls)
|
|
62
|
+
- 7.2 [Cache API Responses Locally](#72-cache-api-responses-locally)
|
|
63
|
+
- 7.3 [Defer await Until Value Needed](#73-defer-await-until-value-needed)
|
|
64
|
+
- 7.4 [Fetch Independent Data in Parallel](#74-fetch-independent-data-in-parallel)
|
|
65
|
+
- 7.5 [Refetch Data on Screen Focus](#75-refetch-data-on-screen-focus)
|
|
66
|
+
8. [Platform Optimizations](#8-platform-optimizations) — **LOW-MEDIUM**
|
|
67
|
+
- 8.1 [Enable ProGuard for Android Release](#81-enable-proguard-for-android-release)
|
|
68
|
+
- 8.2 [Optimize iOS Text Rendering](#82-optimize-ios-text-rendering)
|
|
69
|
+
- 8.3 [Reduce Android Overdraw](#83-reduce-android-overdraw)
|
|
70
|
+
- 8.4 [Use Platform-Specific Optimizations Conditionally](#84-use-platform-specific-optimizations-conditionally)
|
|
71
|
+
|
|
72
|
+
---
|
|
73
|
+
|
|
74
|
+
## 1. App Startup & Bundle Size
|
|
75
|
+
|
|
76
|
+
**Impact: CRITICAL**
|
|
77
|
+
|
|
78
|
+
Startup time determines first impression. Large bundles delay Time to Interactive. Hermes and bundle optimization can reduce startup by 40%.
|
|
79
|
+
|
|
80
|
+
### 1.1 Control Splash Screen Visibility
|
|
81
|
+
|
|
82
|
+
**Impact: CRITICAL (prevents white flash, enables asset preloading)**
|
|
83
|
+
|
|
84
|
+
Use `expo-splash-screen` to manually control when the splash screen hides. This allows you to preload critical assets and data before showing the app, preventing white flashes and incomplete UI states.
|
|
85
|
+
|
|
86
|
+
**Incorrect (splash hides before app is ready):**
|
|
87
|
+
|
|
88
|
+
```typescript
|
|
89
|
+
// App.tsx
|
|
90
|
+
export default function App() {
|
|
91
|
+
const [user, setUser] = useState<User | null>(null);
|
|
92
|
+
|
|
93
|
+
useEffect(() => {
|
|
94
|
+
fetchCurrentUser().then(setUser);
|
|
95
|
+
}, []);
|
|
96
|
+
|
|
97
|
+
// User sees loading spinner after splash disappears
|
|
98
|
+
if (!user) return <LoadingSpinner />;
|
|
99
|
+
|
|
100
|
+
return <MainApp user={user} />;
|
|
101
|
+
}
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
**Correct (splash stays until app is ready):**
|
|
105
|
+
|
|
106
|
+
```typescript
|
|
107
|
+
// App.tsx
|
|
108
|
+
import * as SplashScreen from 'expo-splash-screen';
|
|
109
|
+
|
|
110
|
+
SplashScreen.preventAutoHideAsync();
|
|
111
|
+
|
|
112
|
+
export default function App() {
|
|
113
|
+
const [user, setUser] = useState<User | null>(null);
|
|
114
|
+
|
|
115
|
+
useEffect(() => {
|
|
116
|
+
async function prepare() {
|
|
117
|
+
const userData = await fetchCurrentUser();
|
|
118
|
+
setUser(userData);
|
|
119
|
+
await SplashScreen.hideAsync();
|
|
120
|
+
}
|
|
121
|
+
prepare();
|
|
122
|
+
}, []);
|
|
123
|
+
|
|
124
|
+
if (!user) return null; // Splash still visible
|
|
125
|
+
|
|
126
|
+
return <MainApp user={user} />;
|
|
127
|
+
}
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
**Important:** Call `preventAutoHideAsync()` in module scope (outside components) to ensure it runs before the splash screen auto-hides.
|
|
131
|
+
|
|
132
|
+
Reference: [Expo SplashScreen](https://docs.expo.dev/versions/latest/sdk/splash-screen/)
|
|
133
|
+
|
|
134
|
+
### 1.2 Enable Hermes JavaScript Engine
|
|
135
|
+
|
|
136
|
+
**Impact: CRITICAL (40% faster startup, 30% less memory)**
|
|
137
|
+
|
|
138
|
+
Hermes is a JavaScript engine optimized for React Native that compiles JavaScript to bytecode ahead of time. This eliminates runtime compilation overhead, resulting in faster startup times and reduced memory usage.
|
|
139
|
+
|
|
140
|
+
**Incorrect (using JavaScriptCore, slower startup):**
|
|
141
|
+
|
|
142
|
+
```json
|
|
143
|
+
{
|
|
144
|
+
"expo": {
|
|
145
|
+
"jsEngine": "jsc"
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
**Correct (using Hermes, optimized for mobile):**
|
|
151
|
+
|
|
152
|
+
```json
|
|
153
|
+
{
|
|
154
|
+
"expo": {
|
|
155
|
+
"jsEngine": "hermes"
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
**Note:** Hermes is the default engine in Expo SDK 48+ and React Native 0.70+. Verify your app is using it by checking for `HermesInternal` in the JavaScript runtime.
|
|
161
|
+
|
|
162
|
+
**Benefits:**
|
|
163
|
+
- 40% faster app startup on average
|
|
164
|
+
- 30% reduction in memory usage
|
|
165
|
+
- Smaller app size (bytecode is more compact than JavaScript)
|
|
166
|
+
|
|
167
|
+
Reference: [Using Hermes](https://reactnative.dev/docs/hermes)
|
|
168
|
+
|
|
169
|
+
### 1.3 Preload Critical Assets During Splash
|
|
170
|
+
|
|
171
|
+
**Impact: CRITICAL (eliminates asset loading flicker)**
|
|
172
|
+
|
|
173
|
+
Load critical images and fonts while the splash screen is visible. This prevents UI flicker from missing assets and ensures a smooth transition from splash to app.
|
|
174
|
+
|
|
175
|
+
**Incorrect (assets load after app renders):**
|
|
176
|
+
|
|
177
|
+
```typescript
|
|
178
|
+
// App.tsx
|
|
179
|
+
export default function App() {
|
|
180
|
+
return (
|
|
181
|
+
<View>
|
|
182
|
+
{/* Image flickers in after component mounts */}
|
|
183
|
+
<Image source={require('./assets/logo.png')} />
|
|
184
|
+
<Text style={{ fontFamily: 'Inter' }}>Welcome</Text>
|
|
185
|
+
</View>
|
|
186
|
+
);
|
|
187
|
+
}
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
**Correct (assets preloaded during splash):**
|
|
191
|
+
|
|
192
|
+
```typescript
|
|
193
|
+
// App.tsx
|
|
194
|
+
import * as SplashScreen from 'expo-splash-screen';
|
|
195
|
+
import { Asset } from 'expo-asset';
|
|
196
|
+
import * as Font from 'expo-font';
|
|
197
|
+
|
|
198
|
+
SplashScreen.preventAutoHideAsync();
|
|
199
|
+
|
|
200
|
+
export default function App() {
|
|
201
|
+
const [assetsLoaded, setAssetsLoaded] = useState(false);
|
|
202
|
+
|
|
203
|
+
useEffect(() => {
|
|
204
|
+
async function loadAssets() {
|
|
205
|
+
await Promise.all([
|
|
206
|
+
Asset.loadAsync([
|
|
207
|
+
require('./assets/logo.png'),
|
|
208
|
+
require('./assets/background.png'),
|
|
209
|
+
]),
|
|
210
|
+
Font.loadAsync({
|
|
211
|
+
'Inter': require('./assets/fonts/Inter.ttf'),
|
|
212
|
+
}),
|
|
213
|
+
]);
|
|
214
|
+
setAssetsLoaded(true);
|
|
215
|
+
await SplashScreen.hideAsync();
|
|
216
|
+
}
|
|
217
|
+
loadAssets();
|
|
218
|
+
}, []);
|
|
219
|
+
|
|
220
|
+
if (!assetsLoaded) return null;
|
|
221
|
+
|
|
222
|
+
return <MainApp />;
|
|
223
|
+
}
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
**Benefits:**
|
|
227
|
+
- Zero asset-loading flicker
|
|
228
|
+
- Fonts available on first render
|
|
229
|
+
- Perceived instant app startup
|
|
230
|
+
|
|
231
|
+
Reference: [Expo Asset](https://docs.expo.dev/versions/latest/sdk/asset/)
|
|
232
|
+
|
|
233
|
+
### 1.4 Remove Console Logs in Production
|
|
234
|
+
|
|
235
|
+
**Impact: CRITICAL (eliminates JS thread bottleneck)**
|
|
236
|
+
|
|
237
|
+
Console.log statements cause JavaScript thread bottlenecks in production builds. Each log serializes data and sends it across the bridge, blocking execution. Use a Babel plugin to automatically strip console statements from production bundles.
|
|
238
|
+
|
|
239
|
+
**Incorrect (console.log in production code):**
|
|
240
|
+
|
|
241
|
+
```typescript
|
|
242
|
+
// api/users.ts
|
|
243
|
+
export async function fetchUsers(): Promise<User[]> {
|
|
244
|
+
const response = await fetch('/api/users');
|
|
245
|
+
const users = await response.json();
|
|
246
|
+
console.log('Fetched users:', users); // Blocks JS thread in production
|
|
247
|
+
return users;
|
|
248
|
+
}
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
**Correct (Babel strips console in production):**
|
|
252
|
+
|
|
253
|
+
```javascript
|
|
254
|
+
// babel.config.js
|
|
255
|
+
module.exports = function (api) {
|
|
256
|
+
api.cache(true);
|
|
257
|
+
return {
|
|
258
|
+
presets: ['babel-preset-expo'],
|
|
259
|
+
env: {
|
|
260
|
+
production: {
|
|
261
|
+
plugins: ['transform-remove-console'],
|
|
262
|
+
},
|
|
263
|
+
},
|
|
264
|
+
};
|
|
265
|
+
};
|
|
266
|
+
```
|
|
267
|
+
|
|
268
|
+
Install the plugin: `npm install babel-plugin-transform-remove-console --save-dev`
|
|
269
|
+
|
|
270
|
+
**When to keep console statements:**
|
|
271
|
+
- Use `console.error` for critical errors you need in crash reports
|
|
272
|
+
- Wrap debug logs in `__DEV__` checks instead of relying solely on Babel
|
|
273
|
+
|
|
274
|
+
Reference: [React Native Performance](https://reactnative.dev/docs/performance)
|
|
275
|
+
|
|
276
|
+
### 1.5 Use Async Routes for Code Splitting
|
|
277
|
+
|
|
278
|
+
**Impact: CRITICAL (30-50% smaller initial bundle)**
|
|
279
|
+
|
|
280
|
+
Expo Router supports async routes that split your bundle by route. Only the code needed for the current screen loads initially, deferring the rest until navigation. This dramatically reduces initial bundle size and startup time.
|
|
281
|
+
|
|
282
|
+
**Incorrect (all routes in initial bundle):**
|
|
283
|
+
|
|
284
|
+
```typescript
|
|
285
|
+
// app/_layout.tsx
|
|
286
|
+
import { Stack } from 'expo-router';
|
|
287
|
+
|
|
288
|
+
// All screen code loads at startup, even if never visited
|
|
289
|
+
export default function Layout() {
|
|
290
|
+
return (
|
|
291
|
+
<Stack>
|
|
292
|
+
<Stack.Screen name="index" />
|
|
293
|
+
<Stack.Screen name="settings" />
|
|
294
|
+
<Stack.Screen name="profile" />
|
|
295
|
+
<Stack.Screen name="admin" /> {/* Heavy admin code loaded for all users */}
|
|
296
|
+
</Stack>
|
|
297
|
+
);
|
|
298
|
+
}
|
|
299
|
+
```
|
|
300
|
+
|
|
301
|
+
**Correct (enable async routes in Metro config):**
|
|
302
|
+
|
|
303
|
+
```javascript
|
|
304
|
+
// metro.config.js
|
|
305
|
+
const { getDefaultConfig } = require('expo/metro-config');
|
|
306
|
+
|
|
307
|
+
const config = getDefaultConfig(__dirname);
|
|
308
|
+
|
|
309
|
+
config.transformer = {
|
|
310
|
+
...config.transformer,
|
|
311
|
+
asyncRequireModulePath: require.resolve('expo-router/async-require'),
|
|
312
|
+
};
|
|
313
|
+
|
|
314
|
+
module.exports = config;
|
|
315
|
+
```
|
|
316
|
+
|
|
317
|
+
```json
|
|
318
|
+
// app.json
|
|
319
|
+
{
|
|
320
|
+
"expo": {
|
|
321
|
+
"experiments": {
|
|
322
|
+
"asyncRoutes": true
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
```
|
|
327
|
+
|
|
328
|
+
**How it works:**
|
|
329
|
+
- Each route file becomes a separate bundle chunk
|
|
330
|
+
- Chunks load on-demand when user navigates to that route
|
|
331
|
+
- Initial bundle contains only the entry route
|
|
332
|
+
|
|
333
|
+
Reference: [Expo Router Async Routes](https://docs.expo.dev/router/reference/async-routes/)
|
|
334
|
+
|
|
335
|
+
### 1.6 Use Direct Imports Instead of Barrel Files
|
|
336
|
+
|
|
337
|
+
**Impact: CRITICAL (reduces bundle by 100KB-1MB per library)**
|
|
338
|
+
|
|
339
|
+
Metro bundler doesn't tree-shake effectively. Importing from barrel files (index.js) pulls in entire libraries. Always import directly from the specific module path to include only what you need.
|
|
340
|
+
|
|
341
|
+
**Incorrect (imports entire lodash library):**
|
|
342
|
+
|
|
343
|
+
```typescript
|
|
344
|
+
// utils/data.ts
|
|
345
|
+
import { debounce, throttle } from 'lodash';
|
|
346
|
+
// Bundles all 600KB of lodash even though you use 2 functions
|
|
347
|
+
|
|
348
|
+
export const debouncedSearch = debounce(searchUsers, 300);
|
|
349
|
+
export const throttledScroll = throttle(handleScroll, 100);
|
|
350
|
+
```
|
|
351
|
+
|
|
352
|
+
**Correct (imports only used functions):**
|
|
353
|
+
|
|
354
|
+
```typescript
|
|
355
|
+
// utils/data.ts
|
|
356
|
+
import debounce from 'lodash/debounce';
|
|
357
|
+
import throttle from 'lodash/throttle';
|
|
358
|
+
// Bundles only ~2KB for the two functions
|
|
359
|
+
|
|
360
|
+
export const debouncedSearch = debounce(searchUsers, 300);
|
|
361
|
+
export const throttledScroll = throttle(handleScroll, 100);
|
|
362
|
+
```
|
|
363
|
+
|
|
364
|
+
**Common libraries requiring cherry-picking:**
|
|
365
|
+
|
|
366
|
+
| Library | Bad Import | Good Import |
|
|
367
|
+
|---------|------------|-------------|
|
|
368
|
+
| lodash | `from 'lodash'` | `from 'lodash/debounce'` |
|
|
369
|
+
| date-fns | `from 'date-fns'` | `from 'date-fns/format'` |
|
|
370
|
+
| @expo/vector-icons | `from '@expo/vector-icons'` | `from '@expo/vector-icons/Ionicons'` |
|
|
371
|
+
|
|
372
|
+
**Alternative:** Use `babel-plugin-lodash` or `babel-plugin-date-fns` for automatic transforms.
|
|
373
|
+
|
|
374
|
+
Reference: [Callstack Bundle Optimization](https://www.callstack.com/blog/optimize-react-native-apps-javascript-bundle)
|
|
375
|
+
|
|
376
|
+
---
|
|
377
|
+
|
|
378
|
+
## 2. List Virtualization
|
|
379
|
+
|
|
380
|
+
**Impact: CRITICAL**
|
|
381
|
+
|
|
382
|
+
Lists are the #1 performance killer in mobile apps. FlashList achieves 5-10× better FPS than FlatList on Android through view recycling.
|
|
383
|
+
|
|
384
|
+
### 2.1 Memoize List Item Components
|
|
385
|
+
|
|
386
|
+
**Impact: CRITICAL (prevents re-render of unchanged items)**
|
|
387
|
+
|
|
388
|
+
Wrap list item components in `React.memo()` to prevent re-rendering when their props haven't changed. Without memoization, all visible items re-render whenever the parent component updates.
|
|
389
|
+
|
|
390
|
+
**Incorrect (item re-renders on every list update):**
|
|
391
|
+
|
|
392
|
+
```typescript
|
|
393
|
+
// components/ProductCard.tsx
|
|
394
|
+
interface ProductCardProps {
|
|
395
|
+
product: Product;
|
|
396
|
+
onPress: (id: string) => void;
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
export function ProductCard({ product, onPress }: ProductCardProps) {
|
|
400
|
+
return (
|
|
401
|
+
<Pressable onPress={() => onPress(product.id)}>
|
|
402
|
+
<Image source={{ uri: product.imageUrl }} style={styles.image} />
|
|
403
|
+
<Text>{product.name}</Text>
|
|
404
|
+
<Text>${product.price}</Text>
|
|
405
|
+
</Pressable>
|
|
406
|
+
);
|
|
407
|
+
}
|
|
408
|
+
// Every card re-renders when any card changes
|
|
409
|
+
```
|
|
410
|
+
|
|
411
|
+
**Correct (memoized item only re-renders on prop change):**
|
|
412
|
+
|
|
413
|
+
```typescript
|
|
414
|
+
// components/ProductCard.tsx
|
|
415
|
+
interface ProductCardProps {
|
|
416
|
+
product: Product;
|
|
417
|
+
onPress: (id: string) => void;
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
export const ProductCard = memo(function ProductCard({
|
|
421
|
+
product,
|
|
422
|
+
onPress,
|
|
423
|
+
}: ProductCardProps) {
|
|
424
|
+
return (
|
|
425
|
+
<Pressable onPress={() => onPress(product.id)}>
|
|
426
|
+
<Image source={{ uri: product.imageUrl }} style={styles.image} />
|
|
427
|
+
<Text>{product.name}</Text>
|
|
428
|
+
<Text>${product.price}</Text>
|
|
429
|
+
</Pressable>
|
|
430
|
+
);
|
|
431
|
+
});
|
|
432
|
+
// Only re-renders if product or onPress reference changes
|
|
433
|
+
```
|
|
434
|
+
|
|
435
|
+
**Important:** Ensure `onPress` is stable (use `useCallback`) or memo won't help since the function reference changes every render.
|
|
436
|
+
|
|
437
|
+
Reference: [React.memo](https://react.dev/reference/react/memo)
|
|
438
|
+
|
|
439
|
+
### 2.2 Provide Accurate estimatedItemSize
|
|
440
|
+
|
|
441
|
+
**Impact: CRITICAL (prevents blank cells during fast scroll)**
|
|
442
|
+
|
|
443
|
+
FlashList uses `estimatedItemSize` to calculate how many items to render initially and during scroll. An inaccurate estimate causes blank cells or excessive memory usage. Measure your actual item height and provide it.
|
|
444
|
+
|
|
445
|
+
**Incorrect (default or guessed estimate):**
|
|
446
|
+
|
|
447
|
+
```typescript
|
|
448
|
+
// screens/MessageList.tsx
|
|
449
|
+
export function MessageList({ messages }: Props) {
|
|
450
|
+
return (
|
|
451
|
+
<FlashList
|
|
452
|
+
data={messages}
|
|
453
|
+
renderItem={({ item }) => <MessageBubble message={item} />}
|
|
454
|
+
estimatedItemSize={50} // Wrong: actual messages are ~150px tall
|
|
455
|
+
/>
|
|
456
|
+
// Blank cells appear during fast scrolling
|
|
457
|
+
);
|
|
458
|
+
}
|
|
459
|
+
```
|
|
460
|
+
|
|
461
|
+
**Correct (measured average height):**
|
|
462
|
+
|
|
463
|
+
```typescript
|
|
464
|
+
// screens/MessageList.tsx
|
|
465
|
+
export function MessageList({ messages }: Props) {
|
|
466
|
+
return (
|
|
467
|
+
<FlashList
|
|
468
|
+
data={messages}
|
|
469
|
+
renderItem={({ item }) => <MessageBubble message={item} />}
|
|
470
|
+
estimatedItemSize={150} // Measured from actual rendered items
|
|
471
|
+
/>
|
|
472
|
+
// Smooth scrolling with no blank cells
|
|
473
|
+
);
|
|
474
|
+
}
|
|
475
|
+
```
|
|
476
|
+
|
|
477
|
+
**How to measure:**
|
|
478
|
+
1. Add `onLayout` to your list item temporarily
|
|
479
|
+
2. Log the `event.nativeEvent.layout.height`
|
|
480
|
+
3. Average across multiple items
|
|
481
|
+
4. Use the median value for varied-height lists
|
|
482
|
+
|
|
483
|
+
**For mixed content:** Use the average height. FlashList v2 removes the need for estimates entirely.
|
|
484
|
+
|
|
485
|
+
Reference: [FlashList estimatedItemSize](https://shopify.github.io/flash-list/docs/fundamentals/performant-components)
|
|
486
|
+
|
|
487
|
+
### 2.3 Provide getItemLayout for Fixed Heights
|
|
488
|
+
|
|
489
|
+
**Impact: CRITICAL (eliminates async layout calculation)**
|
|
490
|
+
|
|
491
|
+
When all list items have the same height, provide `getItemLayout` to skip asynchronous measurement. FlatList/FlashList can instantly calculate scroll position without rendering off-screen items.
|
|
492
|
+
|
|
493
|
+
**Incorrect (FlatList measures each item):**
|
|
494
|
+
|
|
495
|
+
```typescript
|
|
496
|
+
// screens/NotificationList.tsx
|
|
497
|
+
const ITEM_HEIGHT = 72;
|
|
498
|
+
|
|
499
|
+
export function NotificationList({ notifications }: Props) {
|
|
500
|
+
return (
|
|
501
|
+
<FlatList
|
|
502
|
+
data={notifications}
|
|
503
|
+
renderItem={({ item }) => (
|
|
504
|
+
<NotificationRow notification={item} style={{ height: ITEM_HEIGHT }} />
|
|
505
|
+
)}
|
|
506
|
+
keyExtractor={(item) => item.id}
|
|
507
|
+
/>
|
|
508
|
+
// FlatList renders items to measure them, causing scroll jank
|
|
509
|
+
);
|
|
510
|
+
}
|
|
511
|
+
```
|
|
512
|
+
|
|
513
|
+
**Correct (skip measurement with getItemLayout):**
|
|
514
|
+
|
|
515
|
+
```typescript
|
|
516
|
+
// screens/NotificationList.tsx
|
|
517
|
+
const ITEM_HEIGHT = 72;
|
|
518
|
+
|
|
519
|
+
export function NotificationList({ notifications }: Props) {
|
|
520
|
+
const getItemLayout = useCallback(
|
|
521
|
+
(_: any, index: number) => ({
|
|
522
|
+
length: ITEM_HEIGHT,
|
|
523
|
+
offset: ITEM_HEIGHT * index,
|
|
524
|
+
index,
|
|
525
|
+
}),
|
|
526
|
+
[]
|
|
527
|
+
);
|
|
528
|
+
|
|
529
|
+
return (
|
|
530
|
+
<FlatList
|
|
531
|
+
data={notifications}
|
|
532
|
+
renderItem={({ item }) => (
|
|
533
|
+
<NotificationRow notification={item} style={{ height: ITEM_HEIGHT }} />
|
|
534
|
+
)}
|
|
535
|
+
keyExtractor={(item) => item.id}
|
|
536
|
+
getItemLayout={getItemLayout}
|
|
537
|
+
/>
|
|
538
|
+
// Instant scroll position calculation, no measurement needed
|
|
539
|
+
);
|
|
540
|
+
}
|
|
541
|
+
```
|
|
542
|
+
|
|
543
|
+
**When NOT to use:** Items with dynamic heights (text wrapping, images). Use FlashList with `estimatedItemSize` instead.
|
|
544
|
+
|
|
545
|
+
Reference: [Optimizing FlatList](https://reactnative.dev/docs/optimizing-flatlist-configuration)
|
|
546
|
+
|
|
547
|
+
### 2.4 Stabilize renderItem with useCallback
|
|
548
|
+
|
|
549
|
+
**Impact: CRITICAL (prevents full list re-render on parent update)**
|
|
550
|
+
|
|
551
|
+
Defining `renderItem` inline creates a new function on every render, causing FlashList/FlatList to think the renderer changed and re-render all visible items. Extract and memoize the render function.
|
|
552
|
+
|
|
553
|
+
**Incorrect (inline renderItem recreated every render):**
|
|
554
|
+
|
|
555
|
+
```typescript
|
|
556
|
+
// screens/ContactList.tsx
|
|
557
|
+
export function ContactList({ contacts }: Props) {
|
|
558
|
+
const [searchQuery, setSearchQuery] = useState('');
|
|
559
|
+
|
|
560
|
+
return (
|
|
561
|
+
<>
|
|
562
|
+
<SearchInput value={searchQuery} onChangeText={setSearchQuery} />
|
|
563
|
+
<FlashList
|
|
564
|
+
data={filteredContacts}
|
|
565
|
+
renderItem={({ item }) => (
|
|
566
|
+
<ContactRow contact={item} />
|
|
567
|
+
)} // New function on every keystroke = all rows re-render
|
|
568
|
+
estimatedItemSize={60}
|
|
569
|
+
/>
|
|
570
|
+
</>
|
|
571
|
+
);
|
|
572
|
+
}
|
|
573
|
+
```
|
|
574
|
+
|
|
575
|
+
**Correct (stable renderItem with useCallback):**
|
|
576
|
+
|
|
577
|
+
```typescript
|
|
578
|
+
// screens/ContactList.tsx
|
|
579
|
+
export function ContactList({ contacts }: Props) {
|
|
580
|
+
const [searchQuery, setSearchQuery] = useState('');
|
|
581
|
+
|
|
582
|
+
const renderContact = useCallback(
|
|
583
|
+
({ item }: { item: Contact }) => <ContactRow contact={item} />,
|
|
584
|
+
[]
|
|
585
|
+
);
|
|
586
|
+
|
|
587
|
+
return (
|
|
588
|
+
<>
|
|
589
|
+
<SearchInput value={searchQuery} onChangeText={setSearchQuery} />
|
|
590
|
+
<FlashList
|
|
591
|
+
data={filteredContacts}
|
|
592
|
+
renderItem={renderContact}
|
|
593
|
+
estimatedItemSize={60}
|
|
594
|
+
/>
|
|
595
|
+
</>
|
|
596
|
+
);
|
|
597
|
+
}
|
|
598
|
+
```
|
|
599
|
+
|
|
600
|
+
**Important:** If `renderItem` uses callbacks like `onPress`, include them in the dependency array or use functional updates to avoid stale closures.
|
|
601
|
+
|
|
602
|
+
Reference: [Optimizing FlatList](https://reactnative.dev/docs/optimizing-flatlist-configuration)
|
|
603
|
+
|
|
604
|
+
### 2.5 Use FlashList Instead of FlatList
|
|
605
|
+
|
|
606
|
+
**Impact: CRITICAL (5-10× better FPS on Android)**
|
|
607
|
+
|
|
608
|
+
FlashList uses view recycling instead of creating new views for each item. This dramatically reduces memory allocation and improves scroll performance, especially on low-end Android devices where FlatList struggles.
|
|
609
|
+
|
|
610
|
+
**Incorrect (FlatList recreates views on scroll):**
|
|
611
|
+
|
|
612
|
+
```typescript
|
|
613
|
+
// screens/ProductList.tsx
|
|
614
|
+
import { FlatList } from 'react-native';
|
|
615
|
+
|
|
616
|
+
export function ProductList({ products }: Props) {
|
|
617
|
+
return (
|
|
618
|
+
<FlatList
|
|
619
|
+
data={products}
|
|
620
|
+
renderItem={({ item }) => <ProductCard product={item} />}
|
|
621
|
+
keyExtractor={(item) => item.id}
|
|
622
|
+
/>
|
|
623
|
+
// Each scroll creates/destroys views, causing jank on Android
|
|
624
|
+
);
|
|
625
|
+
}
|
|
626
|
+
```
|
|
627
|
+
|
|
628
|
+
**Correct (FlashList recycles views):**
|
|
629
|
+
|
|
630
|
+
```typescript
|
|
631
|
+
// screens/ProductList.tsx
|
|
632
|
+
import { FlashList } from '@shopify/flash-list';
|
|
633
|
+
|
|
634
|
+
export function ProductList({ products }: Props) {
|
|
635
|
+
return (
|
|
636
|
+
<FlashList
|
|
637
|
+
data={products}
|
|
638
|
+
renderItem={({ item }) => <ProductCard product={item} />}
|
|
639
|
+
keyExtractor={(item) => item.id}
|
|
640
|
+
estimatedItemSize={120}
|
|
641
|
+
/>
|
|
642
|
+
// Same views recycled, smooth 60 FPS scrolling
|
|
643
|
+
);
|
|
644
|
+
}
|
|
645
|
+
```
|
|
646
|
+
|
|
647
|
+
**Migration:** FlashList is API-compatible with FlatList. Change the import and add `estimatedItemSize`.
|
|
648
|
+
|
|
649
|
+
**Performance gains:**
|
|
650
|
+
- 5× faster UI thread FPS on low-end Android
|
|
651
|
+
- 10× faster JS thread FPS
|
|
652
|
+
- 32% less CPU usage
|
|
653
|
+
|
|
654
|
+
Reference: [FlashList](https://shopify.github.io/flash-list/)
|
|
655
|
+
|
|
656
|
+
### 2.6 Use getItemType for Mixed Lists
|
|
657
|
+
|
|
658
|
+
**Impact: CRITICAL (50% better recycling efficiency)**
|
|
659
|
+
|
|
660
|
+
When a list contains different component types (headers, items, ads), FlashList can only recycle views of the same type. Provide `getItemType` to help FlashList maintain separate recycling pools for each type.
|
|
661
|
+
|
|
662
|
+
**Incorrect (no type differentiation):**
|
|
663
|
+
|
|
664
|
+
```typescript
|
|
665
|
+
// screens/Feed.tsx
|
|
666
|
+
type FeedItem = { type: 'post' | 'ad' | 'header'; data: any };
|
|
667
|
+
|
|
668
|
+
export function Feed({ items }: { items: FeedItem[] }) {
|
|
669
|
+
return (
|
|
670
|
+
<FlashList
|
|
671
|
+
data={items}
|
|
672
|
+
renderItem={({ item }) => {
|
|
673
|
+
if (item.type === 'header') return <SectionHeader data={item.data} />;
|
|
674
|
+
if (item.type === 'ad') return <AdBanner data={item.data} />;
|
|
675
|
+
return <PostCard data={item.data} />;
|
|
676
|
+
}}
|
|
677
|
+
estimatedItemSize={200}
|
|
678
|
+
/>
|
|
679
|
+
// FlashList can't recycle: header view becomes post view = layout thrashing
|
|
680
|
+
);
|
|
681
|
+
}
|
|
682
|
+
```
|
|
683
|
+
|
|
684
|
+
**Correct (typed recycling pools):**
|
|
685
|
+
|
|
686
|
+
```typescript
|
|
687
|
+
// screens/Feed.tsx
|
|
688
|
+
type FeedItem = { type: 'post' | 'ad' | 'header'; data: any };
|
|
689
|
+
|
|
690
|
+
export function Feed({ items }: { items: FeedItem[] }) {
|
|
691
|
+
return (
|
|
692
|
+
<FlashList
|
|
693
|
+
data={items}
|
|
694
|
+
renderItem={({ item }) => {
|
|
695
|
+
if (item.type === 'header') return <SectionHeader data={item.data} />;
|
|
696
|
+
if (item.type === 'ad') return <AdBanner data={item.data} />;
|
|
697
|
+
return <PostCard data={item.data} />;
|
|
698
|
+
}}
|
|
699
|
+
getItemType={(item) => item.type}
|
|
700
|
+
estimatedItemSize={200}
|
|
701
|
+
/>
|
|
702
|
+
// Posts recycle into posts, headers into headers
|
|
703
|
+
);
|
|
704
|
+
}
|
|
705
|
+
```
|
|
706
|
+
|
|
707
|
+
**Result:** Each type maintains its own recycling pool, preventing expensive layout recalculations when items of different heights are recycled.
|
|
708
|
+
|
|
709
|
+
Reference: [FlashList getItemType](https://shopify.github.io/flash-list/docs/fundamentals/performant-components)
|
|
710
|
+
|
|
711
|
+
---
|
|
712
|
+
|
|
713
|
+
## 3. Re-render Optimization
|
|
714
|
+
|
|
715
|
+
**Impact: HIGH**
|
|
716
|
+
|
|
717
|
+
Unnecessary re-renders cascade through component trees, blocking the JS thread and causing dropped frames during interactions.
|
|
718
|
+
|
|
719
|
+
### 3.1 Derive State Instead of Syncing
|
|
720
|
+
|
|
721
|
+
**Impact: HIGH (eliminates redundant state and sync bugs)**
|
|
722
|
+
|
|
723
|
+
When one value can be computed from another, derive it during render instead of storing it in separate state. Synced state causes extra renders and can get out of sync.
|
|
724
|
+
|
|
725
|
+
**Incorrect (synced state, double render):**
|
|
726
|
+
|
|
727
|
+
```typescript
|
|
728
|
+
// screens/ProductDetails.tsx
|
|
729
|
+
export function ProductDetails({ product }: Props) {
|
|
730
|
+
const [quantity, setQuantity] = useState(1);
|
|
731
|
+
const [totalPrice, setTotalPrice] = useState(product.price);
|
|
732
|
+
|
|
733
|
+
useEffect(() => {
|
|
734
|
+
setTotalPrice(product.price * quantity); // Causes second render
|
|
735
|
+
}, [product.price, quantity]);
|
|
736
|
+
|
|
737
|
+
return (
|
|
738
|
+
<View>
|
|
739
|
+
<QuantityPicker value={quantity} onChange={setQuantity} />
|
|
740
|
+
<Text>Total: ${totalPrice}</Text>
|
|
741
|
+
</View>
|
|
742
|
+
);
|
|
743
|
+
}
|
|
744
|
+
```
|
|
745
|
+
|
|
746
|
+
**Correct (derived value, single render):**
|
|
747
|
+
|
|
748
|
+
```typescript
|
|
749
|
+
// screens/ProductDetails.tsx
|
|
750
|
+
export function ProductDetails({ product }: Props) {
|
|
751
|
+
const [quantity, setQuantity] = useState(1);
|
|
752
|
+
const totalPrice = product.price * quantity; // Computed during render
|
|
753
|
+
|
|
754
|
+
return (
|
|
755
|
+
<View>
|
|
756
|
+
<QuantityPicker value={quantity} onChange={setQuantity} />
|
|
757
|
+
<Text>Total: ${totalPrice}</Text>
|
|
758
|
+
</View>
|
|
759
|
+
);
|
|
760
|
+
}
|
|
761
|
+
```
|
|
762
|
+
|
|
763
|
+
**When to derive:**
|
|
764
|
+
- Filtered/sorted lists from source data
|
|
765
|
+
- Computed totals, averages, counts
|
|
766
|
+
- Boolean flags based on other state
|
|
767
|
+
- Formatted display values
|
|
768
|
+
|
|
769
|
+
**When to use useMemo:** Wrap in `useMemo` if the derivation is expensive and deps rarely change.
|
|
770
|
+
|
|
771
|
+
Reference: [Choosing the State Structure](https://react.dev/learn/choosing-the-state-structure#avoid-redundant-state)
|
|
772
|
+
|
|
773
|
+
### 3.2 Memoize Expensive Computations with useMemo
|
|
774
|
+
|
|
775
|
+
**Impact: HIGH (prevents recalculation on every render)**
|
|
776
|
+
|
|
777
|
+
Use `useMemo` to cache the result of expensive computations. Without memoization, computations run on every render, even when inputs haven't changed, blocking the JS thread.
|
|
778
|
+
|
|
779
|
+
**Incorrect (filters/sorts on every render):**
|
|
780
|
+
|
|
781
|
+
```typescript
|
|
782
|
+
// screens/TransactionList.tsx
|
|
783
|
+
export function TransactionList({ transactions, filter }: Props) {
|
|
784
|
+
const [searchQuery, setSearchQuery] = useState('');
|
|
785
|
+
|
|
786
|
+
// Runs on EVERY render, including unrelated state changes
|
|
787
|
+
const filteredTransactions = transactions
|
|
788
|
+
.filter((t) => t.category === filter)
|
|
789
|
+
.filter((t) => t.description.includes(searchQuery))
|
|
790
|
+
.sort((a, b) => b.date.getTime() - a.date.getTime());
|
|
791
|
+
|
|
792
|
+
return <FlashList data={filteredTransactions} /* ... */ />;
|
|
793
|
+
}
|
|
794
|
+
```
|
|
795
|
+
|
|
796
|
+
**Correct (memoized until dependencies change):**
|
|
797
|
+
|
|
798
|
+
```typescript
|
|
799
|
+
// screens/TransactionList.tsx
|
|
800
|
+
export function TransactionList({ transactions, filter }: Props) {
|
|
801
|
+
const [searchQuery, setSearchQuery] = useState('');
|
|
802
|
+
|
|
803
|
+
const filteredTransactions = useMemo(() => {
|
|
804
|
+
return transactions
|
|
805
|
+
.filter((t) => t.category === filter)
|
|
806
|
+
.filter((t) => t.description.includes(searchQuery))
|
|
807
|
+
.sort((a, b) => b.date.getTime() - a.date.getTime());
|
|
808
|
+
}, [transactions, filter, searchQuery]);
|
|
809
|
+
|
|
810
|
+
return <FlashList data={filteredTransactions} /* ... */ />;
|
|
811
|
+
}
|
|
812
|
+
```
|
|
813
|
+
|
|
814
|
+
**When to use useMemo:**
|
|
815
|
+
- Array filtering/sorting with 100+ items
|
|
816
|
+
- Complex object transformations
|
|
817
|
+
- Heavy calculations (date parsing, formatting)
|
|
818
|
+
|
|
819
|
+
**When NOT to use:** Simple operations or operations that always depend on changing values.
|
|
820
|
+
|
|
821
|
+
Reference: [useMemo](https://react.dev/reference/react/useMemo)
|
|
822
|
+
|
|
823
|
+
### 3.3 Split Context by Update Frequency
|
|
824
|
+
|
|
825
|
+
**Impact: HIGH (prevents cascading re-renders across app)**
|
|
826
|
+
|
|
827
|
+
A single context with frequently-changing values causes all consumers to re-render. Split context into separate providers based on update frequency to limit re-render scope.
|
|
828
|
+
|
|
829
|
+
**Incorrect (one context, all consumers re-render):**
|
|
830
|
+
|
|
831
|
+
```typescript
|
|
832
|
+
// contexts/AppContext.tsx
|
|
833
|
+
const AppContext = createContext<{
|
|
834
|
+
user: User;
|
|
835
|
+
theme: Theme;
|
|
836
|
+
notifications: Notification[]; // Updates frequently
|
|
837
|
+
} | null>(null);
|
|
838
|
+
|
|
839
|
+
export function AppProvider({ children }: Props) {
|
|
840
|
+
const [user, setUser] = useState<User | null>(null);
|
|
841
|
+
const [theme, setTheme] = useState<Theme>('light');
|
|
842
|
+
const [notifications, setNotifications] = useState<Notification[]>([]);
|
|
843
|
+
|
|
844
|
+
return (
|
|
845
|
+
<AppContext.Provider value={{ user, theme, notifications }}>
|
|
846
|
+
{children}
|
|
847
|
+
</AppContext.Provider>
|
|
848
|
+
);
|
|
849
|
+
}
|
|
850
|
+
// Every component using user or theme re-renders on new notification
|
|
851
|
+
```
|
|
852
|
+
|
|
853
|
+
**Correct (split by update frequency):**
|
|
854
|
+
|
|
855
|
+
```typescript
|
|
856
|
+
// contexts/UserContext.tsx - rarely changes
|
|
857
|
+
const UserContext = createContext<User | null>(null);
|
|
858
|
+
|
|
859
|
+
// contexts/ThemeContext.tsx - rarely changes
|
|
860
|
+
const ThemeContext = createContext<Theme>('light');
|
|
861
|
+
|
|
862
|
+
// contexts/NotificationsContext.tsx - changes frequently
|
|
863
|
+
const NotificationsContext = createContext<Notification[]>([]);
|
|
864
|
+
|
|
865
|
+
// App.tsx
|
|
866
|
+
export function AppProvider({ children }: Props) {
|
|
867
|
+
const [user, setUser] = useState<User | null>(null);
|
|
868
|
+
const [theme, setTheme] = useState<Theme>('light');
|
|
869
|
+
const [notifications, setNotifications] = useState<Notification[]>([]);
|
|
870
|
+
|
|
871
|
+
return (
|
|
872
|
+
<UserContext.Provider value={user}>
|
|
873
|
+
<ThemeContext.Provider value={theme}>
|
|
874
|
+
<NotificationsContext.Provider value={notifications}>
|
|
875
|
+
{children}
|
|
876
|
+
</NotificationsContext.Provider>
|
|
877
|
+
</ThemeContext.Provider>
|
|
878
|
+
</UserContext.Provider>
|
|
879
|
+
);
|
|
880
|
+
}
|
|
881
|
+
// Only notification consumers re-render on new notification
|
|
882
|
+
```
|
|
883
|
+
|
|
884
|
+
**Alternative:** Use state management libraries like Zustand or Jotai that allow selective subscriptions.
|
|
885
|
+
|
|
886
|
+
Reference: [React Context](https://react.dev/learn/passing-data-deeply-with-context)
|
|
887
|
+
|
|
888
|
+
### 3.4 Stabilize Callbacks with useCallback
|
|
889
|
+
|
|
890
|
+
**Impact: HIGH (prevents child re-renders from new function refs)**
|
|
891
|
+
|
|
892
|
+
Functions defined inside components get new references on every render. When passed as props to memoized children, this breaks memoization. Use `useCallback` to maintain stable function references.
|
|
893
|
+
|
|
894
|
+
**Incorrect (new callback reference breaks child memo):**
|
|
895
|
+
|
|
896
|
+
```typescript
|
|
897
|
+
// screens/UserSettings.tsx
|
|
898
|
+
export function UserSettings({ userId }: Props) {
|
|
899
|
+
const [notifications, setNotifications] = useState(true);
|
|
900
|
+
|
|
901
|
+
// New function created every render
|
|
902
|
+
const handleSave = async () => {
|
|
903
|
+
await saveSettings(userId, { notifications });
|
|
904
|
+
};
|
|
905
|
+
|
|
906
|
+
return (
|
|
907
|
+
<>
|
|
908
|
+
<Toggle value={notifications} onValueChange={setNotifications} />
|
|
909
|
+
<SaveButton onPress={handleSave} /> {/* Re-renders on toggle */}
|
|
910
|
+
</>
|
|
911
|
+
);
|
|
912
|
+
}
|
|
913
|
+
|
|
914
|
+
const SaveButton = memo(({ onPress }: { onPress: () => void }) => (
|
|
915
|
+
<Pressable onPress={onPress}><Text>Save</Text></Pressable>
|
|
916
|
+
));
|
|
917
|
+
```
|
|
918
|
+
|
|
919
|
+
**Correct (stable callback preserves child memo):**
|
|
920
|
+
|
|
921
|
+
```typescript
|
|
922
|
+
// screens/UserSettings.tsx
|
|
923
|
+
export function UserSettings({ userId }: Props) {
|
|
924
|
+
const [notifications, setNotifications] = useState(true);
|
|
925
|
+
|
|
926
|
+
const handleSave = useCallback(async () => {
|
|
927
|
+
await saveSettings(userId, { notifications });
|
|
928
|
+
}, [userId, notifications]);
|
|
929
|
+
|
|
930
|
+
return (
|
|
931
|
+
<>
|
|
932
|
+
<Toggle value={notifications} onValueChange={setNotifications} />
|
|
933
|
+
<SaveButton onPress={handleSave} /> {/* Only re-renders when deps change */}
|
|
934
|
+
</>
|
|
935
|
+
);
|
|
936
|
+
}
|
|
937
|
+
|
|
938
|
+
const SaveButton = memo(({ onPress }: { onPress: () => void }) => (
|
|
939
|
+
<Pressable onPress={onPress}><Text>Save</Text></Pressable>
|
|
940
|
+
));
|
|
941
|
+
```
|
|
942
|
+
|
|
943
|
+
**Note:** `useCallback` only helps when the callback is passed to a memoized component. Without `memo` on the child, it re-renders anyway.
|
|
944
|
+
|
|
945
|
+
Reference: [useCallback](https://react.dev/reference/react/useCallback)
|
|
946
|
+
|
|
947
|
+
### 3.5 Use Functional setState Updates
|
|
948
|
+
|
|
949
|
+
**Impact: HIGH (eliminates state dependency from callbacks)**
|
|
950
|
+
|
|
951
|
+
When updating state based on the current value, use the functional form of setState. This eliminates the need to include state in callback dependencies, allowing truly stable callbacks.
|
|
952
|
+
|
|
953
|
+
**Incorrect (state in dependency array recreates callback):**
|
|
954
|
+
|
|
955
|
+
```typescript
|
|
956
|
+
// screens/Counter.tsx
|
|
957
|
+
export function Counter() {
|
|
958
|
+
const [count, setCount] = useState(0);
|
|
959
|
+
|
|
960
|
+
// Recreated every time count changes
|
|
961
|
+
const increment = useCallback(() => {
|
|
962
|
+
setCount(count + 1);
|
|
963
|
+
}, [count]);
|
|
964
|
+
|
|
965
|
+
return (
|
|
966
|
+
<MemoizedButton onPress={increment} /> // Re-renders on every count change
|
|
967
|
+
);
|
|
968
|
+
}
|
|
969
|
+
```
|
|
970
|
+
|
|
971
|
+
**Correct (functional update, empty deps):**
|
|
972
|
+
|
|
973
|
+
```typescript
|
|
974
|
+
// screens/Counter.tsx
|
|
975
|
+
export function Counter() {
|
|
976
|
+
const [count, setCount] = useState(0);
|
|
977
|
+
|
|
978
|
+
// Never recreated - truly stable callback
|
|
979
|
+
const increment = useCallback(() => {
|
|
980
|
+
setCount((c) => c + 1);
|
|
981
|
+
}, []);
|
|
982
|
+
|
|
983
|
+
return (
|
|
984
|
+
<MemoizedButton onPress={increment} /> // Never re-renders from callback change
|
|
985
|
+
);
|
|
986
|
+
}
|
|
987
|
+
```
|
|
988
|
+
|
|
989
|
+
**Pattern applies to:**
|
|
990
|
+
- Incrementing/decrementing numbers
|
|
991
|
+
- Toggling booleans: `setValue(v => !v)`
|
|
992
|
+
- Adding to arrays: `setItems(items => [...items, newItem])`
|
|
993
|
+
- Updating object properties: `setUser(u => ({ ...u, name }))`
|
|
994
|
+
|
|
995
|
+
Reference: [useState functional updates](https://react.dev/reference/react/useState#updating-state-based-on-the-previous-state)
|
|
996
|
+
|
|
997
|
+
### 3.6 Use Lazy State Initialization
|
|
998
|
+
|
|
999
|
+
**Impact: HIGH (prevents expensive init on every render)**
|
|
1000
|
+
|
|
1001
|
+
When initial state requires expensive computation, pass a function to `useState` instead of calling the computation directly. React only calls the initializer once, but direct calls run on every render.
|
|
1002
|
+
|
|
1003
|
+
**Incorrect (expensive init runs every render):**
|
|
1004
|
+
|
|
1005
|
+
```typescript
|
|
1006
|
+
// screens/Dashboard.tsx
|
|
1007
|
+
function parseStoredPreferences(): Preferences {
|
|
1008
|
+
const stored = localStorage.getItem('prefs');
|
|
1009
|
+
return stored ? JSON.parse(stored) : getDefaultPreferences();
|
|
1010
|
+
}
|
|
1011
|
+
|
|
1012
|
+
export function Dashboard() {
|
|
1013
|
+
// parseStoredPreferences() called EVERY render, result discarded after first
|
|
1014
|
+
const [preferences, setPreferences] = useState(parseStoredPreferences());
|
|
1015
|
+
|
|
1016
|
+
return <PreferencesPanel prefs={preferences} />;
|
|
1017
|
+
}
|
|
1018
|
+
```
|
|
1019
|
+
|
|
1020
|
+
**Correct (lazy init runs only once):**
|
|
1021
|
+
|
|
1022
|
+
```typescript
|
|
1023
|
+
// screens/Dashboard.tsx
|
|
1024
|
+
function parseStoredPreferences(): Preferences {
|
|
1025
|
+
const stored = localStorage.getItem('prefs');
|
|
1026
|
+
return stored ? JSON.parse(stored) : getDefaultPreferences();
|
|
1027
|
+
}
|
|
1028
|
+
|
|
1029
|
+
export function Dashboard() {
|
|
1030
|
+
// Function reference passed - called only on mount
|
|
1031
|
+
const [preferences, setPreferences] = useState(parseStoredPreferences);
|
|
1032
|
+
|
|
1033
|
+
return <PreferencesPanel prefs={preferences} />;
|
|
1034
|
+
}
|
|
1035
|
+
```
|
|
1036
|
+
|
|
1037
|
+
**Note the difference:**
|
|
1038
|
+
- `useState(parseStoredPreferences())` - calls function, passes result
|
|
1039
|
+
- `useState(parseStoredPreferences)` - passes function, React calls once
|
|
1040
|
+
|
|
1041
|
+
**Common use cases:**
|
|
1042
|
+
- Parsing stored data (AsyncStorage, SecureStore)
|
|
1043
|
+
- Creating initial data structures
|
|
1044
|
+
- Complex default calculations
|
|
1045
|
+
|
|
1046
|
+
Reference: [useState lazy initialization](https://react.dev/reference/react/useState#avoiding-recreating-the-initial-state)
|
|
1047
|
+
|
|
1048
|
+
---
|
|
1049
|
+
|
|
1050
|
+
## 4. Animation Performance
|
|
1051
|
+
|
|
1052
|
+
**Impact: HIGH**
|
|
1053
|
+
|
|
1054
|
+
60 FPS requires animations on the UI thread. Bridge crossings cause janky animations. Reanimated and native driver are essential.
|
|
1055
|
+
|
|
1056
|
+
### 4.1 Animate Transform Instead of Dimensions
|
|
1057
|
+
|
|
1058
|
+
**Impact: HIGH (eliminates 60× layout recalculations per second)**
|
|
1059
|
+
|
|
1060
|
+
Animating `width` and `height` triggers layout recalculation on every frame. Use `transform: [{ scale }]` instead, which is GPU-accelerated and doesn't affect layout.
|
|
1061
|
+
|
|
1062
|
+
**Incorrect (animating dimensions, causes reflow):**
|
|
1063
|
+
|
|
1064
|
+
```typescript
|
|
1065
|
+
// components/PulsingButton.tsx
|
|
1066
|
+
export function PulsingButton({ onPress, children }: Props) {
|
|
1067
|
+
const size = useRef(new Animated.Value(100)).current;
|
|
1068
|
+
|
|
1069
|
+
useEffect(() => {
|
|
1070
|
+
Animated.loop(
|
|
1071
|
+
Animated.sequence([
|
|
1072
|
+
Animated.timing(size, { toValue: 110, duration: 500, useNativeDriver: false }),
|
|
1073
|
+
Animated.timing(size, { toValue: 100, duration: 500, useNativeDriver: false }),
|
|
1074
|
+
])
|
|
1075
|
+
).start();
|
|
1076
|
+
}, []);
|
|
1077
|
+
|
|
1078
|
+
return (
|
|
1079
|
+
<Animated.View style={{ width: size, height: size }}>
|
|
1080
|
+
<Pressable onPress={onPress}>{children}</Pressable>
|
|
1081
|
+
</Animated.View>
|
|
1082
|
+
);
|
|
1083
|
+
}
|
|
1084
|
+
// Layout recalculated 60× per second, affects surrounding elements
|
|
1085
|
+
```
|
|
1086
|
+
|
|
1087
|
+
**Correct (animating transform, GPU-accelerated):**
|
|
1088
|
+
|
|
1089
|
+
```typescript
|
|
1090
|
+
// components/PulsingButton.tsx
|
|
1091
|
+
export function PulsingButton({ onPress, children }: Props) {
|
|
1092
|
+
const scale = useRef(new Animated.Value(1)).current;
|
|
1093
|
+
|
|
1094
|
+
useEffect(() => {
|
|
1095
|
+
Animated.loop(
|
|
1096
|
+
Animated.sequence([
|
|
1097
|
+
Animated.timing(scale, { toValue: 1.1, duration: 500, useNativeDriver: true }),
|
|
1098
|
+
Animated.timing(scale, { toValue: 1, duration: 500, useNativeDriver: true }),
|
|
1099
|
+
])
|
|
1100
|
+
).start();
|
|
1101
|
+
}, []);
|
|
1102
|
+
|
|
1103
|
+
return (
|
|
1104
|
+
<Animated.View style={{ transform: [{ scale }] }}>
|
|
1105
|
+
<Pressable onPress={onPress}>{children}</Pressable>
|
|
1106
|
+
</Animated.View>
|
|
1107
|
+
);
|
|
1108
|
+
}
|
|
1109
|
+
// GPU-accelerated, no layout impact, native driver compatible
|
|
1110
|
+
```
|
|
1111
|
+
|
|
1112
|
+
**Transform properties (all native driver compatible):**
|
|
1113
|
+
- `scale`, `scaleX`, `scaleY`
|
|
1114
|
+
- `translateX`, `translateY`
|
|
1115
|
+
- `rotate`, `rotateX`, `rotateY`
|
|
1116
|
+
|
|
1117
|
+
Reference: [React Native Animations](https://reactnative.dev/docs/animations)
|
|
1118
|
+
|
|
1119
|
+
### 4.2 Defer Heavy Work During Animations
|
|
1120
|
+
|
|
1121
|
+
**Impact: HIGH (prevents dropped frames during transitions)**
|
|
1122
|
+
|
|
1123
|
+
Heavy JavaScript execution during animations causes dropped frames. Use `InteractionManager.runAfterInteractions()` to defer expensive work until animations complete.
|
|
1124
|
+
|
|
1125
|
+
**Incorrect (heavy work during navigation animation):**
|
|
1126
|
+
|
|
1127
|
+
```typescript
|
|
1128
|
+
// screens/ProfileScreen.tsx
|
|
1129
|
+
export function ProfileScreen({ userId }: Props) {
|
|
1130
|
+
const [profile, setProfile] = useState<Profile | null>(null);
|
|
1131
|
+
|
|
1132
|
+
useEffect(() => {
|
|
1133
|
+
// Runs immediately, blocks JS thread during screen transition
|
|
1134
|
+
fetchProfile(userId).then(setProfile);
|
|
1135
|
+
loadAnalytics(userId);
|
|
1136
|
+
preloadImages(userId);
|
|
1137
|
+
}, [userId]);
|
|
1138
|
+
|
|
1139
|
+
return profile ? <ProfileView profile={profile} /> : <Loading />;
|
|
1140
|
+
}
|
|
1141
|
+
// Navigation animation stutters as JS thread is blocked
|
|
1142
|
+
```
|
|
1143
|
+
|
|
1144
|
+
**Correct (defer until animation completes):**
|
|
1145
|
+
|
|
1146
|
+
```typescript
|
|
1147
|
+
// screens/ProfileScreen.tsx
|
|
1148
|
+
import { InteractionManager } from 'react-native';
|
|
1149
|
+
|
|
1150
|
+
export function ProfileScreen({ userId }: Props) {
|
|
1151
|
+
const [profile, setProfile] = useState<Profile | null>(null);
|
|
1152
|
+
|
|
1153
|
+
useEffect(() => {
|
|
1154
|
+
const task = InteractionManager.runAfterInteractions(() => {
|
|
1155
|
+
fetchProfile(userId).then(setProfile);
|
|
1156
|
+
loadAnalytics(userId);
|
|
1157
|
+
preloadImages(userId);
|
|
1158
|
+
});
|
|
1159
|
+
|
|
1160
|
+
return () => task.cancel();
|
|
1161
|
+
}, [userId]);
|
|
1162
|
+
|
|
1163
|
+
return profile ? <ProfileView profile={profile} /> : <Loading />;
|
|
1164
|
+
}
|
|
1165
|
+
// Navigation animation completes smoothly, then data loads
|
|
1166
|
+
```
|
|
1167
|
+
|
|
1168
|
+
**When to defer:**
|
|
1169
|
+
- Screen mount data fetching
|
|
1170
|
+
- Heavy list rendering
|
|
1171
|
+
- Image processing
|
|
1172
|
+
- Analytics/logging
|
|
1173
|
+
|
|
1174
|
+
**Trade-off:** Users see loading state slightly longer, but transitions are smooth.
|
|
1175
|
+
|
|
1176
|
+
Reference: [InteractionManager](https://reactnative.dev/docs/interactionmanager)
|
|
1177
|
+
|
|
1178
|
+
### 4.3 Enable Native Driver for Animations
|
|
1179
|
+
|
|
1180
|
+
**Impact: HIGH (consistent 60 FPS vs 15-30 FPS under JS load)**
|
|
1181
|
+
|
|
1182
|
+
The Animated API runs on the JS thread by default, causing jank when the thread is busy. Enable `useNativeDriver: true` to run animations entirely on the UI thread, achieving smooth 60 FPS regardless of JS load.
|
|
1183
|
+
|
|
1184
|
+
**Incorrect (JS thread animation, drops frames under load):**
|
|
1185
|
+
|
|
1186
|
+
```typescript
|
|
1187
|
+
// components/FadeInView.tsx
|
|
1188
|
+
export function FadeInView({ children }: Props) {
|
|
1189
|
+
const opacity = useRef(new Animated.Value(0)).current;
|
|
1190
|
+
|
|
1191
|
+
useEffect(() => {
|
|
1192
|
+
Animated.timing(opacity, {
|
|
1193
|
+
toValue: 1,
|
|
1194
|
+
duration: 300,
|
|
1195
|
+
useNativeDriver: false, // Runs on JS thread
|
|
1196
|
+
}).start();
|
|
1197
|
+
}, []);
|
|
1198
|
+
|
|
1199
|
+
return <Animated.View style={{ opacity }}>{children}</Animated.View>;
|
|
1200
|
+
}
|
|
1201
|
+
```
|
|
1202
|
+
|
|
1203
|
+
**Correct (native thread animation, always 60 FPS):**
|
|
1204
|
+
|
|
1205
|
+
```typescript
|
|
1206
|
+
// components/FadeInView.tsx
|
|
1207
|
+
export function FadeInView({ children }: Props) {
|
|
1208
|
+
const opacity = useRef(new Animated.Value(0)).current;
|
|
1209
|
+
|
|
1210
|
+
useEffect(() => {
|
|
1211
|
+
Animated.timing(opacity, {
|
|
1212
|
+
toValue: 1,
|
|
1213
|
+
duration: 300,
|
|
1214
|
+
useNativeDriver: true, // Runs on UI thread
|
|
1215
|
+
}).start();
|
|
1216
|
+
}, []);
|
|
1217
|
+
|
|
1218
|
+
return <Animated.View style={{ opacity }}>{children}</Animated.View>;
|
|
1219
|
+
}
|
|
1220
|
+
```
|
|
1221
|
+
|
|
1222
|
+
**Native driver limitations:**
|
|
1223
|
+
- Only supports non-layout properties: `transform`, `opacity`
|
|
1224
|
+
- Cannot animate `width`, `height`, `backgroundColor`
|
|
1225
|
+
- Use Reanimated for layout property animations
|
|
1226
|
+
|
|
1227
|
+
Reference: [React Native Animations](https://reactnative.dev/docs/animations#using-the-native-driver)
|
|
1228
|
+
|
|
1229
|
+
### 4.4 Use LayoutAnimation for Simple Transitions
|
|
1230
|
+
|
|
1231
|
+
**Impact: HIGH (50% less code, native 60 FPS)**
|
|
1232
|
+
|
|
1233
|
+
For simple layout changes (showing/hiding, resizing), use `LayoutAnimation` instead of managing animation state. It automatically animates all layout changes in the next render using native animations.
|
|
1234
|
+
|
|
1235
|
+
**Incorrect (manual animation state management):**
|
|
1236
|
+
|
|
1237
|
+
```typescript
|
|
1238
|
+
// components/ExpandableSection.tsx
|
|
1239
|
+
export function ExpandableSection({ title, children }: Props) {
|
|
1240
|
+
const [expanded, setExpanded] = useState(false);
|
|
1241
|
+
const height = useRef(new Animated.Value(0)).current;
|
|
1242
|
+
|
|
1243
|
+
const toggle = () => {
|
|
1244
|
+
setExpanded(!expanded);
|
|
1245
|
+
Animated.timing(height, {
|
|
1246
|
+
toValue: expanded ? 0 : 200,
|
|
1247
|
+
duration: 300,
|
|
1248
|
+
useNativeDriver: false,
|
|
1249
|
+
}).start();
|
|
1250
|
+
};
|
|
1251
|
+
|
|
1252
|
+
return (
|
|
1253
|
+
<View>
|
|
1254
|
+
<Pressable onPress={toggle}><Text>{title}</Text></Pressable>
|
|
1255
|
+
<Animated.View style={{ height, overflow: 'hidden' }}>
|
|
1256
|
+
{children}
|
|
1257
|
+
</Animated.View>
|
|
1258
|
+
</View>
|
|
1259
|
+
);
|
|
1260
|
+
}
|
|
1261
|
+
```
|
|
1262
|
+
|
|
1263
|
+
**Correct (LayoutAnimation handles it):**
|
|
1264
|
+
|
|
1265
|
+
```typescript
|
|
1266
|
+
// components/ExpandableSection.tsx
|
|
1267
|
+
import { LayoutAnimation, UIManager, Platform } from 'react-native';
|
|
1268
|
+
|
|
1269
|
+
if (Platform.OS === 'android') {
|
|
1270
|
+
UIManager.setLayoutAnimationEnabledExperimental?.(true);
|
|
1271
|
+
}
|
|
1272
|
+
|
|
1273
|
+
export function ExpandableSection({ title, children }: Props) {
|
|
1274
|
+
const [expanded, setExpanded] = useState(false);
|
|
1275
|
+
|
|
1276
|
+
const toggle = () => {
|
|
1277
|
+
LayoutAnimation.configureNext(LayoutAnimation.Presets.easeInEaseOut);
|
|
1278
|
+
setExpanded(!expanded);
|
|
1279
|
+
};
|
|
1280
|
+
|
|
1281
|
+
return (
|
|
1282
|
+
<View>
|
|
1283
|
+
<Pressable onPress={toggle}><Text>{title}</Text></Pressable>
|
|
1284
|
+
{expanded && children}
|
|
1285
|
+
</View>
|
|
1286
|
+
);
|
|
1287
|
+
}
|
|
1288
|
+
// Native animation applied automatically to layout change
|
|
1289
|
+
```
|
|
1290
|
+
|
|
1291
|
+
**When to use LayoutAnimation:**
|
|
1292
|
+
- Showing/hiding elements
|
|
1293
|
+
- List item insertions/deletions
|
|
1294
|
+
- Simple size changes
|
|
1295
|
+
|
|
1296
|
+
**When NOT to use:** Gesture-driven or interruptible animations.
|
|
1297
|
+
|
|
1298
|
+
Reference: [LayoutAnimation](https://reactnative.dev/docs/layoutanimation)
|
|
1299
|
+
|
|
1300
|
+
### 4.5 Use Reanimated for Complex Animations
|
|
1301
|
+
|
|
1302
|
+
**Impact: HIGH (60-120 FPS for all properties vs 15-30 FPS)**
|
|
1303
|
+
|
|
1304
|
+
React Native Reanimated runs animation logic on the UI thread using worklets, bypassing the JS bridge entirely. This enables smooth 60-120 FPS animations for any property, including layout properties the native driver doesn't support.
|
|
1305
|
+
|
|
1306
|
+
**Incorrect (Animated API with layout, causes jank):**
|
|
1307
|
+
|
|
1308
|
+
```typescript
|
|
1309
|
+
// components/ExpandingCard.tsx
|
|
1310
|
+
export function ExpandingCard({ expanded }: Props) {
|
|
1311
|
+
const height = useRef(new Animated.Value(100)).current;
|
|
1312
|
+
|
|
1313
|
+
useEffect(() => {
|
|
1314
|
+
Animated.timing(height, {
|
|
1315
|
+
toValue: expanded ? 300 : 100,
|
|
1316
|
+
duration: 300,
|
|
1317
|
+
useNativeDriver: false, // Required for height, but janky
|
|
1318
|
+
}).start();
|
|
1319
|
+
}, [expanded]);
|
|
1320
|
+
|
|
1321
|
+
return <Animated.View style={{ height }} />;
|
|
1322
|
+
}
|
|
1323
|
+
```
|
|
1324
|
+
|
|
1325
|
+
**Correct (Reanimated with worklets):**
|
|
1326
|
+
|
|
1327
|
+
```typescript
|
|
1328
|
+
// components/ExpandingCard.tsx
|
|
1329
|
+
import Animated, { useAnimatedStyle, withTiming } from 'react-native-reanimated';
|
|
1330
|
+
|
|
1331
|
+
export function ExpandingCard({ expanded }: Props) {
|
|
1332
|
+
const animatedStyle = useAnimatedStyle(() => ({
|
|
1333
|
+
height: withTiming(expanded ? 300 : 100, { duration: 300 }),
|
|
1334
|
+
}));
|
|
1335
|
+
|
|
1336
|
+
return <Animated.View style={animatedStyle} />;
|
|
1337
|
+
}
|
|
1338
|
+
// Runs entirely on UI thread, smooth 60+ FPS
|
|
1339
|
+
```
|
|
1340
|
+
|
|
1341
|
+
**Reanimated advantages:**
|
|
1342
|
+
- Animates any style property (height, width, backgroundColor)
|
|
1343
|
+
- Worklets execute on UI thread
|
|
1344
|
+
- Gesture-driven animations with react-native-gesture-handler
|
|
1345
|
+
- Supports 120 FPS on ProMotion displays
|
|
1346
|
+
|
|
1347
|
+
Reference: [React Native Reanimated](https://docs.swmansion.com/react-native-reanimated/)
|
|
1348
|
+
|
|
1349
|
+
---
|
|
1350
|
+
|
|
1351
|
+
## 5. Image & Asset Loading
|
|
1352
|
+
|
|
1353
|
+
**Impact: MEDIUM-HIGH**
|
|
1354
|
+
|
|
1355
|
+
Images are the largest payload in most apps. Poor caching causes network waterfalls. expo-image provides automatic optimization and caching.
|
|
1356
|
+
|
|
1357
|
+
### 5.1 Prefetch Images Before Display
|
|
1358
|
+
|
|
1359
|
+
**Impact: MEDIUM-HIGH (0ms display delay vs 200-500ms on demand)**
|
|
1360
|
+
|
|
1361
|
+
Use `Image.prefetch()` to download images before they're needed. This enables instant display when the user navigates to a screen or scrolls to new content.
|
|
1362
|
+
|
|
1363
|
+
**Incorrect (images load when component mounts):**
|
|
1364
|
+
|
|
1365
|
+
```typescript
|
|
1366
|
+
// screens/ProductDetails.tsx
|
|
1367
|
+
export function ProductDetails({ productId }: Props) {
|
|
1368
|
+
const { data: product } = useQuery(['product', productId], fetchProduct);
|
|
1369
|
+
|
|
1370
|
+
// Images start loading after component renders
|
|
1371
|
+
return (
|
|
1372
|
+
<ScrollView>
|
|
1373
|
+
{product?.images.map((img) => (
|
|
1374
|
+
<Image key={img.id} source={{ uri: img.url }} style={styles.image} />
|
|
1375
|
+
))}
|
|
1376
|
+
</ScrollView>
|
|
1377
|
+
);
|
|
1378
|
+
}
|
|
1379
|
+
// User sees loading placeholders, then images pop in
|
|
1380
|
+
```
|
|
1381
|
+
|
|
1382
|
+
**Correct (prefetch on hover/focus):**
|
|
1383
|
+
|
|
1384
|
+
```typescript
|
|
1385
|
+
// components/ProductCard.tsx
|
|
1386
|
+
import { Image } from 'expo-image';
|
|
1387
|
+
|
|
1388
|
+
export function ProductCard({ product, onPress }: Props) {
|
|
1389
|
+
const prefetchImages = useCallback(() => {
|
|
1390
|
+
product.images.forEach((img) => {
|
|
1391
|
+
Image.prefetch(img.url);
|
|
1392
|
+
});
|
|
1393
|
+
}, [product.images]);
|
|
1394
|
+
|
|
1395
|
+
return (
|
|
1396
|
+
<Pressable
|
|
1397
|
+
onPress={onPress}
|
|
1398
|
+
onHoverIn={prefetchImages}
|
|
1399
|
+
onPressIn={prefetchImages}
|
|
1400
|
+
>
|
|
1401
|
+
<Image source={{ uri: product.thumbnailUrl }} style={styles.thumbnail} />
|
|
1402
|
+
<Text>{product.name}</Text>
|
|
1403
|
+
</Pressable>
|
|
1404
|
+
);
|
|
1405
|
+
}
|
|
1406
|
+
// Images already cached when user opens details
|
|
1407
|
+
```
|
|
1408
|
+
|
|
1409
|
+
**Alternative:** Prefetch during list scroll using `onViewableItemsChanged`.
|
|
1410
|
+
|
|
1411
|
+
Reference: [expo-image prefetch](https://docs.expo.dev/versions/latest/sdk/image/#imageprefetch)
|
|
1412
|
+
|
|
1413
|
+
### 5.2 Request Appropriately Sized Images
|
|
1414
|
+
|
|
1415
|
+
**Impact: MEDIUM-HIGH (50-90% bandwidth reduction)**
|
|
1416
|
+
|
|
1417
|
+
Loading full-resolution images for thumbnail displays wastes bandwidth and memory. Request images sized for their display dimensions using CDN resize parameters.
|
|
1418
|
+
|
|
1419
|
+
**Incorrect (full-size image for thumbnail):**
|
|
1420
|
+
|
|
1421
|
+
```typescript
|
|
1422
|
+
// components/ProductCard.tsx
|
|
1423
|
+
export function ProductCard({ product }: Props) {
|
|
1424
|
+
return (
|
|
1425
|
+
<View style={styles.card}>
|
|
1426
|
+
{/* 100×100 display, but downloading 2000×2000 original */}
|
|
1427
|
+
<Image
|
|
1428
|
+
source={{ uri: product.imageUrl }}
|
|
1429
|
+
style={{ width: 100, height: 100 }}
|
|
1430
|
+
/>
|
|
1431
|
+
<Text>{product.name}</Text>
|
|
1432
|
+
</View>
|
|
1433
|
+
);
|
|
1434
|
+
}
|
|
1435
|
+
// Downloads 2MB image, displays at 100px, wastes bandwidth
|
|
1436
|
+
```
|
|
1437
|
+
|
|
1438
|
+
**Correct (request thumbnail-sized image):**
|
|
1439
|
+
|
|
1440
|
+
```typescript
|
|
1441
|
+
// utils/images.ts
|
|
1442
|
+
export function getResizedImageUrl(url: string, width: number): string {
|
|
1443
|
+
// Cloudinary example
|
|
1444
|
+
return url.replace('/upload/', `/upload/w_${width},c_fill,f_auto/`);
|
|
1445
|
+
// Or imgix: `${url}?w=${width}&auto=format`
|
|
1446
|
+
}
|
|
1447
|
+
|
|
1448
|
+
// components/ProductCard.tsx
|
|
1449
|
+
export function ProductCard({ product }: Props) {
|
|
1450
|
+
const thumbnailUrl = getResizedImageUrl(product.imageUrl, 200); // 2× for Retina
|
|
1451
|
+
|
|
1452
|
+
return (
|
|
1453
|
+
<View style={styles.card}>
|
|
1454
|
+
<Image
|
|
1455
|
+
source={{ uri: thumbnailUrl }}
|
|
1456
|
+
style={{ width: 100, height: 100 }}
|
|
1457
|
+
/>
|
|
1458
|
+
<Text>{product.name}</Text>
|
|
1459
|
+
</View>
|
|
1460
|
+
);
|
|
1461
|
+
}
|
|
1462
|
+
// Downloads 20KB thumbnail instead of 2MB original
|
|
1463
|
+
```
|
|
1464
|
+
|
|
1465
|
+
**CDN resize services:** Cloudinary, imgix, Cloudflare Images, ImageKit
|
|
1466
|
+
|
|
1467
|
+
Reference: [Image Optimization](https://docs.imgix.com/en-US/getting-started/tutorials/responsive-design/rendering-images-in-react-native-faster-with-imgix)
|
|
1468
|
+
|
|
1469
|
+
### 5.3 Use expo-image for Image Loading
|
|
1470
|
+
|
|
1471
|
+
**Impact: MEDIUM-HIGH (automatic caching, placeholder support)**
|
|
1472
|
+
|
|
1473
|
+
expo-image provides built-in disk and memory caching, BlurHash placeholders, and automatic downscaling. It outperforms the standard Image component in both performance and developer experience.
|
|
1474
|
+
|
|
1475
|
+
**Incorrect (React Native Image, no caching):**
|
|
1476
|
+
|
|
1477
|
+
```typescript
|
|
1478
|
+
// components/Avatar.tsx
|
|
1479
|
+
import { Image } from 'react-native';
|
|
1480
|
+
|
|
1481
|
+
export function Avatar({ user }: Props) {
|
|
1482
|
+
return (
|
|
1483
|
+
<Image
|
|
1484
|
+
source={{ uri: user.avatarUrl }}
|
|
1485
|
+
style={styles.avatar}
|
|
1486
|
+
/>
|
|
1487
|
+
// No caching, reloads on every mount
|
|
1488
|
+
// Flickers when source changes
|
|
1489
|
+
);
|
|
1490
|
+
}
|
|
1491
|
+
```
|
|
1492
|
+
|
|
1493
|
+
**Correct (expo-image with caching and placeholder):**
|
|
1494
|
+
|
|
1495
|
+
```typescript
|
|
1496
|
+
// components/Avatar.tsx
|
|
1497
|
+
import { Image } from 'expo-image';
|
|
1498
|
+
|
|
1499
|
+
export function Avatar({ user }: Props) {
|
|
1500
|
+
return (
|
|
1501
|
+
<Image
|
|
1502
|
+
source={{ uri: user.avatarUrl }}
|
|
1503
|
+
placeholder={user.avatarBlurhash}
|
|
1504
|
+
contentFit="cover"
|
|
1505
|
+
transition={200}
|
|
1506
|
+
style={styles.avatar}
|
|
1507
|
+
/>
|
|
1508
|
+
// Automatic disk/memory caching
|
|
1509
|
+
// BlurHash shows while loading
|
|
1510
|
+
// Smooth transition when loaded
|
|
1511
|
+
);
|
|
1512
|
+
}
|
|
1513
|
+
```
|
|
1514
|
+
|
|
1515
|
+
**Key props:**
|
|
1516
|
+
- `placeholder` - BlurHash or ThumbHash while loading
|
|
1517
|
+
- `transition` - Fade duration in ms
|
|
1518
|
+
- `cachePolicy` - Control caching behavior
|
|
1519
|
+
- `recyclingKey` - For FlashList view recycling
|
|
1520
|
+
|
|
1521
|
+
Reference: [expo-image](https://docs.expo.dev/versions/latest/sdk/image/)
|
|
1522
|
+
|
|
1523
|
+
### 5.4 Use recyclingKey in FlashList Images
|
|
1524
|
+
|
|
1525
|
+
**Impact: MEDIUM-HIGH (prevents stale image display in recycled cells)**
|
|
1526
|
+
|
|
1527
|
+
FlashList recycles views, which can cause the previous image to flash before the new one loads. Use `recyclingKey` to ensure images update immediately when cells are recycled.
|
|
1528
|
+
|
|
1529
|
+
**Incorrect (stale images flash in recycled cells):**
|
|
1530
|
+
|
|
1531
|
+
```typescript
|
|
1532
|
+
// components/ProductCard.tsx
|
|
1533
|
+
export function ProductCard({ product }: Props) {
|
|
1534
|
+
return (
|
|
1535
|
+
<View style={styles.card}>
|
|
1536
|
+
<Image
|
|
1537
|
+
source={{ uri: product.imageUrl }}
|
|
1538
|
+
style={styles.image}
|
|
1539
|
+
/>
|
|
1540
|
+
<Text>{product.name}</Text>
|
|
1541
|
+
</View>
|
|
1542
|
+
);
|
|
1543
|
+
}
|
|
1544
|
+
// When scrolling fast, old product image shows briefly before new one loads
|
|
1545
|
+
```
|
|
1546
|
+
|
|
1547
|
+
**Correct (recyclingKey prevents stale images):**
|
|
1548
|
+
|
|
1549
|
+
```typescript
|
|
1550
|
+
// components/ProductCard.tsx
|
|
1551
|
+
import { Image } from 'expo-image';
|
|
1552
|
+
|
|
1553
|
+
export function ProductCard({ product }: Props) {
|
|
1554
|
+
return (
|
|
1555
|
+
<View style={styles.card}>
|
|
1556
|
+
<Image
|
|
1557
|
+
source={{ uri: product.imageUrl }}
|
|
1558
|
+
recyclingKey={product.id}
|
|
1559
|
+
placeholder={product.blurhash}
|
|
1560
|
+
style={styles.image}
|
|
1561
|
+
/>
|
|
1562
|
+
<Text>{product.name}</Text>
|
|
1563
|
+
</View>
|
|
1564
|
+
);
|
|
1565
|
+
}
|
|
1566
|
+
// Image clears immediately when cell is recycled, shows placeholder
|
|
1567
|
+
```
|
|
1568
|
+
|
|
1569
|
+
**How it works:**
|
|
1570
|
+
- `recyclingKey` tells expo-image when the source has changed
|
|
1571
|
+
- When key changes, image immediately shows placeholder instead of stale content
|
|
1572
|
+
- Combined with BlurHash, provides smooth visual experience
|
|
1573
|
+
|
|
1574
|
+
Reference: [expo-image recyclingKey](https://docs.expo.dev/versions/latest/sdk/image/#recyclingkey)
|
|
1575
|
+
|
|
1576
|
+
### 5.5 Use WebP Format for Images
|
|
1577
|
+
|
|
1578
|
+
**Impact: MEDIUM-HIGH (25-35% smaller than JPEG at same quality)**
|
|
1579
|
+
|
|
1580
|
+
WebP provides superior compression compared to JPEG and PNG while maintaining quality. Serve WebP images to reduce bandwidth and improve load times.
|
|
1581
|
+
|
|
1582
|
+
**Incorrect (using JPEG/PNG):**
|
|
1583
|
+
|
|
1584
|
+
```typescript
|
|
1585
|
+
// assets/images.ts
|
|
1586
|
+
export const images = {
|
|
1587
|
+
hero: require('./hero.jpg'), // 450KB
|
|
1588
|
+
product: require('./product.png'), // 280KB
|
|
1589
|
+
background: require('./bg.jpg'), // 320KB
|
|
1590
|
+
};
|
|
1591
|
+
// Total: 1050KB
|
|
1592
|
+
```
|
|
1593
|
+
|
|
1594
|
+
**Correct (using WebP):**
|
|
1595
|
+
|
|
1596
|
+
```typescript
|
|
1597
|
+
// assets/images.ts
|
|
1598
|
+
export const images = {
|
|
1599
|
+
hero: require('./hero.webp'), // 290KB (35% smaller)
|
|
1600
|
+
product: require('./product.webp'), // 180KB (36% smaller)
|
|
1601
|
+
background: require('./bg.webp'), // 210KB (34% smaller)
|
|
1602
|
+
};
|
|
1603
|
+
// Total: 680KB (35% reduction)
|
|
1604
|
+
```
|
|
1605
|
+
|
|
1606
|
+
**Converting existing images:**
|
|
1607
|
+
|
|
1608
|
+
```bash
|
|
1609
|
+
# Using cwebp (install via Homebrew)
|
|
1610
|
+
cwebp -q 80 input.jpg -o output.webp
|
|
1611
|
+
|
|
1612
|
+
# Batch convert with expo-optimize
|
|
1613
|
+
npx expo-optimize ./assets
|
|
1614
|
+
```
|
|
1615
|
+
|
|
1616
|
+
**For remote images:** Request WebP from CDN with `f_auto` or `auto=format` parameter.
|
|
1617
|
+
|
|
1618
|
+
**Compatibility:** WebP is supported on iOS 14+ and all Android versions. For older iOS, provide JPEG fallback.
|
|
1619
|
+
|
|
1620
|
+
Reference: [expo-optimize](https://www.npmjs.com/package/expo-optimize)
|
|
1621
|
+
|
|
1622
|
+
---
|
|
1623
|
+
|
|
1624
|
+
## 6. Memory Management
|
|
1625
|
+
|
|
1626
|
+
**Impact: MEDIUM**
|
|
1627
|
+
|
|
1628
|
+
Memory leaks compound over time. Mobile apps stay in memory longer than web. Uncleaned subscriptions and timers cause crashes.
|
|
1629
|
+
|
|
1630
|
+
### 6.1 Abort Fetch Requests on Unmount
|
|
1631
|
+
|
|
1632
|
+
**Impact: MEDIUM (prevents memory leaks from pending requests)**
|
|
1633
|
+
|
|
1634
|
+
Pending fetch requests continue even after unmount. When they resolve, attempting to update state on an unmounted component causes memory leaks. Use AbortController to cancel requests.
|
|
1635
|
+
|
|
1636
|
+
**Incorrect (fetch continues after unmount):**
|
|
1637
|
+
|
|
1638
|
+
```typescript
|
|
1639
|
+
// screens/UserProfile.tsx
|
|
1640
|
+
export function UserProfile({ userId }: Props) {
|
|
1641
|
+
const [user, setUser] = useState<User | null>(null);
|
|
1642
|
+
|
|
1643
|
+
useEffect(() => {
|
|
1644
|
+
fetch(`/api/users/${userId}`)
|
|
1645
|
+
.then((res) => res.json())
|
|
1646
|
+
.then(setUser); // May run after unmount
|
|
1647
|
+
}, [userId]);
|
|
1648
|
+
|
|
1649
|
+
return user ? <ProfileView user={user} /> : <Loading />;
|
|
1650
|
+
}
|
|
1651
|
+
// If user navigates away during fetch, response still processed
|
|
1652
|
+
```
|
|
1653
|
+
|
|
1654
|
+
**Correct (abort request on unmount):**
|
|
1655
|
+
|
|
1656
|
+
```typescript
|
|
1657
|
+
// screens/UserProfile.tsx
|
|
1658
|
+
export function UserProfile({ userId }: Props) {
|
|
1659
|
+
const [user, setUser] = useState<User | null>(null);
|
|
1660
|
+
|
|
1661
|
+
useEffect(() => {
|
|
1662
|
+
const controller = new AbortController();
|
|
1663
|
+
|
|
1664
|
+
fetch(`/api/users/${userId}`, { signal: controller.signal })
|
|
1665
|
+
.then((res) => res.json())
|
|
1666
|
+
.then(setUser)
|
|
1667
|
+
.catch((err) => {
|
|
1668
|
+
if (err.name !== 'AbortError') throw err;
|
|
1669
|
+
});
|
|
1670
|
+
|
|
1671
|
+
return () => {
|
|
1672
|
+
controller.abort();
|
|
1673
|
+
};
|
|
1674
|
+
}, [userId]);
|
|
1675
|
+
|
|
1676
|
+
return user ? <ProfileView user={user} /> : <Loading />;
|
|
1677
|
+
}
|
|
1678
|
+
// Request cancelled when component unmounts or userId changes
|
|
1679
|
+
```
|
|
1680
|
+
|
|
1681
|
+
**With async/await:**
|
|
1682
|
+
|
|
1683
|
+
```typescript
|
|
1684
|
+
useEffect(() => {
|
|
1685
|
+
const controller = new AbortController();
|
|
1686
|
+
|
|
1687
|
+
async function loadUser() {
|
|
1688
|
+
try {
|
|
1689
|
+
const res = await fetch(`/api/users/${userId}`, { signal: controller.signal });
|
|
1690
|
+
const data = await res.json();
|
|
1691
|
+
setUser(data);
|
|
1692
|
+
} catch (err) {
|
|
1693
|
+
if (err.name !== 'AbortError') throw err;
|
|
1694
|
+
}
|
|
1695
|
+
}
|
|
1696
|
+
|
|
1697
|
+
loadUser();
|
|
1698
|
+
return () => controller.abort();
|
|
1699
|
+
}, [userId]);
|
|
1700
|
+
```
|
|
1701
|
+
|
|
1702
|
+
Reference: [AbortController](https://developer.mozilla.org/en-US/docs/Web/API/AbortController)
|
|
1703
|
+
|
|
1704
|
+
### 6.2 Avoid Inline Objects and Arrays in Props
|
|
1705
|
+
|
|
1706
|
+
**Impact: MEDIUM (prevents unnecessary re-renders from new references)**
|
|
1707
|
+
|
|
1708
|
+
Inline objects and arrays create new references on every render. When passed as props to memoized components, this defeats memoization and causes unnecessary re-renders.
|
|
1709
|
+
|
|
1710
|
+
**Incorrect (new object reference every render):**
|
|
1711
|
+
|
|
1712
|
+
```typescript
|
|
1713
|
+
// screens/SettingsScreen.tsx
|
|
1714
|
+
export function SettingsScreen() {
|
|
1715
|
+
const [darkMode, setDarkMode] = useState(false);
|
|
1716
|
+
|
|
1717
|
+
return (
|
|
1718
|
+
<View>
|
|
1719
|
+
<Toggle value={darkMode} onChange={setDarkMode} />
|
|
1720
|
+
<UserAvatar
|
|
1721
|
+
style={{ width: 50, height: 50 }} // New object every render
|
|
1722
|
+
source={{ uri: user.avatarUrl }} // New object every render
|
|
1723
|
+
/>
|
|
1724
|
+
</View>
|
|
1725
|
+
);
|
|
1726
|
+
}
|
|
1727
|
+
// UserAvatar re-renders on every darkMode toggle
|
|
1728
|
+
```
|
|
1729
|
+
|
|
1730
|
+
**Correct (stable object references):**
|
|
1731
|
+
|
|
1732
|
+
```typescript
|
|
1733
|
+
// screens/SettingsScreen.tsx
|
|
1734
|
+
const avatarStyle = { width: 50, height: 50 };
|
|
1735
|
+
|
|
1736
|
+
export function SettingsScreen() {
|
|
1737
|
+
const [darkMode, setDarkMode] = useState(false);
|
|
1738
|
+
|
|
1739
|
+
const avatarSource = useMemo(
|
|
1740
|
+
() => ({ uri: user.avatarUrl }),
|
|
1741
|
+
[user.avatarUrl]
|
|
1742
|
+
);
|
|
1743
|
+
|
|
1744
|
+
return (
|
|
1745
|
+
<View>
|
|
1746
|
+
<Toggle value={darkMode} onChange={setDarkMode} />
|
|
1747
|
+
<UserAvatar style={avatarStyle} source={avatarSource} />
|
|
1748
|
+
</View>
|
|
1749
|
+
);
|
|
1750
|
+
}
|
|
1751
|
+
// UserAvatar only re-renders when avatarUrl changes
|
|
1752
|
+
```
|
|
1753
|
+
|
|
1754
|
+
**Patterns for stable references:**
|
|
1755
|
+
- Static styles: Define outside component
|
|
1756
|
+
- Dynamic values: Use `useMemo`
|
|
1757
|
+
- Empty arrays: Use module-level `const EMPTY_ARRAY = []`
|
|
1758
|
+
|
|
1759
|
+
Reference: [Avoiding Recreating Objects](https://react.dev/reference/react/useMemo#skipping-re-rendering-of-components)
|
|
1760
|
+
|
|
1761
|
+
### 6.3 Clean Up Subscriptions in useEffect
|
|
1762
|
+
|
|
1763
|
+
**Impact: MEDIUM (prevents memory leaks from orphaned listeners)**
|
|
1764
|
+
|
|
1765
|
+
Subscriptions to events, WebSockets, or external services must be cleaned up when the component unmounts. Without cleanup, the subscription continues running and accumulates memory.
|
|
1766
|
+
|
|
1767
|
+
**Incorrect (subscription never cleaned up):**
|
|
1768
|
+
|
|
1769
|
+
```typescript
|
|
1770
|
+
// hooks/useNotifications.ts
|
|
1771
|
+
export function useNotifications() {
|
|
1772
|
+
const [notifications, setNotifications] = useState<Notification[]>([]);
|
|
1773
|
+
|
|
1774
|
+
useEffect(() => {
|
|
1775
|
+
const subscription = Notifications.addNotificationReceivedListener(
|
|
1776
|
+
(notification) => {
|
|
1777
|
+
setNotifications((prev) => [...prev, notification]);
|
|
1778
|
+
}
|
|
1779
|
+
);
|
|
1780
|
+
// No cleanup - listener keeps running after unmount
|
|
1781
|
+
}, []);
|
|
1782
|
+
|
|
1783
|
+
return notifications;
|
|
1784
|
+
}
|
|
1785
|
+
// Each mount adds a new listener, none removed
|
|
1786
|
+
```
|
|
1787
|
+
|
|
1788
|
+
**Correct (cleanup on unmount):**
|
|
1789
|
+
|
|
1790
|
+
```typescript
|
|
1791
|
+
// hooks/useNotifications.ts
|
|
1792
|
+
export function useNotifications() {
|
|
1793
|
+
const [notifications, setNotifications] = useState<Notification[]>([]);
|
|
1794
|
+
|
|
1795
|
+
useEffect(() => {
|
|
1796
|
+
const subscription = Notifications.addNotificationReceivedListener(
|
|
1797
|
+
(notification) => {
|
|
1798
|
+
setNotifications((prev) => [...prev, notification]);
|
|
1799
|
+
}
|
|
1800
|
+
);
|
|
1801
|
+
|
|
1802
|
+
return () => {
|
|
1803
|
+
subscription.remove();
|
|
1804
|
+
};
|
|
1805
|
+
}, []);
|
|
1806
|
+
|
|
1807
|
+
return notifications;
|
|
1808
|
+
}
|
|
1809
|
+
// Listener properly removed when component unmounts
|
|
1810
|
+
```
|
|
1811
|
+
|
|
1812
|
+
**Common subscriptions needing cleanup:**
|
|
1813
|
+
- Event listeners (keyboard, app state, linking)
|
|
1814
|
+
- WebSocket connections
|
|
1815
|
+
- Firebase/Supabase real-time subscriptions
|
|
1816
|
+
- Notification listeners
|
|
1817
|
+
|
|
1818
|
+
Reference: [useEffect cleanup](https://react.dev/learn/synchronizing-with-effects#how-to-handle-the-effect-firing-twice-in-development)
|
|
1819
|
+
|
|
1820
|
+
### 6.4 Clear Timers on Unmount
|
|
1821
|
+
|
|
1822
|
+
**Impact: MEDIUM (prevents memory leaks and setState errors)**
|
|
1823
|
+
|
|
1824
|
+
Timers (setTimeout, setInterval) continue running after unmount. If the callback updates state, it causes "Can't perform state update on unmounted component" errors and memory leaks.
|
|
1825
|
+
|
|
1826
|
+
**Incorrect (timer not cleared):**
|
|
1827
|
+
|
|
1828
|
+
```typescript
|
|
1829
|
+
// components/Toast.tsx
|
|
1830
|
+
export function Toast({ message, duration = 3000 }: Props) {
|
|
1831
|
+
const [visible, setVisible] = useState(true);
|
|
1832
|
+
|
|
1833
|
+
useEffect(() => {
|
|
1834
|
+
setTimeout(() => {
|
|
1835
|
+
setVisible(false); // Runs even if component unmounted
|
|
1836
|
+
}, duration);
|
|
1837
|
+
}, [duration]);
|
|
1838
|
+
|
|
1839
|
+
if (!visible) return null;
|
|
1840
|
+
return <View style={styles.toast}><Text>{message}</Text></View>;
|
|
1841
|
+
}
|
|
1842
|
+
// If user navigates away quickly, setState called on unmounted component
|
|
1843
|
+
```
|
|
1844
|
+
|
|
1845
|
+
**Correct (timer cleared on unmount):**
|
|
1846
|
+
|
|
1847
|
+
```typescript
|
|
1848
|
+
// components/Toast.tsx
|
|
1849
|
+
export function Toast({ message, duration = 3000 }: Props) {
|
|
1850
|
+
const [visible, setVisible] = useState(true);
|
|
1851
|
+
|
|
1852
|
+
useEffect(() => {
|
|
1853
|
+
const timeoutId = setTimeout(() => {
|
|
1854
|
+
setVisible(false);
|
|
1855
|
+
}, duration);
|
|
1856
|
+
|
|
1857
|
+
return () => {
|
|
1858
|
+
clearTimeout(timeoutId);
|
|
1859
|
+
};
|
|
1860
|
+
}, [duration]);
|
|
1861
|
+
|
|
1862
|
+
if (!visible) return null;
|
|
1863
|
+
return <View style={styles.toast}><Text>{message}</Text></View>;
|
|
1864
|
+
}
|
|
1865
|
+
// Timer cancelled if component unmounts early
|
|
1866
|
+
```
|
|
1867
|
+
|
|
1868
|
+
**For setInterval:**
|
|
1869
|
+
|
|
1870
|
+
```typescript
|
|
1871
|
+
useEffect(() => {
|
|
1872
|
+
const intervalId = setInterval(pollForUpdates, 5000);
|
|
1873
|
+
return () => clearInterval(intervalId);
|
|
1874
|
+
}, []);
|
|
1875
|
+
```
|
|
1876
|
+
|
|
1877
|
+
Reference: [React Native Memory Leak Prevention](https://instamobile.io/blog/react-native-memory-leak-fixes/)
|
|
1878
|
+
|
|
1879
|
+
### 6.5 Limit List Data in Memory
|
|
1880
|
+
|
|
1881
|
+
**Impact: MEDIUM (prevents out-of-memory crashes)**
|
|
1882
|
+
|
|
1883
|
+
Storing thousands of items in state consumes significant memory. Implement pagination or windowing to keep only visible data plus a buffer in memory.
|
|
1884
|
+
|
|
1885
|
+
**Incorrect (loading all data into memory):**
|
|
1886
|
+
|
|
1887
|
+
```typescript
|
|
1888
|
+
// screens/MessageHistory.tsx
|
|
1889
|
+
export function MessageHistory({ conversationId }: Props) {
|
|
1890
|
+
const [messages, setMessages] = useState<Message[]>([]);
|
|
1891
|
+
|
|
1892
|
+
useEffect(() => {
|
|
1893
|
+
// Loads ALL messages - could be 10,000+ items
|
|
1894
|
+
fetchAllMessages(conversationId).then(setMessages);
|
|
1895
|
+
}, [conversationId]);
|
|
1896
|
+
|
|
1897
|
+
return (
|
|
1898
|
+
<FlashList
|
|
1899
|
+
data={messages} // 10,000 items in memory
|
|
1900
|
+
renderItem={({ item }) => <MessageBubble message={item} />}
|
|
1901
|
+
estimatedItemSize={80}
|
|
1902
|
+
/>
|
|
1903
|
+
);
|
|
1904
|
+
}
|
|
1905
|
+
// Memory grows unbounded, crashes on low-end devices
|
|
1906
|
+
```
|
|
1907
|
+
|
|
1908
|
+
**Correct (paginated with cleanup):**
|
|
1909
|
+
|
|
1910
|
+
```typescript
|
|
1911
|
+
// screens/MessageHistory.tsx
|
|
1912
|
+
const PAGE_SIZE = 50;
|
|
1913
|
+
|
|
1914
|
+
export function MessageHistory({ conversationId }: Props) {
|
|
1915
|
+
const [messages, setMessages] = useState<Message[]>([]);
|
|
1916
|
+
const [cursor, setCursor] = useState<string | null>(null);
|
|
1917
|
+
|
|
1918
|
+
const loadMore = useCallback(async () => {
|
|
1919
|
+
const newMessages = await fetchMessages(conversationId, cursor, PAGE_SIZE);
|
|
1920
|
+
setMessages((prev) => {
|
|
1921
|
+
const combined = [...prev, ...newMessages];
|
|
1922
|
+
// Keep max 200 messages in memory
|
|
1923
|
+
return combined.slice(-200);
|
|
1924
|
+
});
|
|
1925
|
+
setCursor(newMessages[newMessages.length - 1]?.id ?? null);
|
|
1926
|
+
}, [conversationId, cursor]);
|
|
1927
|
+
|
|
1928
|
+
return (
|
|
1929
|
+
<FlashList
|
|
1930
|
+
data={messages}
|
|
1931
|
+
renderItem={({ item }) => <MessageBubble message={item} />}
|
|
1932
|
+
onEndReached={loadMore}
|
|
1933
|
+
estimatedItemSize={80}
|
|
1934
|
+
/>
|
|
1935
|
+
);
|
|
1936
|
+
}
|
|
1937
|
+
// Memory capped at ~200 items, older items garbage collected
|
|
1938
|
+
```
|
|
1939
|
+
|
|
1940
|
+
**For infinite lists:** Consider using a library like `react-query` with `useInfiniteQuery` that handles pagination automatically.
|
|
1941
|
+
|
|
1942
|
+
Reference: [FlatList Performance](https://reactnative.dev/docs/optimizing-flatlist-configuration)
|
|
1943
|
+
|
|
1944
|
+
---
|
|
1945
|
+
|
|
1946
|
+
## 7. Async & Data Fetching
|
|
1947
|
+
|
|
1948
|
+
**Impact: MEDIUM**
|
|
1949
|
+
|
|
1950
|
+
Sequential awaits create network waterfalls. AbortController prevents memory leaks on unmount. Parallel fetching reduces load times.
|
|
1951
|
+
|
|
1952
|
+
### 7.1 Batch Related API Calls
|
|
1953
|
+
|
|
1954
|
+
**Impact: MEDIUM (reduces N requests to 1)**
|
|
1955
|
+
|
|
1956
|
+
Making individual API calls for each item creates N network requests. Batch items into a single call when your API supports it to dramatically reduce latency.
|
|
1957
|
+
|
|
1958
|
+
**Incorrect (N requests for N items):**
|
|
1959
|
+
|
|
1960
|
+
```typescript
|
|
1961
|
+
// screens/FriendsActivity.tsx
|
|
1962
|
+
export function FriendsActivity({ friendIds }: Props) {
|
|
1963
|
+
const [activities, setActivities] = useState<Activity[]>([]);
|
|
1964
|
+
|
|
1965
|
+
useEffect(() => {
|
|
1966
|
+
async function loadActivities() {
|
|
1967
|
+
// 10 friends = 10 API calls = 10 round trips
|
|
1968
|
+
const results = await Promise.all(
|
|
1969
|
+
friendIds.map((id) => fetchActivity(id))
|
|
1970
|
+
);
|
|
1971
|
+
setActivities(results.flat());
|
|
1972
|
+
}
|
|
1973
|
+
loadActivities();
|
|
1974
|
+
}, [friendIds]);
|
|
1975
|
+
|
|
1976
|
+
return <ActivityFeed activities={activities} />;
|
|
1977
|
+
}
|
|
1978
|
+
// 10 requests × 100ms each = 1000ms (even with parallelization)
|
|
1979
|
+
```
|
|
1980
|
+
|
|
1981
|
+
**Correct (1 batched request):**
|
|
1982
|
+
|
|
1983
|
+
```typescript
|
|
1984
|
+
// screens/FriendsActivity.tsx
|
|
1985
|
+
export function FriendsActivity({ friendIds }: Props) {
|
|
1986
|
+
const [activities, setActivities] = useState<Activity[]>([]);
|
|
1987
|
+
|
|
1988
|
+
useEffect(() => {
|
|
1989
|
+
async function loadActivities() {
|
|
1990
|
+
// Single API call with all IDs
|
|
1991
|
+
const results = await fetchActivities(friendIds);
|
|
1992
|
+
setActivities(results);
|
|
1993
|
+
}
|
|
1994
|
+
loadActivities();
|
|
1995
|
+
}, [friendIds]);
|
|
1996
|
+
|
|
1997
|
+
return <ActivityFeed activities={activities} />;
|
|
1998
|
+
}
|
|
1999
|
+
// 1 request × 150ms = 150ms (85% faster)
|
|
2000
|
+
```
|
|
2001
|
+
|
|
2002
|
+
**API design pattern:**
|
|
2003
|
+
|
|
2004
|
+
```typescript
|
|
2005
|
+
// Instead of: GET /activity/:userId (called N times)
|
|
2006
|
+
// Use: POST /activities/batch with { userIds: string[] }
|
|
2007
|
+
```
|
|
2008
|
+
|
|
2009
|
+
**Client-side batching:** Libraries like `dataloader` can automatically batch calls within a time window.
|
|
2010
|
+
|
|
2011
|
+
Reference: [DataLoader](https://github.com/graphql/dataloader)
|
|
2012
|
+
|
|
2013
|
+
### 7.2 Cache API Responses Locally
|
|
2014
|
+
|
|
2015
|
+
**Impact: MEDIUM (eliminates redundant network requests)**
|
|
2016
|
+
|
|
2017
|
+
Repeated API calls for the same data waste bandwidth and slow down the app. Use a caching library to store and reuse responses.
|
|
2018
|
+
|
|
2019
|
+
**Incorrect (fetch on every mount):**
|
|
2020
|
+
|
|
2021
|
+
```typescript
|
|
2022
|
+
// screens/ProductDetails.tsx
|
|
2023
|
+
export function ProductDetails({ productId }: Props) {
|
|
2024
|
+
const [product, setProduct] = useState<Product | null>(null);
|
|
2025
|
+
|
|
2026
|
+
useEffect(() => {
|
|
2027
|
+
fetchProduct(productId).then(setProduct);
|
|
2028
|
+
}, [productId]);
|
|
2029
|
+
|
|
2030
|
+
// User navigates away and back: fetches again
|
|
2031
|
+
return product ? <ProductView product={product} /> : <Loading />;
|
|
2032
|
+
}
|
|
2033
|
+
// Same product fetched every time user visits this screen
|
|
2034
|
+
```
|
|
2035
|
+
|
|
2036
|
+
**Correct (cached with TanStack Query):**
|
|
2037
|
+
|
|
2038
|
+
```typescript
|
|
2039
|
+
// screens/ProductDetails.tsx
|
|
2040
|
+
import { useQuery } from '@tanstack/react-query';
|
|
2041
|
+
|
|
2042
|
+
export function ProductDetails({ productId }: Props) {
|
|
2043
|
+
const { data: product, isLoading } = useQuery({
|
|
2044
|
+
queryKey: ['product', productId],
|
|
2045
|
+
queryFn: () => fetchProduct(productId),
|
|
2046
|
+
staleTime: 5 * 60 * 1000, // Consider fresh for 5 minutes
|
|
2047
|
+
});
|
|
2048
|
+
|
|
2049
|
+
// Instant display on return visit, background refetch if stale
|
|
2050
|
+
return product ? <ProductView product={product} /> : <Loading />;
|
|
2051
|
+
}
|
|
2052
|
+
// Cached response shown immediately, refetches in background
|
|
2053
|
+
```
|
|
2054
|
+
|
|
2055
|
+
**Benefits of caching libraries:**
|
|
2056
|
+
- Automatic deduplication of in-flight requests
|
|
2057
|
+
- Background refetching when data is stale
|
|
2058
|
+
- Optimistic updates for mutations
|
|
2059
|
+
- Request retry on failure
|
|
2060
|
+
|
|
2061
|
+
Reference: [TanStack Query](https://tanstack.com/query/latest)
|
|
2062
|
+
|
|
2063
|
+
### 7.3 Defer await Until Value Needed
|
|
2064
|
+
|
|
2065
|
+
**Impact: MEDIUM (20-50% faster by overlapping async work)**
|
|
2066
|
+
|
|
2067
|
+
Start promises immediately but defer `await` until you actually need the value. This allows intermediate synchronous code to run while the promise is pending.
|
|
2068
|
+
|
|
2069
|
+
**Incorrect (await blocks immediately):**
|
|
2070
|
+
|
|
2071
|
+
```typescript
|
|
2072
|
+
// screens/CheckoutScreen.tsx
|
|
2073
|
+
export async function processCheckout(cartId: string, userId: string) {
|
|
2074
|
+
const cart = await fetchCart(cartId); // Blocks here
|
|
2075
|
+
|
|
2076
|
+
// Sync validation could run while cart fetches
|
|
2077
|
+
const shippingValid = validateShippingAddress(userId);
|
|
2078
|
+
if (!shippingValid) throw new Error('Invalid shipping');
|
|
2079
|
+
|
|
2080
|
+
const inventory = await checkInventory(cart.items); // Blocks here
|
|
2081
|
+
return { cart, inventory };
|
|
2082
|
+
}
|
|
2083
|
+
```
|
|
2084
|
+
|
|
2085
|
+
**Correct (start early, await late):**
|
|
2086
|
+
|
|
2087
|
+
```typescript
|
|
2088
|
+
// screens/CheckoutScreen.tsx
|
|
2089
|
+
export async function processCheckout(cartId: string, userId: string) {
|
|
2090
|
+
// Start both fetches immediately
|
|
2091
|
+
const cartPromise = fetchCart(cartId);
|
|
2092
|
+
const inventoryCheck = async () => {
|
|
2093
|
+
const cart = await cartPromise;
|
|
2094
|
+
return checkInventory(cart.items);
|
|
2095
|
+
};
|
|
2096
|
+
|
|
2097
|
+
// Sync validation runs while cart fetches
|
|
2098
|
+
const shippingValid = validateShippingAddress(userId);
|
|
2099
|
+
if (!shippingValid) throw new Error('Invalid shipping');
|
|
2100
|
+
|
|
2101
|
+
// Now await only when we need the values
|
|
2102
|
+
const [cart, inventory] = await Promise.all([
|
|
2103
|
+
cartPromise,
|
|
2104
|
+
inventoryCheck(),
|
|
2105
|
+
]);
|
|
2106
|
+
|
|
2107
|
+
return { cart, inventory };
|
|
2108
|
+
}
|
|
2109
|
+
```
|
|
2110
|
+
|
|
2111
|
+
**Key insight:** The promise starts executing when called, not when awaited. Use this to overlap network and computation.
|
|
2112
|
+
|
|
2113
|
+
Reference: [Async/Await Best Practices](https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Asynchronous/Promises)
|
|
2114
|
+
|
|
2115
|
+
### 7.4 Fetch Independent Data in Parallel
|
|
2116
|
+
|
|
2117
|
+
**Impact: MEDIUM (2-5× faster screen load time)**
|
|
2118
|
+
|
|
2119
|
+
Sequential awaits create network waterfalls where each request waits for the previous to complete. Use `Promise.all()` for independent requests to run them concurrently.
|
|
2120
|
+
|
|
2121
|
+
**Incorrect (sequential requests, 3 round trips):**
|
|
2122
|
+
|
|
2123
|
+
```typescript
|
|
2124
|
+
// screens/Dashboard.tsx
|
|
2125
|
+
export function Dashboard({ userId }: Props) {
|
|
2126
|
+
const [data, setData] = useState<DashboardData | null>(null);
|
|
2127
|
+
|
|
2128
|
+
useEffect(() => {
|
|
2129
|
+
async function loadData() {
|
|
2130
|
+
const user = await fetchUser(userId);
|
|
2131
|
+
const orders = await fetchOrders(userId);
|
|
2132
|
+
const notifications = await fetchNotifications(userId);
|
|
2133
|
+
setData({ user, orders, notifications });
|
|
2134
|
+
}
|
|
2135
|
+
loadData();
|
|
2136
|
+
}, [userId]);
|
|
2137
|
+
|
|
2138
|
+
return data ? <DashboardView data={data} /> : <Loading />;
|
|
2139
|
+
}
|
|
2140
|
+
// Total time: user + orders + notifications (e.g., 300 + 400 + 200 = 900ms)
|
|
2141
|
+
```
|
|
2142
|
+
|
|
2143
|
+
**Correct (parallel requests, 1 round trip):**
|
|
2144
|
+
|
|
2145
|
+
```typescript
|
|
2146
|
+
// screens/Dashboard.tsx
|
|
2147
|
+
export function Dashboard({ userId }: Props) {
|
|
2148
|
+
const [data, setData] = useState<DashboardData | null>(null);
|
|
2149
|
+
|
|
2150
|
+
useEffect(() => {
|
|
2151
|
+
async function loadData() {
|
|
2152
|
+
const [user, orders, notifications] = await Promise.all([
|
|
2153
|
+
fetchUser(userId),
|
|
2154
|
+
fetchOrders(userId),
|
|
2155
|
+
fetchNotifications(userId),
|
|
2156
|
+
]);
|
|
2157
|
+
setData({ user, orders, notifications });
|
|
2158
|
+
}
|
|
2159
|
+
loadData();
|
|
2160
|
+
}, [userId]);
|
|
2161
|
+
|
|
2162
|
+
return data ? <DashboardView data={data} /> : <Loading />;
|
|
2163
|
+
}
|
|
2164
|
+
// Total time: max(user, orders, notifications) (e.g., 400ms)
|
|
2165
|
+
```
|
|
2166
|
+
|
|
2167
|
+
**When NOT to parallelize:** When requests depend on each other (e.g., need user.id for orders).
|
|
2168
|
+
|
|
2169
|
+
Reference: [Promise.all](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/all)
|
|
2170
|
+
|
|
2171
|
+
### 7.5 Refetch Data on Screen Focus
|
|
2172
|
+
|
|
2173
|
+
**Impact: MEDIUM (prevents stale data after hours in background)**
|
|
2174
|
+
|
|
2175
|
+
Mobile apps stay in background for extended periods. Refetch important data when the screen becomes focused to ensure users see current information.
|
|
2176
|
+
|
|
2177
|
+
**Incorrect (data stale after background):**
|
|
2178
|
+
|
|
2179
|
+
```typescript
|
|
2180
|
+
// screens/NotificationsScreen.tsx
|
|
2181
|
+
export function NotificationsScreen() {
|
|
2182
|
+
const [notifications, setNotifications] = useState<Notification[]>([]);
|
|
2183
|
+
|
|
2184
|
+
useEffect(() => {
|
|
2185
|
+
fetchNotifications().then(setNotifications);
|
|
2186
|
+
}, []);
|
|
2187
|
+
|
|
2188
|
+
// User backgrounds app, returns hours later, sees stale notifications
|
|
2189
|
+
return <NotificationList notifications={notifications} />;
|
|
2190
|
+
}
|
|
2191
|
+
```
|
|
2192
|
+
|
|
2193
|
+
**Correct (refetch on screen focus):**
|
|
2194
|
+
|
|
2195
|
+
```typescript
|
|
2196
|
+
// screens/NotificationsScreen.tsx
|
|
2197
|
+
import { useFocusEffect } from '@react-navigation/native';
|
|
2198
|
+
|
|
2199
|
+
export function NotificationsScreen() {
|
|
2200
|
+
const [notifications, setNotifications] = useState<Notification[]>([]);
|
|
2201
|
+
|
|
2202
|
+
useFocusEffect(
|
|
2203
|
+
useCallback(() => {
|
|
2204
|
+
fetchNotifications().then(setNotifications);
|
|
2205
|
+
}, [])
|
|
2206
|
+
);
|
|
2207
|
+
|
|
2208
|
+
// Fresh data loaded every time screen becomes visible
|
|
2209
|
+
return <NotificationList notifications={notifications} />;
|
|
2210
|
+
}
|
|
2211
|
+
```
|
|
2212
|
+
|
|
2213
|
+
**With TanStack Query (automatic):**
|
|
2214
|
+
|
|
2215
|
+
```typescript
|
|
2216
|
+
import { useQuery } from '@tanstack/react-query';
|
|
2217
|
+
import { useFocusEffect } from '@react-navigation/native';
|
|
2218
|
+
|
|
2219
|
+
export function NotificationsScreen() {
|
|
2220
|
+
const { data: notifications, refetch } = useQuery({
|
|
2221
|
+
queryKey: ['notifications'],
|
|
2222
|
+
queryFn: fetchNotifications,
|
|
2223
|
+
});
|
|
2224
|
+
|
|
2225
|
+
useFocusEffect(useCallback(() => { refetch(); }, [refetch]));
|
|
2226
|
+
|
|
2227
|
+
return <NotificationList notifications={notifications ?? []} />;
|
|
2228
|
+
}
|
|
2229
|
+
```
|
|
2230
|
+
|
|
2231
|
+
**Note:** Balance freshness with performance. Not every screen needs refetch on focus.
|
|
2232
|
+
|
|
2233
|
+
Reference: [useFocusEffect](https://reactnavigation.org/docs/use-focus-effect/)
|
|
2234
|
+
|
|
2235
|
+
---
|
|
2236
|
+
|
|
2237
|
+
## 8. Platform Optimizations
|
|
2238
|
+
|
|
2239
|
+
**Impact: LOW-MEDIUM**
|
|
2240
|
+
|
|
2241
|
+
iOS and Android have different performance characteristics. Platform-specific optimizations extract maximum performance from each OS.
|
|
2242
|
+
|
|
2243
|
+
### 8.1 Enable ProGuard for Android Release
|
|
2244
|
+
|
|
2245
|
+
**Impact: LOW-MEDIUM (10-20% smaller APK size)**
|
|
2246
|
+
|
|
2247
|
+
ProGuard shrinks, obfuscates, and optimizes Android code. It removes unused Java/Kotlin code from your APK, reducing size and improving load time.
|
|
2248
|
+
|
|
2249
|
+
**Incorrect (ProGuard disabled):**
|
|
2250
|
+
|
|
2251
|
+
```groovy
|
|
2252
|
+
// android/app/build.gradle
|
|
2253
|
+
android {
|
|
2254
|
+
buildTypes {
|
|
2255
|
+
release {
|
|
2256
|
+
minifyEnabled false
|
|
2257
|
+
shrinkResources false
|
|
2258
|
+
}
|
|
2259
|
+
}
|
|
2260
|
+
}
|
|
2261
|
+
// Full APK includes all library code, even unused
|
|
2262
|
+
```
|
|
2263
|
+
|
|
2264
|
+
**Correct (ProGuard enabled):**
|
|
2265
|
+
|
|
2266
|
+
```groovy
|
|
2267
|
+
// android/app/build.gradle
|
|
2268
|
+
android {
|
|
2269
|
+
buildTypes {
|
|
2270
|
+
release {
|
|
2271
|
+
minifyEnabled true
|
|
2272
|
+
shrinkResources true
|
|
2273
|
+
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
|
|
2274
|
+
}
|
|
2275
|
+
}
|
|
2276
|
+
}
|
|
2277
|
+
```
|
|
2278
|
+
|
|
2279
|
+
```proguard
|
|
2280
|
+
# android/app/proguard-rules.pro
|
|
2281
|
+
# Keep React Native
|
|
2282
|
+
-keep class com.facebook.react.** { *; }
|
|
2283
|
+
-keep class com.facebook.hermes.** { *; }
|
|
2284
|
+
|
|
2285
|
+
# Keep your app's models
|
|
2286
|
+
-keep class com.yourapp.models.** { *; }
|
|
2287
|
+
|
|
2288
|
+
# Common rules for libraries
|
|
2289
|
+
-dontwarn okio.**
|
|
2290
|
+
-dontwarn javax.annotation.**
|
|
2291
|
+
```
|
|
2292
|
+
|
|
2293
|
+
**Benefits:**
|
|
2294
|
+
- 10-20% smaller APK
|
|
2295
|
+
- Code obfuscation for security
|
|
2296
|
+
- Dead code elimination
|
|
2297
|
+
|
|
2298
|
+
**Note:** Test thoroughly after enabling—ProGuard can break reflection-based code.
|
|
2299
|
+
|
|
2300
|
+
Reference: [Enable Proguard](https://reactnative.dev/docs/signed-apk-android#enabling-proguard-to-reduce-the-size-of-the-apk-optional)
|
|
2301
|
+
|
|
2302
|
+
### 8.2 Optimize iOS Text Rendering
|
|
2303
|
+
|
|
2304
|
+
**Impact: LOW-MEDIUM (faster text layout on iOS)**
|
|
2305
|
+
|
|
2306
|
+
iOS Text components can be expensive to render, especially with custom fonts or complex layouts. Use `allowFontScaling={false}` for fixed-size text and avoid unnecessary text nesting.
|
|
2307
|
+
|
|
2308
|
+
**Incorrect (nested Text, slow layout):**
|
|
2309
|
+
|
|
2310
|
+
```typescript
|
|
2311
|
+
// components/PriceDisplay.tsx
|
|
2312
|
+
export function PriceDisplay({ price, currency }: Props) {
|
|
2313
|
+
return (
|
|
2314
|
+
<View>
|
|
2315
|
+
<Text style={styles.price}>
|
|
2316
|
+
<Text style={styles.currency}>{currency}</Text>
|
|
2317
|
+
<Text style={styles.amount}>{price.toFixed(2)}</Text>
|
|
2318
|
+
<Text style={styles.decimal}>.{(price % 1).toFixed(2).slice(2)}</Text>
|
|
2319
|
+
</Text>
|
|
2320
|
+
</View>
|
|
2321
|
+
);
|
|
2322
|
+
}
|
|
2323
|
+
// Nested Text requires multiple layout passes
|
|
2324
|
+
```
|
|
2325
|
+
|
|
2326
|
+
**Correct (flat structure, optimized):**
|
|
2327
|
+
|
|
2328
|
+
```typescript
|
|
2329
|
+
// components/PriceDisplay.tsx
|
|
2330
|
+
export function PriceDisplay({ price, currency }: Props) {
|
|
2331
|
+
const formattedPrice = `${currency}${price.toFixed(2)}`;
|
|
2332
|
+
|
|
2333
|
+
return (
|
|
2334
|
+
<Text
|
|
2335
|
+
style={styles.price}
|
|
2336
|
+
allowFontScaling={false} // Skip accessibility scaling calculation
|
|
2337
|
+
numberOfLines={1} // Single line, skip line break calculation
|
|
2338
|
+
>
|
|
2339
|
+
{formattedPrice}
|
|
2340
|
+
</Text>
|
|
2341
|
+
);
|
|
2342
|
+
}
|
|
2343
|
+
// Single Text node, minimal layout calculation
|
|
2344
|
+
```
|
|
2345
|
+
|
|
2346
|
+
**When to use `allowFontScaling={false}`:**
|
|
2347
|
+
- Icons and logos
|
|
2348
|
+
- Fixed-size UI elements
|
|
2349
|
+
- Performance-critical list items
|
|
2350
|
+
|
|
2351
|
+
**Note:** Don't disable font scaling for body text—accessibility matters.
|
|
2352
|
+
|
|
2353
|
+
Reference: [Text Component](https://reactnative.dev/docs/text)
|
|
2354
|
+
|
|
2355
|
+
### 8.3 Reduce Android Overdraw
|
|
2356
|
+
|
|
2357
|
+
**Impact: LOW-MEDIUM (20-30% rendering improvement on Android)**
|
|
2358
|
+
|
|
2359
|
+
Overdraw occurs when the same pixel is drawn multiple times per frame. On Android, excessive overdraw significantly impacts performance. Remove unnecessary backgrounds and flatten view hierarchies.
|
|
2360
|
+
|
|
2361
|
+
**Incorrect (multiple overlapping backgrounds):**
|
|
2362
|
+
|
|
2363
|
+
```typescript
|
|
2364
|
+
// components/ProductCard.tsx
|
|
2365
|
+
export function ProductCard({ product }: Props) {
|
|
2366
|
+
return (
|
|
2367
|
+
<View style={{ backgroundColor: 'white' }}>
|
|
2368
|
+
<View style={{ backgroundColor: 'white', padding: 16 }}>
|
|
2369
|
+
<View style={{ backgroundColor: '#f5f5f5', borderRadius: 8 }}>
|
|
2370
|
+
<Image source={{ uri: product.image }} style={styles.image} />
|
|
2371
|
+
</View>
|
|
2372
|
+
<View style={{ backgroundColor: 'white' }}>
|
|
2373
|
+
<Text>{product.name}</Text>
|
|
2374
|
+
</View>
|
|
2375
|
+
</View>
|
|
2376
|
+
</View>
|
|
2377
|
+
);
|
|
2378
|
+
}
|
|
2379
|
+
// Same pixels painted 3-4 times per frame
|
|
2380
|
+
```
|
|
2381
|
+
|
|
2382
|
+
**Correct (single background, flat hierarchy):**
|
|
2383
|
+
|
|
2384
|
+
```typescript
|
|
2385
|
+
// components/ProductCard.tsx
|
|
2386
|
+
export function ProductCard({ product }: Props) {
|
|
2387
|
+
return (
|
|
2388
|
+
<View style={styles.card}>
|
|
2389
|
+
<Image source={{ uri: product.image }} style={styles.image} />
|
|
2390
|
+
<Text style={styles.name}>{product.name}</Text>
|
|
2391
|
+
</View>
|
|
2392
|
+
);
|
|
2393
|
+
}
|
|
2394
|
+
|
|
2395
|
+
const styles = StyleSheet.create({
|
|
2396
|
+
card: {
|
|
2397
|
+
backgroundColor: 'white',
|
|
2398
|
+
padding: 16,
|
|
2399
|
+
borderRadius: 8,
|
|
2400
|
+
},
|
|
2401
|
+
image: { /* ... */ },
|
|
2402
|
+
name: { /* ... */ },
|
|
2403
|
+
});
|
|
2404
|
+
// Each pixel painted only once
|
|
2405
|
+
```
|
|
2406
|
+
|
|
2407
|
+
**Debug overdraw on Android:**
|
|
2408
|
+
1. Developer Options > Debug GPU Overdraw
|
|
2409
|
+
2. Blue = 1× overdraw, Green = 2×, Pink = 3×, Red = 4×
|
|
2410
|
+
3. Target: mostly blue with minimal green
|
|
2411
|
+
|
|
2412
|
+
Reference: [Android Overdraw](https://developer.android.com/topic/performance/rendering/overdraw)
|
|
2413
|
+
|
|
2414
|
+
### 8.4 Use Platform-Specific Optimizations Conditionally
|
|
2415
|
+
|
|
2416
|
+
**Impact: LOW-MEDIUM (2-3× better performance on target platform)**
|
|
2417
|
+
|
|
2418
|
+
iOS and Android have different performance characteristics. Apply platform-specific optimizations where they matter most.
|
|
2419
|
+
|
|
2420
|
+
**Incorrect (same approach for both platforms):**
|
|
2421
|
+
|
|
2422
|
+
```typescript
|
|
2423
|
+
// components/AnimatedCard.tsx
|
|
2424
|
+
import { Animated } from 'react-native';
|
|
2425
|
+
|
|
2426
|
+
export function AnimatedCard({ children, visible }: Props) {
|
|
2427
|
+
const opacity = useRef(new Animated.Value(0)).current;
|
|
2428
|
+
|
|
2429
|
+
useEffect(() => {
|
|
2430
|
+
Animated.timing(opacity, {
|
|
2431
|
+
toValue: visible ? 1 : 0,
|
|
2432
|
+
duration: 300,
|
|
2433
|
+
// Can't use native driver with shadow on Android
|
|
2434
|
+
useNativeDriver: true,
|
|
2435
|
+
}).start();
|
|
2436
|
+
}, [visible]);
|
|
2437
|
+
|
|
2438
|
+
return (
|
|
2439
|
+
<Animated.View style={[styles.card, { opacity }]}>
|
|
2440
|
+
{children}
|
|
2441
|
+
</Animated.View>
|
|
2442
|
+
);
|
|
2443
|
+
}
|
|
2444
|
+
```
|
|
2445
|
+
|
|
2446
|
+
**Correct (platform-optimized):**
|
|
2447
|
+
|
|
2448
|
+
```typescript
|
|
2449
|
+
// components/AnimatedCard.tsx
|
|
2450
|
+
import { Platform, Animated } from 'react-native';
|
|
2451
|
+
import Reanimated, { useAnimatedStyle, withTiming } from 'react-native-reanimated';
|
|
2452
|
+
|
|
2453
|
+
export function AnimatedCard({ children, visible }: Props) {
|
|
2454
|
+
// iOS: Use Animated API (lightweight for simple opacity)
|
|
2455
|
+
// Android: Use Reanimated (better performance with shadows)
|
|
2456
|
+
const AnimatedContainer = Platform.select({
|
|
2457
|
+
ios: AnimatedContainerIOS,
|
|
2458
|
+
android: AnimatedContainerAndroid,
|
|
2459
|
+
})!;
|
|
2460
|
+
|
|
2461
|
+
return <AnimatedContainer visible={visible}>{children}</AnimatedContainer>;
|
|
2462
|
+
}
|
|
2463
|
+
|
|
2464
|
+
function AnimatedContainerIOS({ children, visible }: Props) {
|
|
2465
|
+
const opacity = useRef(new Animated.Value(0)).current;
|
|
2466
|
+
useEffect(() => {
|
|
2467
|
+
Animated.timing(opacity, {
|
|
2468
|
+
toValue: visible ? 1 : 0,
|
|
2469
|
+
duration: 300,
|
|
2470
|
+
useNativeDriver: true,
|
|
2471
|
+
}).start();
|
|
2472
|
+
}, [visible]);
|
|
2473
|
+
return <Animated.View style={{ opacity }}>{children}</Animated.View>;
|
|
2474
|
+
}
|
|
2475
|
+
|
|
2476
|
+
function AnimatedContainerAndroid({ children, visible }: Props) {
|
|
2477
|
+
const style = useAnimatedStyle(() => ({
|
|
2478
|
+
opacity: withTiming(visible ? 1 : 0, { duration: 300 }),
|
|
2479
|
+
}));
|
|
2480
|
+
return <Reanimated.View style={style}>{children}</Reanimated.View>;
|
|
2481
|
+
}
|
|
2482
|
+
```
|
|
2483
|
+
|
|
2484
|
+
**Common platform differences:**
|
|
2485
|
+
- Shadow rendering (expensive on Android)
|
|
2486
|
+
- List scrolling (FlashList critical on Android)
|
|
2487
|
+
- Font rendering (iOS more efficient)
|
|
2488
|
+
|
|
2489
|
+
Reference: [Platform Module](https://reactnative.dev/docs/platform-specific-code)
|
|
2490
|
+
|
|
2491
|
+
---
|
|
2492
|
+
|
|
2493
|
+
## References
|
|
2494
|
+
|
|
2495
|
+
1. [https://reactnative.dev/docs/performance](https://reactnative.dev/docs/performance)
|
|
2496
|
+
2. [https://expo.dev/blog/best-practices-for-reducing-lag-in-expo-apps](https://expo.dev/blog/best-practices-for-reducing-lag-in-expo-apps)
|
|
2497
|
+
3. [https://shopify.github.io/flash-list/](https://shopify.github.io/flash-list/)
|
|
2498
|
+
4. [https://docs.swmansion.com/react-native-reanimated/docs/guides/performance/](https://docs.swmansion.com/react-native-reanimated/docs/guides/performance/)
|
|
2499
|
+
5. [https://www.callstack.com/ebooks/the-ultimate-guide-to-react-native-optimization](https://www.callstack.com/ebooks/the-ultimate-guide-to-react-native-optimization)
|
|
2500
|
+
6. [https://reactnative.dev/docs/hermes](https://reactnative.dev/docs/hermes)
|
|
2501
|
+
7. [https://docs.expo.dev/versions/latest/sdk/image/](https://docs.expo.dev/versions/latest/sdk/image/)
|
|
2502
|
+
8. [https://docs.expo.dev/versions/latest/sdk/splash-screen/](https://docs.expo.dev/versions/latest/sdk/splash-screen/)
|
|
2503
|
+
|
|
2504
|
+
---
|
|
2505
|
+
|
|
2506
|
+
## Source Files
|
|
2507
|
+
|
|
2508
|
+
This document was compiled from individual reference files. For detailed editing or extension:
|
|
2509
|
+
|
|
2510
|
+
| File | Description |
|
|
2511
|
+
|------|-------------|
|
|
2512
|
+
| [references/_sections.md](references/_sections.md) | Category definitions and impact ordering |
|
|
2513
|
+
| [assets/templates/_template.md](assets/templates/_template.md) | Template for creating new rules |
|
|
2514
|
+
| [SKILL.md](SKILL.md) | Quick reference entry point |
|
|
2515
|
+
| [metadata.json](metadata.json) | Version and reference URLs |
|