# uta-ecosystem: UTA Ecosystem Overview URL: https://dev.updatetheapp.com/docs/uta-ecosystem Documentation: https://dev.updatetheapp.com/docs/uta-ecosystem Understanding how the UTA ecosystem components work together to accelerate React Native development while maintaining platform independence. *** title: UTA Ecosystem Overview description: Understanding how the UTA ecosystem components work together to accelerate React Native development while maintaining platform independence. --------------------------------------------------------------------------------------------------------------------------------------------------------- import { Callout } from 'fumadocs-ui/components/callout'; import { Steps, Step } from 'fumadocs-ui/components/steps'; import { Card, Cards } from 'fumadocs-ui/components/card'; import { Tabs, Tab } from 'fumadocs-ui/components/tabs'; # UTA Ecosystem Overview ## Overview The UTA ecosystem provides standardized React Native development patterns and deployment solutions that work independently or together. Rather than creating vendor lock-in, UTA offers proven architectural patterns that enhance any React Native project, regardless of your chosen deployment strategy. This approach eliminates architectural decision fatigue while maintaining complete freedom in your deployment choices. Whether you use Expo Updates, CodePush, updatetheapp.com, or any other solution, UTA patterns provide the foundation for scalable, maintainable applications. **What Makes UTA Different** UTA provides architectural guidance that works with **any** deployment solution. The patterns stand on their own merit and can be adopted independently of any specific platform or service. ## Purpose & Scope This document explains how UTA's components work together while maintaining independence, helping you understand how to adopt UTA patterns regardless of your deployment choices. **Target Audience**: Architects, technical leads, and development teams evaluating standardized development approaches for React Native projects. ## Prerequisites To get the most out of this architectural overview, we recommend you first understand these key areas and review the related guides: * Basic familiarity with React Native development patterns and common challenges * Understanding of modern JavaScript/TypeScript development workflows and tooling * Awareness of over-the-air (OTA) update concepts and deployment options in the React Native ecosystem For implementation-specific guidance and step-by-step instructions, see the [UI Development Getting Started](/docs/ui-development/getting-started) guide after understanding the ecosystem foundations. ## Core Ecosystem Components ### Independent Yet Complementary Components UTA consists of three independent components that can work together or separately: Works with any deployment solution One deployment option among many Educational resource for any React Native project **Key Principle**: Each component provides independent value while working well together when combined. ### Platform-Agnostic Development Foundation **Purpose**: A production-ready React Native template with battle-tested architectural patterns **Core Principles**: * **Deployment Neutral**: Works with Expo Updates, CodePush, updatetheapp.com, or any OTA solution * **Proven Patterns**: Pre-selected architectural approaches that have been tested across different projects * **Immediate Productivity**: Fork, setup, and start building features from day one **What's Included**: * Feature-Domain Hybrid architecture (UI organization + business logic separation) * Standardized state management patterns with Zustand * API integration patterns using TanStack Query * Comprehensive testing strategies and utilities * AI-optimized code patterns for better development assistance **Benefits for Your Team**: * Eliminates architectural decision paralysis across projects * Ensures consistent code patterns regardless of deployment choice * Dramatically reduces onboarding time for new developers * Provides immediate AI assistance with high accuracy ### Choose Your Deployment Strategy **Purpose**: UTA patterns work with any React Native deployment approach **Supported Deployment Options**: * **Expo Updates**: Native integration with Expo's OTA update system * **CodePush**: Microsoft's OTA update solution for React Native * **updatetheapp.com**: UTA's own deployment platform with advanced features * **Custom Solutions**: Any deployment strategy that supports JavaScript bundle updates **Platform-Specific Optimizations**: * Configuration templates for popular deployment services * Environment-specific build scripts and configurations * Deployment workflow examples for different platforms * Migration guides between deployment solutions **No Vendor Lock-in**: * Template code remains independent of deployment choice * Switch deployment platforms without architectural changes * Test different deployment strategies without code modifications ### Educational Resource Architecture **Purpose**: Comprehensive documentation that serves development needs regardless of deployment choice **Documentation Strategy**: * **Development Patterns**: Architectural guidance that applies to any React Native project * **Deployment Examples**: Show multiple deployment options, not just one platform * **AI Training Context**: Machine-readable patterns optimized for AI assistance **Key Benefits**: * Learn patterns that transfer to any React Native project * Compare different deployment strategies with real examples * Access consistent architectural guidance regardless of your platform choices * Get AI assistance that understands modern React Native patterns **Educational Philosophy**: * Explain the "why" behind architectural decisions * Show multiple implementation approaches when relevant * Provide migration paths between different solutions * Focus on long-term maintainability over vendor-specific features ## How the Components Work Together The UTA ecosystem creates value through **independent excellence** rather than forced integration: **Template** → **Documentation** → **Deployment Choice** * **react-native-uta template** provides proven architectural patterns that work anywhere * **UTA DevHub documentation** offers comprehensive guidance for any React Native project * **Your deployment choice** can be any solution that fits your needs (Expo, CodePush, updatetheapp.com, custom) This independent approach ensures that adopting UTA patterns doesn't lock you into any specific deployment platform, while providing examples of how these patterns work well with various deployment strategies. ## Documentation Strategy: Platform-Neutral Guidance UTA documentation focuses on architectural patterns that work with any deployment solution, while providing specific examples for popular platforms. ### Development Focus: UTA DevHub (This Site) **Purpose**: UTA DevHub - comprehensive development guide for platform-agnostic React Native patterns\ **Target Audience**: Developers building applications with modern React Native patterns **What You'll Find Here**: * Architecture patterns and design decisions with detailed explanations * UI component development guidelines and best practices * State management implementation using modern patterns * API integration approaches with TanStack Query * Development workflows that work with any deployment platform ### Platform-Specific Examples **Purpose**: Show how UTA patterns work with different deployment solutions\ **Target Audience**: Teams evaluating or implementing specific deployment strategies **Deployment Examples Covered**: * Expo Updates configuration and workflow * CodePush integration and release management * updatetheapp.com deployment and monitoring * Custom deployment pipeline setup **Educational Approach** * **How to architect** → UTA DevHub patterns (platform-neutral) * **How to deploy** → Multiple examples showing different platforms * **How to choose** → Decision frameworks for evaluating deployment options ## Complete Developer Journey Understanding how UTA works in practice, regardless of your deployment choice: ### Foundation: Start with Proven Architecture Begin with the react-native-uta template that provides battle-tested patterns: **What Happens**: * Fork a production-ready template with established architectural patterns * Run automated setup that configures your development environment * Access comprehensive documentation and AI-optimized patterns **Immediate Benefits**: * Day one productivity with zero architectural decisions required * Consistent patterns that work with any deployment strategy * AI assistance that understands your codebase from the start **Why This Matters**: Instead of spending weeks debating state management libraries or folder structures, your team starts building features immediately with patterns that work everywhere. ### Development: Build with Proven Patterns Develop features using pre-established, deployment-agnostic architectural patterns: **Standardized Patterns**: * **State Management**: Zustand for predictable, scalable state handling * **API Layer**: TanStack Query for robust data fetching and caching * **UI Architecture**: Feature-Domain Hybrid system for maintainable organization * **Testing Strategy**: Comprehensive testing patterns built into the template **Key Principle**: Follow proven patterns that enhance maintainability regardless of your deployment platform. **Team Benefits**: Code reviews become faster, onboarding becomes easier, and everyone writes code that works consistently across different deployment environments. ### Deployment: Choose Your Strategy Deploy using whichever solution best fits your needs: **Popular Options**: ```bash # Expo Updates expo publish --channel production # CodePush appcenter codepush release-react MyApp-iOS # updatetheapp.com uta deploy --production # Custom solution npm run build:release && ./deploy-script.sh ``` **Business Impact**: Ship updates using the deployment strategy that best matches your team's needs and constraints. **Real-World Flexibility**: Teams have successfully used UTA patterns with Expo Updates for rapid prototyping, CodePush for enterprise deployment, updatetheapp.com for managed deployment, and custom solutions for specialized requirements. ### Iteration: Maintain Development Velocity Maintain momentum with your chosen deployment approach: **Continuous Improvement**: * Monitor application performance and user behavior * Push feature updates using your preferred deployment method * Switch deployment platforms if needs change (architectural patterns remain the same) * All operations work independently of UTA-specific services **Long-term Benefits**: Your team maintains high velocity because architectural patterns remain consistent even if deployment strategies evolve. ## Why This Ecosystem Approach Works ### Individual Developer Benefits **Platform-Independent Productivity**: * **(Do ✅) Day 1 Productivity**: Fork and start building immediately with any deployment target * **(Do ✅) Eliminate Decision Fatigue**: Proven patterns work consistently across platforms * **(Do ✅) AI-Assisted Development**: Achieve high accuracy with standardized patterns * **(Do ✅) Deployment Flexibility**: Ship updates using the platform that best fits your project **Learning & Growth**: * Learn industry-standard patterns that transfer to any React Native project * Focus on business logic instead of architectural plumbing * Get immediate feedback through consistent, predictable development patterns * Build confidence with patterns that work regardless of deployment choice ### Team Collaboration Benefits **Organizational Efficiency**: * **(Do ✅) Reduced Context Switching**: Standardized patterns mean less time debating approaches * **(Do ✅) Faster Onboarding**: New developers become productive within days, not weeks * **(Do ✅) Streamlined Code Reviews**: Everyone follows consistent patterns and conventions * **(Do ✅) Deployment Flexibility**: Team can evaluate and switch deployment platforms without architectural changes **Collaboration Improvements**: * Shared vocabulary and patterns reduce miscommunication * Standardized architecture makes it easy to work on any part of the codebase * Consistent testing patterns ensure reliable quality across all deployment targets * Documentation that everyone can contribute to and benefit from ### Strategic Business Advantages **Competitive Benefits**: * **(Do ✅) Ship Faster**: From code to production using your preferred deployment method * **(Do ✅) Risk Reduction**: Proven patterns reduce architectural risks across platforms * **(Do ✅) Platform Independence**: Avoid vendor lock-in with deployment-agnostic patterns * **(Do ✅) Strategic Flexibility**: Evaluate and adopt different deployment strategies as needs evolve **Operational Efficiency**: * Reduced development costs through faster onboarding and fewer architectural mistakes * Lower maintenance overhead with standardized, well-documented patterns * Improved reliability through battle-tested architectural decisions * Better resource allocation since teams spend time on features, not infrastructure decisions **Important Considerations**: * **(Be Aware ❗) Learning Investment**: Teams need time to adapt to standardized patterns initially * **(Be Aware ❗) Platform Evaluation**: Teams should evaluate deployment options based on their specific needs * **(Consider 🤔) Deployment Strategy**: Choose deployment platforms based on requirements, not architectural constraints ## Implementation Considerations ### Performance & Scalability **Deployment-Agnostic Optimization**: * Monitor bundle size to ensure fast updates regardless of deployment platform * Implement code splitting patterns that work with any OTA update system * Balance update frequency with user experience across different deployment strategies **Application Performance**: * Built-in performance monitoring through standardized template patterns * Established patterns for code splitting and lazy loading * Proven state management patterns that scale with application complexity ### Security & Reliability **Platform-Independent Security**: * Secure coding patterns that work with any deployment strategy * Authentication and authorization patterns that don't depend on specific platforms * Data protection strategies that remain consistent across deployment environments **Operational Security**: * Template includes security best practices for any deployment platform * Audit trail capabilities that work with multiple deployment solutions * Monitoring and alerting patterns that adapt to different platforms ### Scalability Aspects **Team Scaling**: * Standardized patterns support teams from 1 to 100+ developers * Consistent architecture reduces coordination overhead as teams grow * Documentation and AI assistance provide self-service onboarding **Technical Scaling**: * Architecture patterns proven to work at scale with real-world applications * Built-in performance patterns and optimization strategies * Deployment infrastructure patterns designed for enterprise-scale applications ## Getting Started Understand UTA's architectural principles and design decisions Get hands-on with the react-native-uta template ## Related Documents For deeper technical implementation details, explore these related resources: * [UI Development Getting Started](/docs/ui-development/getting-started) - Hands-on guide to building with UTA patterns * [State Management Architecture](/docs/state-management/state-management-architecture) - Comprehensive guide to modern state management patterns * [API Client Architecture](/docs/api/client-architecture) - TanStack Query implementation patterns and best practices * [Team Onboarding Checklist](/docs/guides/team-onboarding-checklist) - Step-by-step guide for new team members **The UTA Philosophy** UTA represents proven React Native architectural patterns that enhance any project, regardless of deployment choice. By standardizing on battle-tested approaches, your team can focus on what matters most: building exceptional features for your users. # UTA Architecture: Architecture Documentation URL: https://dev.updatetheapp.com/docs/architecture Documentation: https://dev.updatetheapp.com/docs/architecture Core architectural patterns and design philosophy of the UTA mobile application template *** title: Architecture Documentation description: Core architectural patterns and design philosophy of the UTA mobile application template ----------------------------------------------------------------------------------------------------- import { Card, Cards } from 'fumadocs-ui/components/card'; import { ChevronRightIcon } from 'lucide-react'; # Architecture Documentation ## Overview The UTA mobile application architecture follows a modular, domain-driven design that emphasizes separation of concerns, testability, and maintainability. Our architecture is designed to scale with your application's complexity while providing clear boundaries between different parts of your system. Our architecture is organized into several interconnected layers: * **Core Framework** provides fundamental infrastructure and utilities * **Domain Modules** encapsulate business logic and domain-specific functionality * **Feature Modules** implement complete user-facing features * **UI Components** create the visual interface and user interactions These layers interact with state management systems and external integrations to form a cohesive application architecture. ## Key Architecture Documents } title="Design Philosophy & Tradeoffs" description="Comprehensive evaluation of architectural strengths, limitations, and decision-making framework for adoption." href="/docs/architecture/design-philosophy-and-tradeoffs" /> } title="Core Architecture" description="Understand the foundational principles, patterns, and structure of the UTA mobile application template." href="/docs/architecture/core-architecture" /> } title="Project Structure" description="Learn about the file and folder organization, and how different parts of the codebase relate to each other." href="/docs/architecture/project-structure" /> } title="Domain Architecture" description="Explore our domain-driven design approach that creates clear boundaries between different parts of your application." href="/docs/architecture/domain-architecture" /> ## Architecture Philosophy Our architecture is built around these core principles: 1. **Separation of Concerns** - Each module has a single responsibility and clear boundaries 2. **Domain-Driven Design** - Business logic is organized into cohesive domains 3. **Unidirectional Data Flow** - Predictable state management with clear update patterns 4. **Feature-First Organization** - Code organized by feature rather than technical layer 5. **Testability** - Architecture designed to facilitate comprehensive testing strategies ## Related Areas The architecture works together with these other important aspects of the application: } title="API Architecture" description="Learn how our API clients integrate with backend services, including authentication and error handling." href="/docs/api" /> } title="State Management" description="Understand our approach to managing application state, both globally and locally." href="/docs/state-management" /> } title="Feature Implementation" description="Discover patterns for implementing features in a consistent way across the application." href="/docs/features" /> # UTA Architecture: Design Philosophy & Tradeoffs URL: https://dev.updatetheapp.com/docs/architecture/design-philosophy-and-tradeoffs Documentation: https://dev.updatetheapp.com/docs/architecture/design-philosophy-and-tradeoffs Honest evaluation of UTA's architectural patterns, design decisions, and guidance for teams considering adoption of our Feature-Domain Hybrid approach. *** title: Design Philosophy & Tradeoffs description: Honest evaluation of UTA's architectural patterns, design decisions, and guidance for teams considering adoption of our Feature-Domain Hybrid approach. -------------------------------------------------------------------------------------------------------------------------------------------------------------------- import { Card, Cards } from 'fumadocs-ui/components/card'; import { Callout } from 'fumadocs-ui/components/callout'; import { Tab, Tabs } from 'fumadocs-ui/components/tabs'; # Design Philosophy & Tradeoffs ## Overview This document provides an honest evaluation of our UTA design philosophy and the specific architectural patterns we've chosen. We believe in transparency about both the benefits and costs of our approach, helping you understand when our Feature-Domain Hybrid strategy aligns with your project needs. Rather than claiming our architecture fits every scenario, we examine real-world tradeoffs, learning investments, and situations where simpler approaches might be more appropriate. This balanced perspective helps teams make informed decisions about whether our architectural patterns match their specific context and goals. ## Purpose & Scope **Target Audience**: Technical decision-makers, senior developers, and team leads evaluating architectural approaches for React Native projects, particularly those considering domain-driven design patterns. **What This Document Addresses**: * Our core architectural decisions and the problems they solve * Honest assessment of learning investment and complexity costs * Decision framework for evaluating if our patterns fit your project * Team readiness considerations and adoption strategies * Alternative architectural approaches for different project types **What This Document Does NOT Cover**: * Step-by-step implementation details (see our [Implementation Guides](/docs/guides)) * Specific API documentation (see [API Reference](/docs/api)) * Development workflow specifics (see [Getting Started Guide](/docs/guides/getting-started) and [Team Onboarding](/docs/guides/team-onboarding-checklist)) ## Prerequisites To get the most value from this document, we recommend first exploring these foundational resources: * **[Core Architecture Reference](/docs/architecture/core-architecture)** - This will help you understand our technology stack and foundational patterns * **[Domain Architecture Guide](/docs/architecture/domain-architecture)** - Essential for understanding our domain-driven design approach * **[Project Structure](/docs/architecture/project-structure)** - Useful for seeing how our file organization philosophy works in practice **Technical Background**: You'll get more from the architectural discussions if you're familiar with React Native development, TypeScript, and modern state management concepts (like the difference between server state and client state). **Technical Foundation**: Our architectural patterns work with any modern React Native setup - whether you're using Expo Router + CNG, React Native CLI, or custom configurations. The patterns focus on code organization and business logic, not the underlying technical foundation. ## UTA Architectural Design Philosophy ### Core Organizational Strategy: Feature-Domain Hybrid After working with various React Native project structures, we've developed a **Feature-Domain Hybrid** approach that addresses common pain points we've encountered. This strategy combines the intuitive nature of feature-first organization with the business logic clarity of domain-driven design. **Why We Chose This Approach**: Traditional feature-only organization works great initially, but as business logic becomes complex, you end up with logic scattered across UI components. Pure domain-driven approaches can feel abstract for mobile apps. Our hybrid approach gives you the best of both worlds: Domains Domains --> Infrastructure style Features fill:#e3f2fd style Domains fill:#f3e5f5 style Infrastructure fill:#e8f5e8 `} /> **Understanding the Layers**: * **Features** handle everything users see and interact with (screens, navigation, UI state) * **Domains** contain pure business logic that could work in any UI context * **Infrastructure** provides shared services that both layers can use This separation means you can change how features look and behave without touching business rules, and you can modify business logic without breaking UI components. ### Key Architectural Decisions & Their Benefits We've made several opinionated decisions based on real-world React Native development challenges we've encountered. Each decision addresses specific pain points that teams commonly face as projects grow: #### Feature-Domain Hybrid Organization * **Features for UI**: Screen components, navigation, and user-facing logic grouped by user workflow * **Domains for Business Logic**: Pure business rules and data models isolated from UI concerns * **Predictable Structure**: Teams know exactly where to find and place different types of code * **Scaling Benefits**: New features don't require restructuring existing code organization #### Separated State Management Strategy * **Server State**: TanStack Query handles API data, caching, and synchronization * **Client State**: Zustand manages application-specific UI state and user preferences * **No State Confusion**: Clear boundaries eliminate debates about where state belongs * **Performance Optimized**: Right tool for each type of state management need #### TypeScript-First Design * **Domain Types**: Business entities defined as TypeScript interfaces/types * **API Contracts**: Full type safety from backend responses to UI components * **Component Props**: Comprehensive typing for all React Native components * **Developer Experience**: IDE support, refactoring safety, and runtime error prevention #### Testability by Design * **Pure Business Logic**: Domain functions are easily unit tested without UI dependencies * **Dependency Injection**: Services can be mocked and replaced for testing * **Component Isolation**: UI components tested independently from business logic * **Integration Testing**: Clear boundaries make integration testing more focused and reliable ### Problems This Architecture Solves These patterns emerged from real challenges we've faced in React Native projects. Understanding these pain points helps explain why we've made specific architectural choices: **Why This Matters**: These aren't theoretical problems - they're based on real experiences from teams scaling React Native applications. Each pattern we've chosen addresses specific pain points that become critical as projects grow beyond initial prototypes. ### Architectural Principles Building on the CNG foundation, the UTA architecture is guided by these principles: Scalable DDD --> Maintainable UDF --> Predictable FTO --> Collaborative Test --> Quality style SoC fill:#e1f5fe style DDD fill:#f3e5f5 style UDF fill:#e8f5e8 style FTO fill:#fff3e0 style Test fill:#fce4ec `} /> #### 1. Separation of Concerns * **Business Logic**: Isolated in domain modules, independent of UI * **Presentation Logic**: Contained within feature modules and components * **Infrastructure**: Shared utilities and configuration separated from business rules #### 2. Domain-Driven Design * **Business-Centric Organization**: Code structure reflects business domains * **Bounded Contexts**: Clear boundaries prevent domain logic bleeding * **Ubiquitous Language**: Consistent terminology across code and documentation #### 3. Unidirectional Data Flow * **Predictable State Changes**: Clear patterns for state mutations * **Server State Management**: TanStack Query handles API interactions * **Client State Isolation**: Zustand manages application-specific state #### 4. Feature-First Organization * **User-Centric Structure**: Features organized around user workflows * **Colocated Code**: Related components, screens, and navigation grouped together * **Encapsulation**: Features contain their own state and navigation logic #### 5. Testability by Design * **Dependency Injection**: Services can be easily mocked for testing * **Pure Functions**: Business logic written as testable pure functions * **Component Isolation**: UI components testable independently of business logic ### When This Architecture Excels ## Honest Assessment of Architectural Tradeoffs ### When Our Patterns Create Overhead **Honest Reality**: Our architecture is optimized for projects that will grow in complexity over time. If you're building a simple app that will stay simple, this approach may feel like overkill. #### Learning Investment You Should Expect We believe in being upfront about the learning curve. Here's what your team should be prepared for: * **Domain-Driven Design Concepts**: Understanding business logic separation is a mindset shift that takes time to internalize * **State Management Patterns**: Learning when to use TanStack Query vs. Zustand requires understanding the difference between server and client state * **TypeScript Discipline**: Our approach relies heavily on TypeScript for safety, so team members need to be comfortable with comprehensive typing * **Architectural Thinking**: Success requires embracing layered architecture concepts rather than putting everything in components #### Complexity Considerations |May be overkill| Architecture[UTA Architecture] Complex --> |Well-suited| Architecture Architecture --> Overhead[Setup Overhead] Architecture --> Benefits[Long-term Benefits] style Simple fill:#ffebee style Complex fill:#e8f5e8 style Architecture fill:#e3f2fd style Overhead fill:#fff3e0 style Benefits fill:#f1f8e9 `} /> ### When NOT to Use This Architecture **Key Insight**: No architecture fits every scenario. Here's when alternatives might be better choices. #### Project Types Where Simpler Approaches Excel #### Rapid Prototyping & MVPs * **Timeline**: Projects with 2-4 week development cycles * **Scope**: Single-feature or proof-of-concept applications * **Alternative**: Expo Router with simple folder structure * **Why**: Setup overhead exceeds project duration #### Small-Scale Applications * **Team Size**: 1-2 developers * **Feature Count**: Fewer than 5 major features * **Complexity**: Simple CRUD operations * **Alternative**: Standard React Native CLI with basic organization * **Why**: Architectural benefits don't justify complexity #### Educational Projects * **Purpose**: Learning React Native fundamentals * **Focus**: Understanding core concepts rather than architecture * **Timeline**: Short-term learning exercises * **Alternative**: Tutorial-based structures * **Why**: Architecture concepts can distract from core learning #### Emergency Projects * **Constraints**: Extremely tight deadlines * **Resources**: No time for team training * **Scope**: Single-purpose applications * **Alternative**: Framework defaults with minimal customization * **Why**: Training time conflicts with delivery requirements #### Technical Constraints * **Legacy Integration**: Heavy integration with legacy systems that conflict with modern patterns * **Extreme Performance**: Real-time applications with microsecond requirements (rare in mobile apps) ### Known Tradeoffs & Their Impact #### Development Velocity Tradeoffs Slow Week3 --> Breaking Week5 --> Fast style Week1 fill:#ffebee style Week3 fill:#fff3e0 style Week5 fill:#e8f5e8 style Slow fill:#ffcdd2 style Breaking fill:#fff9c4 style Fast fill:#c8e6c9 `} /> #### Performance Considerations * **Bundle Size**: Additional dependencies increase application size * **Memory Usage**: Multiple state management solutions consume more memory * **Initialization Time**: Complex setup can slow application startup * **Runtime Overhead**: Abstraction layers introduce minimal performance cost #### Team Requirements * **Skill Level**: Requires developers comfortable with TypeScript and modern React patterns * **Onboarding Time**: New team members need 1-2 weeks to become productive * **Maintenance Expertise**: Long-term success requires understanding architectural principles * **Decision Making**: Teams must commit to following established patterns consistently ## Decision Framework ### Evaluation Criteria We've created this framework to help you honestly assess whether our architectural approach makes sense for your specific situation. Rather than advocating for our approach in every case, we want to help you make the right choice for your project: #### Project Characteristics **Strong Fit Indicators** ✅ * Development timeline exceeding 3 months * Expected feature count above 10 major features * Multiple user roles and workflows * Plans for ongoing feature development * Integration with multiple backend services **Weak Fit Indicators** ❌ * One-time prototype or proof of concept * Simple CRUD application * Single-purpose utility app * Fixed scope with no planned expansion * Minimal business logic complexity #### Team Capabilities **Strong Fit Indicators** ✅ * 3+ developers on the team * Experience with TypeScript and modern React * Commitment to learning new patterns * Plans for team growth * Emphasis on code quality and maintainability **Weak Fit Indicators** ❌ * Solo developer or pair programming only * Beginner-level React Native experience * Tight deadlines preventing learning time * High team turnover expected * Preference for minimal abstractions #### Technical Needs **Strong Fit Indicators** ✅ * Complex state management requirements * Multiple API integrations * Need for comprehensive testing * Performance optimization important * Platform-specific customization needed **Weak Fit Indicators** ❌ * Simple local data storage * Single API endpoint integration * Minimal testing requirements * Basic performance needs * Standard platform behaviors sufficient #### Business Context **Strong Fit Indicators** ✅ * Long-term product vision (1+ years) * Multiple stakeholders and requirements * Plans for feature iteration and evolution * Importance of maintainability * Budget for proper development practices **Weak Fit Indicators** ❌ * Short-term tactical solution * Single stakeholder with simple needs * Fixed requirements unlikely to change * Time-to-market prioritized over quality * Limited development budget ### Decision Tree TeamSize{Team Size > 2?} TeamSize -->|No| Simple[Consider Simpler Architecture] TeamSize -->|Yes| Timeline{Timeline > 3 months?} Timeline -->|No| Simple Timeline -->|Yes| Features{Features > 10?} Features -->|No| Evaluate[Evaluate Other Factors] Features -->|Yes| Complex{Complex Business Logic?} Complex -->|No| Evaluate Complex -->|Yes| UTA[UTA Architecture Recommended] Evaluate --> Experience{Team TypeScript Experience?} Experience -->|Low| Training{Training Time Available?} Experience -->|High| Maintenance{Long-term Maintenance?} Training -->|No| Simple Training -->|Yes| UTA Maintenance -->|No| Simple Maintenance -->|Yes| UTA style UTA fill:#c8e6c9 style Simple fill:#ffcdd2 style Evaluate fill:#fff3e0 `} /> ### Questions to Discuss with Your Team **Team Alignment**: These questions can help facilitate productive discussions about whether our approach fits your team's needs and constraints. #### Technical Assessment 1. How comfortable is our team with TypeScript and modern React patterns? 2. Are we planning to integrate with multiple backend services or APIs? 3. Do we expect significant growth in features and complexity over time? 4. How important is comprehensive testing for this project? #### Project Context 1. What are our realistic development timeline and team size constraints? 2. How do we balance long-term maintainability with time-to-market pressures? 3. Do we have stakeholder support for investing in architectural patterns? 4. Are we prepared to invest time in team onboarding and learning? #### Risk Assessment 1. What are the real consequences if our architectural choice doesn't work out? 2. How difficult would it be to migrate to a different approach later? 3. Do we have the expertise to adapt these patterns to our specific needs? 4. How will we maintain architectural consistency as the team grows? ## Migration Considerations ### Adoption Strategies #### New Project Implementation **Recommended Approach**: Full adoption from project start **Timeline**: 1-2 weeks for initial setup and team training **Steps**: 1. Clone template and configure for your project 2. Conduct team training sessions on architectural patterns 3. Implement first feature following established patterns 4. Establish code review processes for pattern compliance 5. Create project-specific documentation additions **Success Factors**: * Dedicated time for learning and setup * Team commitment to following patterns * Clear communication about architectural decisions #### Migrating Existing Codebases **Recommended Approach**: Gradual migration by feature **Timeline**: 2-6 months depending on codebase size **Steps**: 1. Assess current architecture and identify pain points 2. Create migration plan prioritizing high-value features 3. Implement new features using UTA patterns 4. Gradually refactor existing features during maintenance 5. Update team processes and documentation **Challenges**: * Maintaining consistency during transition * Managing technical debt during migration * Team adoption of new patterns while maintaining old code #### Selective Pattern Adoption **Recommended Approach**: Adopt specific architectural patterns without full migration **Use Cases**: * Teams wanting domain organization without full template * Projects requiring only state management improvements * Gradual architectural evolution **Considerations**: * May miss benefits of integrated architectural approach * Requires deeper understanding of individual patterns * Risk of inconsistent implementation ### Team Preparation Requirements #### Technical Skills Development * **TypeScript Proficiency**: Essential for taking advantage of type safety * **Modern React Patterns**: Hooks, context, and state management * **TanStack Query**: Server state management and caching strategies * **Testing Practices**: Component testing and integration testing approaches #### Process Adaptations * **Code Review Guidelines**: Establishing architectural pattern compliance * **Documentation Habits**: Maintaining up-to-date architectural documentation * **Feature Planning**: Incorporating domain and feature analysis into planning * **Onboarding Processes**: Training new team members on architectural patterns ### Success Metrics #### Short-term Indicators (First 3 months) * Team comfort level with architectural patterns * Consistency of pattern implementation across features * Reduction in time spent on code organization decisions * Quality of code reviews and architectural discussions #### Long-term Indicators (6+ months) * Developer velocity for new feature implementation * Reduced time for new team member onboarding * Decreased bug rates related to state management * Improved test coverage and reliability ## Alternative Architectural Approaches **UTA's Role**: UTA provides architectural patterns that can be applied on top of any React Native technical foundation (Expo Router + CNG, React Native CLI, or custom setups). ### When to Consider Different Architectural Strategies Minimal Rapid --> Minimal Custom --> CustomArch Enterprise --> UTA style Simple fill:#e8f5e8 style Enterprise fill:#e3f2fd style UTA fill:#c8e6c9 `} /> #### Simple Project Organization (Framework Defaults) * **Best For**: Prototypes, MVPs, small teams (1-3 developers) * **Benefits**: Minimal setup, fast initial development, easy to understand * **Tradeoffs**: Limited scalability, no architectural guidance for complexity #### Custom Domain-Driven Architecture * **Best For**: Enterprise teams with specific architectural requirements, existing DDD expertise * **Benefits**: Complete control over patterns, tailored to business context * **Tradeoffs**: Requires significant architectural expertise, longer standardization time #### UTA's Position as Architectural Layer **Key Understanding**: UTA architectural patterns sit **above** the technical foundation. Whether you use Expo Router + CNG, React Native CLI, or custom configurations, UTA provides the business logic organization and state management strategies. **When UTA Architectural Patterns Are Most Valuable**: * Teams building complex applications that will scale beyond simple CRUD operations * Projects requiring clear separation between UI logic and business logic * Organizations prioritizing long-term maintainability and team collaboration * Applications with sophisticated state management needs (multiple APIs, complex caching) **When Simpler Architectural Approaches Are Better**: * Rapid prototypes and proof-of-concept projects * Small teams (1-3 developers) building straightforward applications * Projects with extremely tight deadlines where architectural investment isn't justified * Teams with strong existing architectural expertise who prefer custom solutions ## Related Documents This document provides the foundation for architectural decision-making. For implementation details and specific guidance, explore these related resources: *** *This document represents our current understanding of architectural tradeoffs based on real-world experience. As we continue to evolve the architecture based on feedback and new requirements, we'll update this assessment to reflect new insights and learnings.* # UTA Architecture: Core Architecture URL: https://dev.updatetheapp.com/docs/architecture/core-architecture Documentation: https://dev.updatetheapp.com/docs/architecture/core-architecture Reference guide to the core architecture of the React Native app, covering patterns, component interactions, and technology stack. *** title: Core Architecture description: Reference guide to the core architecture of the React Native app, covering patterns, component interactions, and technology stack. ----------------------------------------------------------------------------------------------------------------------------------------------- # Core Architecture ## Overview This document outlines the core architecture for our React Native mobile application. It defines the fundamental patterns, structures, and frameworks that support all features and functionality. Our architecture employs a hybrid approach combining feature-based UI organization with domain-based business logic for optimal scalability and maintainability. ## Purpose & Scope This document provides a comprehensive reference for understanding the overall application architecture. It's intended for all developers working on the project, particularly those who need to understand how different system components interact and the distinction between shared utilities and domain-specific code. ## Core Architecture Components ### 1. Architectural Overview QueryProvider[TanStack Query Provider] App --> StoreProvider[State Store Provider] App --> Navigation[Navigation Container] QueryProvider --> Features[Feature Modules] StoreProvider --> Features Navigation --> Features Features --> Domains[Domain Services] Features --> SharedUtils[Shared Utilities] Features --> UI[Shared UI Components] Domains --> ApiClient[API Client] SharedUtils --> ApiClient Domains --> SharedUtils ApiClient --> Backend[Backend Services] `} /> Our application follows a hybrid architecture that combines: * **Feature-based organization** for UI/UX concerns * **Domain-based organization** for business logic and data * **Clear separation** between shared utilities and domain-specific code ### 2. Technology Stack | Category | Technology | Purpose | | ------------ | ----------------------------------- | -------------------------------------------------------------------- | | Framework | React Native | Cross-platform mobile application development | | Language | TypeScript | Static typing and improved developer productivity | | Server State | TanStack Query | Data fetching, caching, synchronization, and server state management | | Client State | Zustand | Global application state management | | Navigation | React Navigation | Screen routing and navigation management | | HTTP Client | Axios | Promise-based HTTP requests, integrated with TanStack Query | | Styling | React Native StyleSheet / Unistyles | Native styling with performance optimization and theme support | | Testing | Jest & React Testing Library | Unit and integration testing | | E2E Testing | Detox | End-to-end testing for mobile applications | ### 3. Hybrid Architecture: Features + Domains Our architecture employs a dual approach for optimal organization: #### Domain-Based Business Logic * **Purpose**: Organize business logic by domain entities * **Location**: `core/domains/[domain]/` * **Contents**: API services, types, hooks, query keys * **Examples**: Products, Users, Auth, Orders #### Feature-Based UI Organization * **Purpose**: Organize user-facing functionality by features * **Location**: `features/[feature]/` * **Contents**: Screens, components, navigation * **Examples**: Shopping Cart, Product Catalog, User Profile #### Shared Utilities * **Purpose**: Cross-cutting concerns used everywhere * **Location**: `core/shared/` * **Contents**: Generic hooks, utilities, types * **Examples**: useDebounce, formatDate, ApiResponse ### 4. Core Infrastructure The core infrastructure is divided into domain-specific and shared components: ``` core/ ├── domains/ # Domain-based business logic │ ├── products/ # Product domain │ │ ├── api.ts # Product API service │ │ ├── types.ts # Product type definitions │ │ ├── hooks.ts # Product query hooks │ │ └── queryKeys.ts # Product query keys │ ├── users/ # User domain │ ├── auth/ # Authentication domain │ │ ├── api.ts │ │ ├── types.ts │ │ ├── hooks.ts │ │ ├── store.ts # Auth state management │ │ ├── tokenService.ts # Token management │ │ └── events.ts # Auth events │ └── [other-domains]/ │ └── shared/ # Truly shared utilities ├── api/ │ ├── client.ts # Axios instance with interceptors │ └── interceptors.ts # Request/response interceptors ├── query/ │ └── queryClient.ts # TanStack Query client config ├── hooks/ # Generic React hooks ├── utils/ # General utility functions ├── types/ # Cross-domain types └── styles/ # Global styling ``` ### 5. Shared vs Domain Guidelines Clear separation between shared and domain-specific code is crucial: #### What Goes in Shared Folders * **core/shared/hooks/**: Generic React hooks (useDebounce, useLocalStorage) * **core/shared/utils/**: General utilities (formatDate, capitalizeString) * **core/shared/types/**: Cross-domain types (ApiResponse, AsyncState, ErrorResponse) #### What Stays in Domain Folders * **core/domains/\[domain]/hooks.ts**: Domain queries (useProducts, useUser) * **core/domains/\[domain]/types.ts**: Domain models (Product, User) * **core/domains/\[domain]/api.ts**: Domain services ### 6. Feature Module Structure Each feature module is a self-contained unit focused on user-facing functionality: ``` features/ ├── product-catalog/ # Product browsing feature │ ├── components/ # Feature components │ ├── screens/ # Screen components │ ├── navigation.tsx # Feature navigation │ └── state/ # Feature-specific state └── [other-features]/ # Other feature modules ``` ### 7. State Management Architecture ServerState[Server State] UI <--> ClientState[Client State] subgraph TanStack Query ServerState --> Cache[(Cache)] Cache --> Hooks[Domain Query Hooks] end subgraph Zustand ClientState <--> Stores[(State Stores)] Stores <--> StateHooks[State Hooks] end Hooks --> DomainServices[Domain Services] DomainServices --> Backend[Backend] `} /> Our application clearly separates different types of state: * **Server State**: Managed by TanStack Query through domain hooks * **Client State**: Managed by Zustand for global application state * **Component State**: Local state for UI-specific concerns ### 8. Navigation System The navigation system provides type-safe routing: * **Navigation Container**: Central React Navigation setup * **Feature-Based Navigation**: Each feature defines its screens * **Deep Linking**: Support for external app opening * **Type Safety**: Full TypeScript integration ### 9. Communication Patterns |"Domain Service"| DomainService[Domain Service] DomainService --> ComponentB ComponentA -->|"Events"| EventBus[Event Bus] EventBus --> ComponentB ComponentA -->|"Shared State"| SharedState[Shared State] SharedState --> ComponentB ComponentA -->|"Navigation"| Navigation[Navigation] Navigation --> ComponentB `} /> Features communicate through several patterns: * **Domain Services**: Shared data access through domain hooks * **Event Bus**: Publish-subscribe for loose coupling * **Shared State**: Query cache and global state * **Navigation**: Parameter passing between screens ### 10. Authentication Architecture Authentication is handled as a special domain with additional concerns: ``` core/domains/auth/ ├── api.ts # Auth API calls ├── types.ts # Auth types ├── hooks.ts # Auth query hooks ├── store.ts # Auth state management ├── tokenService.ts # Token storage and refresh └── events.ts # Auth events (session expired) ``` Key features: * Automatic token refresh through interceptors * Session management with event-driven updates * Secure token storage abstraction ## Design Principles ### 1. Feature-Based Modularity We organize UI/UX by features rather than technical concerns to create clear user journey mapping and better team ownership. **(Do ✅) Group related UI components by feature** * This approach makes it easier to understand the complete user journey within a feature. * Example: All components related to product browsing are grouped in `features/product-catalog/`. **(Consider 🤔) When a feature becomes too large, consider breaking it into sub-features** * Large features (>15-20 components) can become difficult to maintain. * Use a subdirectory structure to organize related components within large features. ### 2. Domain-Driven Design We organize business logic by domains for better scalability and maintainability. This separation has proven to significantly reduce complexity as applications grow. **(Do ✅) Identify clear domain boundaries based on business entities** * Group related business logic, types, and API calls by domain entity. * Example: Everything related to products is in `core/domains/products/`. **(Don't ❌) Mix domain concerns within a single domain module** * Avoid including user-related functionality in the product domain. * Each domain should have a single, well-defined responsibility. ### 3. Clear Separation of Concerns Our architecture maintains clear boundaries between different responsibilities: **(Do ✅) Use appropriate tools for different state needs** * **Server State vs. Client State**: TanStack Query for server data, Zustand for client-only state * **Shared vs. Domain**: Place truly shared utilities in `core/shared`, domain-specific code in domains * **UI vs. Business Logic**: Features handle UI/UX, domains handle data and business rules ### 4. Type Safety Comprehensive TypeScript throughout the application improves reliability and developer experience: **(Do ✅) Define complete type interfaces for all data structures** * **API Types**: Full request/response typing with proper documentation * **State Types**: Typed stores and hooks for compile-time safety * **Navigation Types**: Type-safe routes and parameters **(Don't ❌) Use `any` types or ignore TypeScript warnings** * Type safety is only effective when consistently applied throughout the codebase. * If you find yourself using `any`, it might indicate a design issue that needs addressing. ### 5. Performance First We prioritize performance considerations in our architecture design: **(Do ✅) Implement performance optimizations from the start** * **Optimized Rendering**: Strategic memoization of components and callbacks * **Query Optimization**: Smart caching strategies for data fetching * **Code Splitting**: Domain-based lazy loading for faster initial load **(Consider 🤔) Adding performance monitoring to identify bottlenecks** * Implement basic performance tracking to identify areas for improvement. * Regular performance audits help maintain a fast application experience. ### 6. Developer Experience Our architecture is designed to enhance developer productivity: **(Do ✅) Maintain consistent patterns across the codebase** * **Consistent Patterns**: Predictable organization makes onboarding easier * **Clear Boundaries**: Obvious where code belongs reduces decision fatigue * **Self-Documenting**: Structure reveals intent and guides development ## Implementation Considerations ### 1. Adding New Domains Domains represent business entities and contain related business logic. When creating a new domain, follow these steps: #### Create domain folder Create a new directory in `core/domains/` with a descriptive name representing the business entity (e.g., `products`, `orders`, `payments`). #### Add required files At minimum, every domain needs these three files: ```typescript // api.ts - API service methods class NewDomainApi { public readonly public = { /* public endpoints */ }; public readonly protected = { /* auth-required endpoints */ }; } export const newDomainApi = new NewDomainApi(); // types.ts - TypeScript interfaces export interface NewDomainEntity { id: string; // other properties } // hooks.ts - React Query hooks export function useNewDomainEntities() { return useQuery({ queryKey: ['newDomain', 'list'], queryFn: () => newDomainApi.public.getAll() }); } ``` #### Add optional files as needed Consider adding these files for more complex domains: * `queryKeys.ts` - For consistent query key management * `events.ts` - For pub/sub communication * `store.ts` - For domain-specific UI state * `utils.ts` - For domain-specific utility functions #### Create barrel exports Add an `index.ts` file that exports the public API of your domain: ```typescript // index.ts export * from './api'; export * from './types'; export * from './hooks'; // export other files as needed ``` **(Do ✅) Follow the established domain structure consistently** * A consistent structure makes the codebase more predictable and easier to navigate. * Each domain should have the same basic file organization. **(Don't ❌) Mix domain logic with feature-specific UI code** * Domains should contain only business logic, data fetching, and types. * Feature-specific UI components should stay in feature folders. ### 2. Creating New Features Features represent user-facing functionality and organize related UI components. To create a new feature: #### Create feature folder Add a new directory in `features/` with a name describing the user-facing functionality (e.g., `product-catalog`, `shopping-cart`, `checkout`). #### Add screens and components Create subdirectories for organization: ``` features/new-feature/ ├── components/ # Feature-specific components ├── screens/ # Screen components ├── navigation.tsx # Feature navigation └── index.ts # Barrel exports ``` #### Connect to domains Import and use domain hooks to access data and business logic: ```typescript // features/product-catalog/screens/ProductListScreen.tsx import { useProducts } from '@/core/domains/products'; export function ProductListScreen() { const { data: products, isLoading } = useProducts(); // Render UI using the products data } ``` #### Add feature-specific state if needed For UI state that's specific to the feature but doesn't belong in a domain: ```typescript // features/product-catalog/state/filters.ts import { create } from 'zustand'; export const useProductFiltersStore = create((set) => ({ viewMode: 'grid', setViewMode: (mode) => set({ viewMode: mode }), })); ``` **(Do ✅) Keep features focused on user-facing functionality** * Features should primarily contain UI components and layout logic. * The API for a feature should be clean and limited to what's necessary for other parts of the app. **(Be Aware ❗) Features can get complex - consider sub-features** * For very large features, consider creating sub-feature folders. * Use the same organizational patterns within sub-features. ### 3. Shared vs Domain Decision Deciding where code belongs is critical for maintaining the architecture. Use this decision tree: Q1{"Is it specific to\none business domain?"} Q1 -->|"Yes"| Domain["Place in domain folder\ncore/domains/[domain-name]/"] Q1 -->|"No"| Q2{"Is it a reusable\nutility or hook?"} Q2 -->|"Yes"| Shared["Place in shared folder\ncore/shared/[appropriate-subfolder]/"] Q2 -->|"No"| Q3{"Is it UI-related?"} Q3 -->|"Yes"| UI["Is it feature-specific?"] Q3 -->|"No"| Q4{"Is it infrastructure?"} UI -->|"Yes"| Feature["Place in feature folder\nfeatures/[feature-name]/"] UI -->|"No"| UILib["Place in UI library\nui/[component-type]/"] Q4 -->|"Yes"| Config["Place in app config\ncore/config/"] Q4 -->|"No"| Reconsider["Reconsider the design"] classDef decision fill:#f9f,stroke:#333; classDef location fill:#bfb,stroke:#333; classDef reconsider fill:#fbb,stroke:#333; class Q1,Q2,Q3,Q4,UI decision; class Domain,Shared,Feature,UILib,Config location; class Reconsider reconsider; `} /> **(Do ✅) Ask critical questions when deciding placement** * "Is this specific to one business domain?" → Domain folder * "Is it a reusable utility used across domains?" → Shared folder * "Is it a UI component specific to one feature?" → Feature folder **(Don't ❌) Create ambiguous or overlapping responsibilities** * Avoid creating utilities that do the same thing in different places. * Don't add business logic to UI components. ### 4. Cross-Feature Communication Features should never directly import from other features. Instead, use these patterns: **(Do ✅) Use domain hooks for shared data access** ```typescript // Both features use the same domain hook // features/product-catalog/screens/ProductList.tsx // features/product-detail/screens/ProductDetail.tsx import { useProduct } from '@/core/domains/products'; ``` **(Do ✅) Use events for loose coupling** ```typescript // features/shopping-cart/screens/CartScreen.tsx import { cartEvents } from '@/core/domains/cart'; // Emit event when item added cartEvents.emit('cart:itemAdded', { productId, quantity }); // features/product-detail/components/AddedNotification.tsx // Listen for cart events cartEvents.on('cart:itemAdded', handleItemAdded); ``` **(Do ✅) Pass parameters through navigation** ```typescript // features/product-catalog/components/ProductCard.tsx import { useNavigation } from '@react-navigation/native'; const navigation = useNavigation(); navigation.navigate('ProductDetail', { productId: product.id }); ``` **(Don't ❌) Import directly between features** ```typescript // AVOID THIS - creates tight coupling import { ProductCard } from '@/features/product-catalog/components/ProductCard'; ``` **(Consider 🤔) Using Signals for real-time updates** * For real-time updates that need to flow across features, consider using a signals library or reactive state. * This provides a performance benefit for frequently changing values. ## Related Documents * [Project Structure Reference](/docs/architecture/project-structure) * [Domain Architecture Guide](/docs/architecture/domain-architecture) * [Shared Utilities Guide](/docs/features/shared-utilities) * [File Naming Conventions](/docs/coding-standards/file-naming-conventions) * [Communication Patterns](/docs/features/communication-patterns) * [Authentication Architecture](/docs/api/authentication) * [State Management](/docs/state-management) * [API Architecture](/docs/api) # UTA Architecture: Project Structure URL: https://dev.updatetheapp.com/docs/architecture/project-structure Documentation: https://dev.updatetheapp.com/docs/architecture/project-structure Comprehensive reference for the project's directory structure, file organization, and module boundaries with clear shared/domain separation. *** title: Project Structure description: Comprehensive reference for the project's directory structure, file organization, and module boundaries with clear shared/domain separation. --------------------------------------------------------------------------------------------------------------------------------------------------------- # Project Structure ## Overview This document provides a comprehensive reference for the project's directory structure, file organization, and module boundaries. It outlines how code is organized following a hybrid architecture that combines feature-based UI organization with domain-based business logic, emphasizing the clear separation between shared utilities and domain-specific code. ## Purpose & Scope This reference is intended for all developers working on the project to understand how code should be organized and where different types of code should be placed. It serves as the definitive guide for project organization with clear rules for shared versus domain-specific code placement. ## Directory Structure ### Root Structure ``` project-root/ ├── src/ # Source code │ ├── core/ # Domain logic & shared utilities │ ├── features/ # Feature modules (UI/UX) │ ├── ui/ # Shared UI components │ ├── navigation/ # Navigation configuration │ ├── App.tsx # Application entry with providers │ └── index.js # Root file ├── assets/ # Static assets (project root level) ├── android/ # Android platform files ├── ios/ # iOS platform files └── package.json # Project configuration ``` #### High-Level Architecture Relationships Core Features --> UI Features --> Nav App --> Features App --> Core App --> UI App --> Nav style Features fill:#f9f,stroke:#333 style Core fill:#bfb,stroke:#333 style UI fill:#bbf,stroke:#333 style Nav fill:#fbb,stroke:#333 `} /> ### Core Directory The `core` directory is divided into two main sections: domain-based business logic and truly shared utilities: domain_internals style domains fill:#f9f,stroke:#333 style shared fill:#bfb,stroke:#333 style domain_internals fill:#ddd,stroke:#333 `} /> ``` core/ ├── domains/ # Domain-based business logic │ ├── products/ # Product domain │ │ ├── api.ts # Product API service │ │ ├── types.ts # Product types (Product, ProductCategory) │ │ ├── hooks.ts # Product hooks (useProducts, useProduct) │ │ └── queryKeys.ts # Product query keys │ │ │ ├── users/ # User domain │ │ ├── api.ts # User API service │ │ ├── types.ts # User types (User, UserProfile) │ │ ├── hooks.ts # User hooks (useUser, useUpdateUser) │ │ └── queryKeys.ts # User query keys │ │ │ ├── auth/ # Authentication domain │ │ ├── api.ts # Auth API service │ │ ├── types.ts # Auth types (AuthState, LoginRequest) │ │ ├── hooks.ts # Auth hooks (useAuth, useLogin) │ │ ├── store.ts # Auth state management │ │ ├── tokenService.ts # Token management service │ │ ├── events.ts # Auth events (sessionExpired) │ │ └── queryKeys.ts # Auth query keys │ │ │ └── [other-domains]/ # Additional domains (orders, payments, etc.) │ └── shared/ # Truly shared, non-domain utilities ├── api/ │ ├── client.ts # Axios instance with interceptors │ └── interceptors.ts # Request/response interceptors │ ├── query/ │ └── queryClient.ts # TanStack Query client config │ ├── hooks/ # Generic React hooks (NO domain logic) │ ├── useDebounce.ts # Debounce hook │ ├── useLocalStorage.ts # Local storage hook │ ├── useMediaQuery.ts # Media query hook │ └── useTimeout.ts # Timeout hook │ ├── utils/ # General utilities (NO domain logic) │ ├── date.ts # Date formatting utilities │ ├── string.ts # String manipulation utilities │ ├── validation.ts # Generic validation helpers │ └── number.ts # Number formatting utilities │ ├── types/ # Cross-domain types │ ├── ui.ts # Cross-domain UI patterns (BaseComponentProps, LoadingState) │ ├── global.ts # Global app types (AppState, Theme) │ └── api.ts # Generic API types (ErrorResponse, Pagination) │ └── styles/ # Global styling ├── theme.ts # Theme constants (colors, spacing) ├── typography.ts # Text styles and fonts ├── layout.ts # Layout utilities ├── shadows.ts # Shadow styles └── mixins.ts # Reusable style patterns ``` For detailed information on domain-based business logic organization and implementation patterns, see the [Domain Architecture Guide](/docs/architecture/domain-architecture). ### Features Directory Features contain UI/UX code organized by user-facing functionality: ``` features/ ├── product-catalog/ # Product browsing feature │ ├── components/ # Feature-specific components │ │ └── ProductCard/ │ │ ├── ProductCard.tsx │ │ ├── styles.ts │ │ └── index.ts │ ├── screens/ # Feature screens │ │ └── ProductListScreen/ │ │ ├── ProductListScreen.tsx │ │ ├── styles.ts │ │ └── index.ts │ ├── navigation.tsx # Feature navigation setup │ └── state/ # Feature-specific UI state │ └── filterStore.ts # UI filters state │ ├── shopping-cart/ # Shopping cart feature │ ├── components/ │ ├── screens/ │ ├── navigation.tsx │ └── state/ │ └── cartUIStore.ts # Cart UI state (drawer open/close) │ └── [other-features]/ # Additional features ``` For more information on implementing features and communication between features and domains, see the [Communication Patterns](/docs/features/communication-patterns) guide. ### UI Directory Shared UI components used across features: ``` ui/ ├── Button/ # Generic button component │ ├── Button.tsx │ ├── styles.ts │ └── index.ts ├── Card/ # Generic card component │ ├── Card.tsx │ ├── styles.ts │ └── index.ts ├── Input/ # Generic input component │ ├── Input.tsx │ ├── styles.ts │ └── index.ts └── [other-components]/ # Other shared UI components ``` For UI component best practices and implementation details, refer to the UI Development guides. ### Navigation Directory Navigation configuration for the entire app: ``` navigation/ ├── RootNavigator.tsx # Main navigation setup ├── AuthNavigator.tsx # Auth flow navigation ├── MainNavigator.tsx # Main app navigation └── types.ts # Navigation type definitions ``` For more information on navigation configuration and routing strategies, see the related sections in the [Core Architecture Reference](/docs/architecture/core-architecture). ### Assets Directory Static assets organization for brand assets, fonts, animations, and other media: ``` assets/ # Project root level ├── brand/ # Brand assets (logos, illustrations) │ ├── logos/ # Company/product logos │ │ ├── company-logo.svg # SVG for scalability │ │ ├── product-logo.svg # SVG for scalability │ │ └── partner-logo.png # PNG with @2x, @3x variants │ ├── illustrations/ # Marketing/onboarding graphics │ │ ├── onboarding/ │ │ └── empty-states/ │ └── backgrounds/ # Hero banners, splash screens ├── fonts/ # Custom font files │ ├── OpenSans-Regular.ttf │ ├── OpenSans-Bold.ttf │ └── OpenSans-Italic.ttf └── animations/ # Lottie and other animations └── loading.json # Loading animation ``` **Important**: UI icons (navigation, actions, status) are managed separately in the UI component system. See the [Icon Management Guide](/docs/ui-development/assets/icon-management) for UI icons and the [Assets Management Guide](/docs/ui-development/assets/assets-management) for brand assets. ### Reference Implementations Directory Working examples and demonstrations that mirror the main project structure: |"mirrors"| ref MainCore -.->|"examples"| RefCore MainFeatures -.->|"patterns"| RefFeatures MainUI -.->|"components"| RefUI style main fill:#f9f,stroke:#333 style ref fill:#bfb,stroke:#333 `} /> ``` reference-implementations/ # Project root level ├── README.md # Overview and usage guide ├── core/ # Domain and shared utilities examples │ ├── domains/ # Business logic implementations │ │ ├── products/ # Product domain examples │ │ │ ├── api.example.ts # Product API service patterns │ │ │ ├── hooks.example.ts # Product hooks implementations │ │ │ ├── types.example.ts # Product type definitions │ │ │ └── README.md # Domain implementation guide │ │ ├── users/ # User domain examples │ │ ├── auth/ # Authentication examples │ │ └── [other-domains]/ # Additional domain examples │ │ │ └── shared/ # Shared utilities examples │ ├── api/ # API client patterns │ │ ├── client.example.ts # Configured API client │ │ ├── interceptors.example.ts # Request/response handling │ │ └── README.md # API integration guide │ ├── hooks/ # Generic hook implementations │ │ ├── useDebounce.example.ts # Debounce hook pattern │ │ ├── useLocalStorage.example.ts # Storage hook pattern │ │ └── README.md # Hook development guide │ ├── utils/ # Utility function examples │ │ ├── validation.example.ts # Validation patterns │ │ ├── formatting.example.ts # Data formatting │ │ └── README.md # Utility development guide │ └── types/ # Cross-domain type examples │ ├── api.example.ts # Generic API types │ ├── ui.example.ts # UI pattern types │ └── README.md # Type architecture guide │ ├── features/ # Feature implementation examples │ ├── product-catalog/ # Product browsing feature example │ │ ├── components/ # Feature-specific components │ │ ├── screens/ # Feature screen implementations │ │ ├── navigation.example.tsx # Feature navigation patterns │ │ ├── state.example.ts # Feature state management │ │ └── README.md # Feature implementation guide │ ├── shopping-cart/ # Shopping cart feature example │ ├── user-authentication/ # Auth flow feature example │ └── [other-features]/ # Additional feature patterns │ ├── ui/ # UI component implementations │ ├── foundation/ # Basic building blocks │ │ ├── Button/ # Button component examples │ │ │ ├── Button.example.tsx # Main implementation │ │ │ ├── Button.variants.tsx # All visual variants │ │ │ ├── Button.playground.tsx # Interactive examples │ │ │ ├── Button.test.tsx # Testing patterns │ │ │ └── README.md # Component guide │ │ ├── Input/ # Input component examples │ │ ├── Card/ # Card component examples │ │ └── [other-foundation]/ # Additional foundation components │ │ │ ├── patterns/ # Composed UI patterns │ │ ├── Modal/ # Modal pattern implementations │ │ │ ├── Modal.example.tsx # Basic modal usage │ │ │ ├── Modal.complex.tsx # Advanced patterns │ │ │ ├── Modal.accessibility.tsx # A11y implementation │ │ │ └── README.md # Pattern guide │ │ ├── Form/ # Form pattern examples │ │ ├── DataTable/ # Data table implementations │ │ └── [other-patterns]/ # Additional pattern components │ │ │ └── business/ # Domain-aware components │ ├── ProductCard/ # Product display examples │ │ ├── ProductCard.example.tsx # Basic implementation │ │ ├── ProductCard.loading.tsx # Loading states │ │ ├── ProductCard.error.tsx # Error handling │ │ └── README.md # Business component guide │ ├── UserAvatar/ # User display examples │ └── [other-business]/ # Additional business components │ └── docs/ # Documentation examples ├── guides/ # Implementation guides ├── patterns/ # Code pattern examples └── best-practices/ # Best practice implementations ``` #### Purpose and Benefits **Documentation → Examples → Implementation Flow**: 1. **Learn**: Read comprehensive documentation 2. **Study**: Examine working reference implementations 3. **Implement**: Build production features with confidence **Key Benefits**: * **Working Code**: All examples are tested and functional * **Mirror Structure**: 1:1 mapping with main project architecture * **Progressive Learning**: From simple examples to complex patterns * **Template Ready**: Copy-paste starting points for new features * **Best Practices**: Demonstrates recommended patterns and approaches **Cross-References**: Each implementation includes: * Links back to relevant documentation sections * GitHub source links for the actual production code * Testing examples and patterns * Performance considerations * Accessibility implementations For detailed information on using reference implementations effectively, see the [Reference Implementation Guide](/docs/reference-implementations). ## Module Boundaries ### Shared vs Domain Rules useDebounce, useLocalStorage] SharedUtils[Generic Utils
formatDate, capitalizeString] SharedTypes[Cross-domain Types
ApiResponse, AsyncState] end subgraph Domains[core/domains - Business Logic] ProductDomain[products/
API, types, hooks] UserDomain[users/
API, types, hooks] AuthDomain[auth/
API, types, hooks, store] end subgraph Features[features/ - UI Layer] ProductCatalog[product-catalog] ShoppingCart[shopping-cart] end Features --> Shared Features --> Domains Domains --> Shared Domains -.->|"Allowed but minimize"| Domains Features -.-|"❌ NEVER"| Features style Domains fill:#f9f,stroke:#333,stroke-width:2px style Shared fill:#9ff,stroke:#333,stroke-width:2px style Features fill:#ff9,stroke:#333,stroke-width:2px `} /> ### Import Rules **Why Import Rules Matter** Clear import boundaries help maintain a clean architecture by preventing circular dependencies, ensuring proper separation of concerns, and making the codebase more maintainable and testable. #### Allowed Import Patterns * **Features** can import from: * Core domains (`core/domains/*`) for business logic * Shared utilities (`core/shared/*`) for common functions * UI components (`ui/*`) for shared interface elements * Navigation (`navigation/*`) for routing functions * **Core domains** can import from: * Shared utilities (`core/shared/*`) only * **UI components** can import from: * Shared utilities (`core/shared/*`) only * **Navigation** can import from: * Shared utilities (`core/shared/*`) only #### Prohibited Import Patterns * **Features** cannot import from: * Other features (to prevent feature coupling) * **Core domains** cannot import from: * Features (to prevent business logic depending on UI) * Other domains (to maintain domain independence) * **Shared utilities** cannot import from: * Features (to maintain utility independence) * Domains (to keep utilities truly shared and reusable) |✓ can import| Domains Features -->|✓ can import| Shared Features -->|✓ can import| UI Features -->|✓ can import| Navigation Domains -->|✓ can import| Shared UI -->|✓ can import| Shared Navigation -->|✓ can import| Shared end subgraph Prohibited[Prohibited Import Patterns] Features2[features/*] Domains2[core/domains/*] Shared2[core/shared/*] Features2 -->|❌ cannot import| Features2 Domains2 -->|❌ cannot import| Features2 Domains2 -->|❌ cannot import| Domains2 Shared2 -->|❌ cannot import| Domains2 Shared2 -->|❌ cannot import| Features2 end style Allowed fill:#f8f8f8,stroke:#333 style Prohibited fill:#fff8f8,stroke:#333 style Features fill:#f9f,stroke:#333 style UI fill:#bbf,stroke:#333 style Navigation fill:#fbb,stroke:#333 style Domains fill:#bfb,stroke:#333 style Shared fill:#dfd,stroke:#333 style Features2 fill:#f9f,stroke:#333 style Domains2 fill:#bfb,stroke:#333 style Shared2 fill:#dfd,stroke:#333 `} /> #### Common Import Violations to Avoid * **Cross-feature imports**: Features should not directly import from each other * **Domain-to-domain imports**: Domains should remain independent of each other * **Shared importing domains**: Shared utilities must not depend on domain-specific code * **Circular dependencies**: No module should directly or indirectly depend on itself ### Decision Tree for Code Placement ``` Is this code specific to one business domain? ├─ YES → Place in core/domains/[domain]/ │ ├─ API calls? → api.ts │ ├─ Data types? → types.ts │ ├─ React hooks? → hooks.ts │ └─ Query keys? → queryKeys.ts │ └─ NO → Is it used across multiple domains? ├─ YES → Place in core/shared/ │ ├─ React hook? → shared/hooks/ │ ├─ Utility function? → shared/utils/ │ └─ Type definition? → shared/types/ │ └─ NO → Is it UI-specific? ├─ YES → Place in features/[feature]/ └─ NO → Reconsider architecture ``` ## Component Structure All components follow this consistent structure: ``` ComponentName/ ├── ComponentName.tsx # Component implementation ├── styles.ts # Component-specific styles ├── types.ts # Component-specific types (optional) ├── __tests__/ # Tests directory │ └── ComponentName.test.tsx # Unit tests └── index.ts # Exports component ``` ## File Naming Conventions This project follows strict file naming conventions to ensure consistency: * **Components**: PascalCase (e.g., `Button.tsx`, `ProductCard.tsx`) * **Hooks**: camelCase with "use" prefix (e.g., `useAuth.ts`, `useDebounce.ts`) * **Domain files**: lowercase descriptive (e.g., `api.ts`, `types.ts`) * **Utilities**: lowercase by category (e.g., `date.ts`, `string.ts`) * **Features**: kebab-case folders (e.g., `product-catalog/`) * **Tests**: `.test.ts` or `.test.tsx` suffix For complete naming guidelines, see the [File Naming Conventions](/docs/coding-standards/file-naming-conventions). ## Do's and Don'ts ### Shared Folders Guidelines * (Do ✅) Place **generic, reusable** hooks like `useDebounce` in `core/shared/hooks/` * (Don't ❌) Put **domain-specific** hooks like `useProducts` in shared hooks * (Do ✅) Keep **general utilities** like `formatDate` in `core/shared/utils/` * (Don't ❌) Put **domain utilities** like `calculateProductDiscount` in shared utils * (Do ✅) Store **cross-domain types** like `ApiResponse` and `AsyncState` in `core/shared/types/` * (Don't ❌) Put **component-specific types** like `ButtonProps` in shared types - these belong co-located with components per the [Feature Implementation Decision Tree](/docs/guides/feature-implementation-decision-tree) * (Don't ❌) Duplicate **domain types** like `Product` in shared types ### Domain Folders Guidelines * (Do ✅) Keep **domain queries** like `useProducts` in `core/domains/products/hooks.ts` * (Do ✅) Store **domain models** like `Product` in `core/domains/products/types.ts` * (Do ✅) Place **domain API calls** in `core/domains/products/api.ts` * (Consider 🤔) Creating `utils.ts` in domain if needed for domain-specific helpers * (Consider 🤔) Adding `queryKeys.ts` only if you have > 3 query keys ### Feature Folders Guidelines * (Do ✅) Import data from **domain hooks** (`core/domains/*/hooks.ts`) * (Do ✅) Use **shared utilities** from `core/shared/*` * (Do ✅) Keep **UI state** local to features * (Don't ❌) Import from **other features** * (Don't ❌) Implement **business logic** in features ### General Guidelines * (Do ✅) Use **index.ts** files for clean exports * (Do ✅) Maintain **consistent naming** conventions * (Do ✅) Write **tests** alongside components * (Don't ❌) Create **circular dependencies** * (Don't ❌) Mix **concerns** (UI with business logic) ## Examples ### Correct Import Patterns ```typescript // In features/product-catalog/screens/ProductList.tsx import { useProducts } from '@/core/domains/products'; // ✅ Domain hook import { formatCurrency } from '@/core/shared/utils'; // ✅ Shared utility import { Button } from '@/ui/foundation/Button'; // ✅ Shared UI component // In core/domains/products/api.ts import { apiClient } from '@/core/shared/api'; // ✅ Shared API client import type { Product } from './types'; // ✅ Same domain type // In core/shared/hooks/useDebounce.ts import { useState, useEffect } from 'react'; // ✅ Only React imports // No domain imports! ✅ ``` ### Incorrect Import Patterns ```typescript // In features/product-catalog/screens/ProductList.tsx import { CartItem } from '@/features/shopping-cart/components'; // ❌ Cross-feature // In core/shared/utils/product.ts import { Product } from '@/core/domains/products'; // ❌ Domain in shared // In core/domains/products/hooks.ts import { useDebounce } from '@/core/shared/hooks'; // ✅ Fixed: Use shared hook ``` ## Implementation Considerations ### Adding New Domains 1. Create folder: `core/domains/[domain-name]/` 2. Add required files: * `api.ts` - API service methods * `types.ts` - TypeScript interfaces * `hooks.ts` - Query/mutation hooks 3. Add optional files as needed: * `queryKeys.ts` - If many query keys * `store.ts` - If client state needed * `events.ts` - If event system needed 4. Update barrel exports ### Adding New Features 1. Create folder: `features/[feature-name]/` 2. Add standard structure: * `screens/` - Feature screens * `components/` - Feature components * `navigation.tsx` - Feature routing 3. Import from domains for data 4. Keep business logic in domains ### Migration Strategy When refactoring existing code: 1. **Identify domain boundaries** * Group related API calls * Extract business types 2. **Move to appropriate locations** * Domain logic → `core/domains/` * Generic utilities → `core/shared/` * UI components → Features or `ui/` 3. **Update imports** * Fix all import paths * Remove circular dependencies 4. **Test thoroughly** * Ensure no regressions * Verify module boundaries ## Related Documents * [](/docs/architecture/core-architecture) * [](/docs/architecture/domain-architecture) * [](/docs/features/shared-utilities) * [](/docs/coding-standards/file-naming-conventions) * [](/docs/features/communication-patterns) * [](/docs/ui-development/assets) # UTA Architecture: Domain Architecture Guide URL: https://dev.updatetheapp.com/docs/architecture/domain-architecture Documentation: https://dev.updatetheapp.com/docs/architecture/domain-architecture Guide to implementing domain-based architecture for business logic organization. *** title: Domain Architecture Guide description: Guide to implementing domain-based architecture for business logic organization. --------------------------------------------------------------------------------------------- import { Tab, Tabs } from 'fumadocs-ui/components/tabs'; # Domain Architecture Guide ## Overview This guide explains our approach to domain-based architecture, which we've implemented in the `core/domains/` directory. In our experience, organizing business logic by entity (such as products, users, authentication) rather than by technical concern creates clearer boundaries and significantly improves scalability as applications grow. Domain-based architecture helps solve several common challenges in React Native development: * **Code organization** as applications scale beyond a handful of features * **Maintainability** by creating logical boundaries between business entities * **Team collaboration** by enabling parallel work with minimal conflicts * **Testing** by isolating business logic from UI concerns By following these patterns, we've found teams can work more efficiently and produce more robust applications. ## Purpose & Scope This document is designed to help developers at all experience levels: * When and why to create separate domains for business logic * How to structure domain folders for consistency and discoverability * Patterns for implementing domain services, hooks, and types * Best practices for maintaining proper boundaries between domains * Techniques for cross-domain communication without tight coupling * **New team members** wanting to understand our architecture * **Feature developers** needing to integrate with domain services * **Domain maintainers** responsible for implementing business logic * **Technical leads** making architectural decisions To get the most from this guide, you should be familiar with: * Basic React and React Native concepts * TypeScript fundamentals (interfaces, types) * [Core Architecture Reference](/docs/architecture/core-architecture) (recommended first) * [API Integration](/docs/api) (helpful for understanding API services) ## Domain Boundaries ### Key Principles 1. **Domain Independence**: Domains should be self-contained and not directly import from each other 2. **Shared Interface**: Domains expose public APIs through hooks and types 3. **Feature Integration**: Features compose multiple domains for UI ProductDomain ShoppingCart --> ProductDomain ShoppingCart --> CartDomain Checkout --> CartDomain Checkout --> UserDomain Checkout --> OrderDomain style Features fill:#f8f8f8,stroke:#333 style Domains fill:#f0f0f0,stroke:#333 style ProductList fill:#bbf,stroke:#333 style ShoppingCart fill:#bbf,stroke:#333 style Checkout fill:#bbf,stroke:#333 style ProductDomain fill:#bfb,stroke:#333 style CartDomain fill:#bfb,stroke:#333 style UserDomain fill:#bfb,stroke:#333 style OrderDomain fill:#bfb,stroke:#333 `} /> ### Cross-Domain Communication OrdersHooks FeatureUI --> ProductsHooks FeatureUI --> UserHooks %% Domain-to-domain communication OrdersHooks --> ProductsAPI OrdersHooks --> UserAPI %% Event-based communication AuthEvents --> Orders AuthEvents --> Products AuthEvents --> User style FeatureUI fill:#bbf,stroke:#333 style OrdersHooks fill:#f9f,stroke:#333 style ProductsHooks fill:#f9f,stroke:#333 style UserHooks fill:#f9f,stroke:#333 style OrdersAPI fill:#bfb,stroke:#333 style ProductsAPI fill:#bfb,stroke:#333 style UserAPI fill:#bfb,stroke:#333 style AuthEvents fill:#fbb,stroke:#333 style AuthAPI fill:#bfb,stroke:#333 `} /> #### Communication Patterns 1. **Direct Hook Usage**: Features import and use hooks from multiple domains simultaneously 2. **Cross-Domain API Calls**: Domain services can access other domain services through their public APIs 3. **Event-Based Communication**: Domains can broadcast events that other domains listen for 4. **Shared State Access**: Domains can share data through centralized state stores ## What is a Domain? A domain represents a distinct business entity or area of functionality: * **Products**: Everything related to product data and operations * **Users**: User management and profile functionality * **Auth**: Authentication and authorization logic * **Orders**: Order processing and management * **Payments**: Payment processing logic ## Domain Structure ### Standard Domain Anatomy Every domain follows this structure: ``` core/domains/[domain-name]/ ├── api.ts # API service methods (required) ├── types.ts # TypeScript interfaces (required) ├── hooks.ts # React Query hooks (required) ├── queryKeys.ts # Query key factory (optional) ├── store.ts # Client state if needed (optional) ├── events.ts # Event definitions (optional) ├── utils.ts # Domain utilities (optional) └── index.ts # Barrel exports (required) ``` API Index --> Types Index --> Hooks Index --> QueryKeys Index --> Store Index --> Events Index --> Utils Hooks --> API Hooks --> QueryKeys Hooks --> Types API --> Types classDef required fill:#f9f,stroke:#333 classDef optional fill:#bfb,stroke:#333 class API,Types,Hooks,Index required class QueryKeys,Store,Events,Utils optional `} /> ### Required Files All domain files follow our [File Naming Conventions](/docs/coding-standards/file-naming-conventions). Each file has a specific purpose: #### 1. `api.ts` - API Service Methods Contains all API calls for the domain, following the **mandatory** public/protected pattern: ```typescript // core/domains/products/api.ts import { publicApi, authenticatedApi } from '@/core/shared/api/client'; import type { Product, CreateProductDto, UpdateProductDto } from './types'; class ProductApi { // Public endpoints (no auth required) public readonly public = { getAll: async (): Promise => { const { data } = await publicApi.get('/products'); return data; }, getById: async (id: string): Promise => { const { data } = await publicApi.get(`/products/${id}`); return data; }, search: async (query: string): Promise => { const { data } = await publicApi.get('/products/search', { params: { q: query } }); return data; }, }; // Protected endpoints (auth required) public readonly protected = { create: async (product: CreateProductDto): Promise => { const { data } = await authenticatedApi.post('/products', product); return data; }, update: async (id: string, updates: UpdateProductDto): Promise => { const { data } = await authenticatedApi.put(`/products/${id}`, updates); return data; }, delete: async (id: string): Promise => { await authenticatedApi.delete(`/products/${id}`); }, getUserProducts: async (userId: string): Promise => { const { data } = await authenticatedApi.get(`/users/${userId}/products`); return data; }, }; } export const productApi = new ProductApi(); ``` **This pattern is required for all domain APIs**. Benefits: * Clear separation of public vs authenticated endpoints * No runtime flags or configuration needed * Type-safe API calls * Better developer experience with IntelliSense PublicClient Protected --> AuthClient PublicClient --> Backend AuthClient --> Backend Backend[Backend API] style Public fill:#bfb,stroke:#333 style Protected fill:#f9f,stroke:#333 `} /> #### 2. `types.ts` - TypeScript Interfaces Defines all types for the domain: ```typescript // core/domains/products/types.ts export interface Product { id: string; name: string; description: string; price: number; category: string; inStock: boolean; createdAt: Date; updatedAt: Date; } export interface CreateProductDto { name: string; description: string; price: number; category: string; inStock?: boolean; } export interface UpdateProductDto { name?: string; description?: string; price?: number; category?: string; inStock?: boolean; } export interface ProductFilters { category?: string; minPrice?: number; maxPrice?: number; inStock?: boolean; } ``` #### 3. `hooks.ts` - React Query Hooks Implements data fetching hooks using TanStack Query, following the public/protected pattern: ```typescript // core/domains/products/hooks.ts import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'; import { productApi } from './api'; import { productQueryKeys } from './queryKeys'; import type { Product, CreateProductDto, UpdateProductDto, ProductFilters } from './types'; // Public query hooks (no auth required) export function useProducts(filters?: ProductFilters) { return useQuery({ queryKey: productQueryKeys.list(filters), queryFn: () => productApi.public.getAll(filters), }); } export function useProduct(id: string) { return useQuery({ queryKey: productQueryKeys.detail(id), queryFn: () => productApi.public.getById(id), enabled: !!id, }); } export function useProductSearch(query: string) { return useQuery({ queryKey: productQueryKeys.search(query), queryFn: () => productApi.public.search(query), enabled: query.length > 2, }); } // Protected mutation hooks (auth required) export function useCreateProduct() { const queryClient = useQueryClient(); return useMutation({ mutationFn: productApi.protected.create, onSuccess: () => { queryClient.invalidateQueries({ queryKey: productQueryKeys.lists() }); }, }); } export function useUpdateProduct() { const queryClient = useQueryClient(); return useMutation({ mutationFn: ({ id, updates }: { id: string; updates: UpdateProductDto }) => productApi.protected.update(id, updates), onSuccess: (data, { id }) => { queryClient.setQueryData(productQueryKeys.detail(id), data); queryClient.invalidateQueries({ queryKey: productQueryKeys.lists() }); }, }); } export function useDeleteProduct() { const queryClient = useQueryClient(); return useMutation({ mutationFn: productApi.protected.delete, onSuccess: (_, id) => { queryClient.removeQueries({ queryKey: productQueryKeys.detail(id) }); queryClient.invalidateQueries({ queryKey: productQueryKeys.lists() }); }, }); } // Protected query hooks (auth required) export function useUserProducts(userId: string) { return useQuery({ queryKey: productQueryKeys.userProducts(userId), queryFn: () => productApi.protected.getUserProducts(userId), enabled: !!userId, }); } ``` #### Domain Data Flow Pattern Hooks Hooks --> API API --> Backend Backend --> API API --> Hooks Hooks --> UI style UI fill:#bbf,stroke:#333 style Hooks fill:#f9f,stroke:#333 style API fill:#bfb,stroke:#333 style Backend fill:#fbb,stroke:#333 `} /> #### Data Flow Steps 1. UI Component calls domain hook (e.g., `useProducts()`) 2. Domain hook calls API service 3. API service makes HTTP request to backend 4. Backend responds with data 5. API service returns typed data to hook 6. Hook returns cached data to UI ### Optional Files #### 4. `queryKeys.ts` - Query Key Factory Use when you have > 3 query keys: ```typescript // core/domains/products/queryKeys.ts export const productQueryKeys = { all: ['products'] as const, lists: () => [...productQueryKeys.all, 'list'] as const, list: (filters?: ProductFilters) => [...productQueryKeys.lists(), { filters }] as const, details: () => [...productQueryKeys.all, 'detail'] as const, detail: (id: string) => [...productQueryKeys.details(), id] as const, byCategory: (category: string) => [...productQueryKeys.all, 'category', category] as const, }; ``` #### 5. `store.ts` - Client State For domain-specific UI state: ```typescript // core/domains/products/store.ts import { create } from 'zustand'; interface ProductsUIState { selectedCategory: string | null; viewMode: 'grid' | 'list'; sortBy: 'name' | 'price' | 'date'; setSelectedCategory: (category: string | null) => void; setViewMode: (mode: 'grid' | 'list') => void; setSortBy: (sort: 'name' | 'price' | 'date') => void; } export const useProductsUIStore = create((set) => ({ selectedCategory: null, viewMode: 'grid', sortBy: 'name', setSelectedCategory: (category) => set({ selectedCategory: category }), setViewMode: (mode) => set({ viewMode: mode }), setSortBy: (sort) => set({ sortBy: sort }), })); ``` #### 6. `events.ts` - Event Definitions For pub/sub communication: ```typescript // core/domains/products/events.ts import { EventEmitter } from '@/core/shared/utils/events'; export type ProductEvents = { 'product:created': { productId: string }; 'product:updated': { productId: string; changes: Record }; 'product:deleted': { productId: string }; 'product:stockLow': { productId: string; quantity: number }; }; export const productEvents = new EventEmitter(); // Usage example productEvents.on('product:stockLow', ({ productId, quantity }) => { console.log(`Product ${productId} has low stock: ${quantity}`); }); ``` #### 7. `index.ts` - Barrel Exports Clean public API for the domain: ```typescript // core/domains/products/index.ts export * from './api'; export * from './types'; export * from './hooks'; export * from './queryKeys'; export { productEvents } from './events'; export { useProductsUIStore } from './store'; ``` ## Real-World Examples ### Authentication Domain The auth domain has special requirements: ```typescript // core/domains/auth/tokenService.ts import { storage } from '@/core/shared/utils/storage'; export const tokenService = { getAccessToken: () => storage.get('accessToken'), getRefreshToken: () => storage.get('refreshToken'), setTokens: (access: string, refresh: string) => { storage.set('accessToken', access); storage.set('refreshToken', refresh); }, clearTokens: () => { storage.remove('accessToken'); storage.remove('refreshToken'); }, }; ``` ```typescript // core/domains/auth/events.ts import { EventEmitter } from '@/core/shared/utils/events'; export type AuthEvents = { 'auth:login': { userId: string }; 'auth:logout': void; 'auth:sessionExpired': void; 'auth:tokenRefreshed': { accessToken: string }; }; export const authEvents = new EventEmitter(); ``` ### User Domain ```typescript // core/domains/users/hooks.ts export function useCurrentUser() { return useQuery({ queryKey: userQueryKeys.current(), queryFn: userApi.getCurrentUser, }); } export function useUpdateProfile() { const queryClient = useQueryClient(); return useMutation({ mutationFn: (updates: UpdateProfileDto) => userApi.updateProfile(updates), onSuccess: (data) => { queryClient.setQueryData(userQueryKeys.current(), data); }, }); } ``` ## Communication Between Domains ### Preferred: Through Shared Cache ```typescript // In products domain export function useProductWithUser(productId: string) { const { data: product } = useProduct(productId); const { data: user } = useUser(product?.ownerId); return { product, owner: user }; } ``` ### Alternative: Through Events ```typescript // In orders domain import { productEvents } from '@/core/domains/products'; export function useCreateOrder() { return useMutation({ mutationFn: orderApi.create, onSuccess: (order) => { // Notify product domain about stock changes order.items.forEach(item => { productEvents.emit('product:stockLow', { productId: item.productId, quantity: item.remainingStock, }); }); }, }); } ``` ## Domain Creation Checklist When creating a new domain: ### 1. Identify the Domain * [ ] Clear business entity (e.g., products, users) * [ ] Has distinct API endpoints * [ ] Contains related types and operations ### 2. Create the Structure * [ ] Create folder: `core/domains/[domain-name]/` * [ ] Add required files: `api.ts`, `types.ts`, `hooks.ts` * [ ] Add optional files as needed * [ ] Create barrel export: `index.ts` ### 3. Implement Core Functionality * [ ] Define types in `types.ts` * [ ] Implement API methods in `api.ts` * [ ] Create query/mutation hooks in `hooks.ts` * [ ] Add query keys if > 3 queries ### 4. Handle Special Cases * [ ] Add client state if needed (`store.ts`) * [ ] Implement events if needed (`events.ts`) * [ ] Add domain utilities if needed (`utils.ts`) ### 5. Update Documentation * [ ] Add domain to architecture docs * [ ] Document special behaviors * [ ] Update team guidelines ## Best Practices **Why These Practices Matter** These patterns have evolved through our experience building and maintaining large-scale React Native applications. They help ensure consistency, maintainability, and scalability as your application grows and as team members come and go. ### 1. API Service Patterns **(Do ✅) Use the public/protected pattern for all domain APIs** This pattern creates a clear separation between endpoints that require authentication and those that don't, which helps prevent security mistakes and improves developer experience: ```typescript class ProductApi { public readonly public = { // Endpoints that don't require authentication getAll: async (): Promise => { const { data } = await publicApi.get('/products'); return data; }, }; public readonly protected = { // Endpoints that require authentication create: async (product: CreateProductDto): Promise => { const { data } = await authenticatedApi.post('/products', product); return data; }, }; } ``` The benefits of this approach include: * Clear visual distinction between authenticated and unauthenticated endpoints * Compile-time safety through TypeScript's property access checking * Better IntelliSense support with grouped methods * No runtime checks or flags needed for auth requirements **(Don't ❌) Use API clients directly or rely on configuration flags** Avoid approaches that mix authentication concerns or rely on flags: ```typescript // DON'T do this export const productApi = { // Unclear which endpoints need authentication getAll: () => apiClient.get('/products', { skipAuth: true }), create: (data) => apiClient.post('/products', data), }; ``` This approach creates several problems: * Authentication requirements are not obvious from the code structure * Easy to forget the `skipAuth` flag and cause runtime errors * No compile-time safety for authentication requirements * Harder to maintain consistency as the codebase grows ### 2. Domain Boundaries and Focus **(Do ✅) Keep domains focused on a single business entity** A well-designed domain should represent a single business concept with clear boundaries: ```typescript // core/domains/products/api.ts class ProductApi { public readonly public = { getAll: async (): Promise => { const { data } = await publicApi.get('/products'); return data; }, getById: async (id: string): Promise => { const { data } = await publicApi.get(`/products/${id}`); return data; }, }; public readonly protected = { create: async (product: CreateProductDto): Promise => { const { data } = await authenticatedApi.post('/products', product); return data; }, update: async (id: string, updates: UpdateProductDto): Promise => { const { data } = await authenticatedApi.put(`/products/${id}`, updates); return data; }, delete: async (id: string): Promise => { await authenticatedApi.delete(`/products/${id}`); }, }; } ``` By keeping domains focused, you gain several benefits: * **Easier maintenance**: Each domain is smaller and more manageable * **Better code organization**: Logical grouping of related functionality * **Clearer ownership**: Teams can own specific domains * **More targeted testing**: Domain-specific test suites **(Don't ❌) Mix multiple business entities in a single domain** Mixing responsibilities creates confusion and maintenance challenges: ```typescript // DON'T do this class ProductAndUserApi { public readonly public = { // Product methods getProducts: async (): Promise => { const { data } = await publicApi.get('/products'); return data; }, getProductById: async (id: string): Promise => { const { data } = await publicApi.get(`/products/${id}`); return data; }, // User methods - should be in users domain getUsers: async (): Promise => { const { data } = await publicApi.get('/users'); return data; }, getUserById: async (id: string): Promise => { const { data } = await publicApi.get(`/users/${id}`); return data; }, }; }; ``` This approach creates several problems: * **Unclear domain boundaries**: Where does one entity end and another begin? * **Harder to maintain**: Changes to one entity might affect the other * **Difficult testing**: Tests for different entities are mixed together * **Poor code organization**: Code for different business concepts is intermingled * **Challenges for team ownership**: Hard to assign ownership to different teams ### 3. Use Consistent Naming Consistent naming patterns significantly improve developer experience by making code more predictable and easier to navigate. They also reduce the cognitive load when switching between different parts of the codebase. **(Do ✅) Follow established naming conventions for all domain artifacts** These conventions make code more predictable and discoverable: | Type | Pattern | Examples | | -------------- | ----------------------- | ----------------------------------------------------- | | API instances | `[domain]Api` | `productApi`, `userApi`, `orderApi` | | Query hooks | `use[Resource]` | `useProducts`, `useUser`, `useOrder` | | Mutation hooks | `use[Action][Resource]` | `useCreateProduct`, `useUpdateUser`, `useDeleteOrder` | | State stores | `use[Domain]Store` | `useProductStore`, `useCartStore` | | Events | `[domain]Events` | `productEvents`, `authEvents` | | Query keys | `[domain]QueryKeys` | `productQueryKeys`, `userQueryKeys` | **(Consider 🤔) Creating a domain naming cheat sheet for your team** As your application grows, consider documenting naming patterns in a quick-reference format for new team members. **(Don't ❌) Mix naming conventions or create inconsistent patterns** Inconsistent naming creates confusion and slows down development: ```typescript // DON'T do this - inconsistent naming // Different naming styles for similar concepts export const productApiService = new ProductApiService(); // Too verbose export const userApiClient = new UserApiClient(); // Different suffix export const orderService = new OrderService(); // Missing "Api" // Inconsistent hook naming export function useGetProducts() { /* ... */ } // "Get" is redundant export function fetchUsers() { /* ... */ } // Missing "use" prefix export function orderQueryHook() { /* ... */ } // Unclear purpose ``` Consistent naming provides several benefits: * **Predictability**: Developers can guess file and export names correctly * **Clarity**: The purpose of each artifact is immediately clear * **Maintainability**: Easier to implement tooling and linting rules * **Onboarding**: New team members learn patterns more quickly ### 4. Separate Public and Protected Hooks The authentication requirements of your hooks should match the authentication requirements of the underlying API methods they use. This creates a consistent security model throughout your application. **(Do ✅) Match hook authentication requirements to API authentication requirements** When creating hooks, ensure their authentication requirements align with the API methods they call: ```typescript // Public hooks (no auth required) call public API methods export function useProducts() { return useQuery({ queryKey: productQueryKeys.list(), queryFn: () => productApi.public.getAll(), }); } // Protected hooks (auth required) call protected API methods export function useCreateProduct() { const queryClient = useQueryClient(); return useMutation({ mutationFn: productApi.protected.create, onSuccess: () => { queryClient.invalidateQueries({ queryKey: productQueryKeys.lists() }); }, }); } ``` This alignment provides several benefits: * **Security consistency**: Authentication requirements are clear at every level * **Predictable behavior**: Hooks fail in expected ways when auth is missing * **Better developer experience**: API structure guides hook organization **(Don't ❌) Mix authentication requirements or add unnecessary authentication** These approaches create confusion and security risks: ```typescript // DON'T do this // Public data shouldn't require authentication export function usePublicProducts() { // Wrong: Using protected API for public data return useQuery({ queryKey: ['products'], queryFn: () => protectedApiClient.get('/public-products'), }); } // Don't add conditional authentication export function useProducts(requireAuth = false) { // Wrong: Dynamic authentication creates unpredictable behavior const client = requireAuth ? protectedApiClient : publicApiClient; return useQuery({ queryKey: ['products', { auth: requireAuth }], queryFn: () => client.get('/products'), }); } ``` **(Consider 🤔) Adding explicit documentation about authentication requirements** For larger teams, consider adding JSDoc comments that explicitly state authentication requirements: ```typescript /** * Hook to fetch products list - no authentication required */ export function useProducts() { /* ... */ } /** * Hook to create a new product - requires authentication * @throws {UnauthorizedError} When user is not authenticated */ export function useCreateProduct() { /* ... */ } ``` ### 5. Handle Errors Gracefully Error handling is a critical aspect of domain hooks. Well-designed error handling improves the user experience and makes debugging easier. **(Do ✅) Implement consistent error handling in all query hooks** ```typescript export function useProduct(id: string) { return useQuery({ queryKey: productQueryKeys.detail(id), queryFn: async () => { try { return await productApi.public.getById(id); } catch (error) { // Transform API errors to more user-friendly format if (error.status === 404) { throw new ProductNotFoundError(id); } throw error; // Re-throw other errors } }, enabled: !!id, retry: (failureCount, error) => { // Only retry for network errors, not for 404s if (error instanceof ProductNotFoundError) return false; return failureCount < 3; }, }); } ``` ### 6. Optimize Query Patterns ```typescript // Use query options effectively export function useProducts(options?: UseQueryOptions) { return useQuery({ queryKey: productQueryKeys.all(), queryFn: productApi.public.getAll, staleTime: 5 * 60 * 1000, // 5 minutes ...options, }); } ``` ## Anti-Patterns to Avoid ### 1. Direct State Mutation ❌ **Wrong**: Mutating cache directly ```typescript const queryClient = useQueryClient(); const products = queryClient.getQueryData(['products']); products.push(newProduct); // Don't do this! ``` ✅ **Correct**: Use proper mutations ```typescript const createProduct = useCreateProduct(); createProduct.mutate(newProduct); ``` ### 2. Circular Dependencies ❌ **Wrong**: Domains importing each other ```typescript // core/domains/products/api.ts import { userApi } from '@/core/domains/users/api'; // Avoid! ``` ✅ **Correct**: Use shared hooks or events ```typescript // In feature component const { data: product } = useProduct(id); const { data: user } = useUser(product?.ownerId); ``` ### 3. Business Logic in Features ❌ **Wrong**: Calculate in features ```typescript // features/product-catalog/components/ProductCard.tsx const discountPrice = product.price * 0.9; // Business logic! ``` ✅ **Correct**: Domain handles logic ```typescript // core/domains/products/utils.ts export function calculateDiscount(product: Product): number { return product.price * 0.9; } ``` ## Related Documents * [Core Architecture Reference](/docs/architecture/core-architecture) * [Project Structure Reference](/docs/architecture/project-structure) * [Shared Utilities Guide](/docs/features/shared-utilities) * [File Naming Conventions](/docs/coding-standards/file-naming-conventions) * [Communication Patterns](/docs/features/communication-patterns) * [API Integration Guide](/docs/api) * [State Management Guide](/docs/guides/state-management) # UTA Architecture: Architecture Decision Record Template URL: https://dev.updatetheapp.com/docs/architecture/adr-template Documentation: https://dev.updatetheapp.com/docs/architecture/adr-template Template for documenting architectural decisions with emphasis on explaining rationale and context *** title: Architecture Decision Record Template description: Template for documenting architectural decisions with emphasis on explaining rationale and context --------------------------------------------------------------------------------------------------------------- import { Callout } from 'fumadocs-ui/components/callout'; import { Tabs, Tab } from 'fumadocs-ui/components/tabs'; # Architecture Decision Record (ADR) Template ## Overview Architecture Decision Records (ADRs) help our team document important architectural choices, their context, and most importantly, the reasoning behind them. This creates a valuable knowledge base that helps current and future team members understand not just *what* we decided, but *why* we made those choices. **Why ADRs Matter**: ADRs prevent "archaeology" - the need to dig through old code and commits to understand why things are the way they are. They capture the context and constraints that influenced our decisions, which often gets lost over time. ## ADR Structure ### ADR-\[NUMBER]: \[Descriptive Title] **Date**: \[YYYY-MM-DD]\ **Status**: \[Proposed | Accepted | Deprecated | Superseded]\ **Deciders**: \[List of people involved in the decision]\ **Technical Story**: \[Ticket/Issue reference if applicable] ### Context and Problem Statement \[Describe the situation that necessitates this decision. What challenge are we facing? What opportunity are we addressing? Write this in 2-3 clear sentences that someone new to the project could understand.] ### Why is this decision necessary? \[Explain the importance and urgency of making this decision. What happens if we don't address this? What pain points does it solve? This section helps readers understand the motivation.] ### Decision Drivers Key factors influencing our decision: * \[Driver 1 - e.g., Performance requirements] * \[Driver 2 - e.g., Team expertise] * \[Driver 3 - e.g., Time constraints] * \[Additional drivers as needed] ### Considered Options We evaluated the following approaches: 1. **\[Option 1 Name]** - \[Brief description] 2. **\[Option 2 Name]** - \[Brief description] 3. **\[Option 3 Name]** - \[Brief description] ### Decision Outcome **Chosen option**: "\[Selected Option]" We selected this option because \[provide clear justification linking back to the decision drivers]. #### Why this option? \[Provide detailed explanation of why this option best addresses our needs. Include: * How it satisfies our key requirements * What trade-offs we're accepting * Why other options were less suitable] ### Consequences #### Positive Outcomes * \[Benefit 1 - e.g., Improved developer experience] * \[Benefit 2 - e.g., Better performance] * \[Additional benefits] #### Trade-offs and Considerations * \[Trade-off 1 - e.g., Increased complexity in certain areas] * \[Trade-off 2 - e.g., Additional learning curve] * \[Other considerations] ### Implementation Details #### File Structure Impact ``` # Show how this decision affects project organization src/ ├── core/ │ └── [new/modified structure] └── features/ └── [affected areas] ``` #### Code Examples ```typescript // Current implementation approach [Show existing pattern or structure] ``` ```typescript // New implementation following this decision [Show new pattern with helpful comments] ``` ### Validation How we'll measure success: * \[Metric 1 - e.g., 50% reduction in related bugs] * \[Metric 2 - e.g., New developer onboarding time under 2 days] * \[Metric 3 - e.g., Decreased "where does this go?" questions] ### Related Resources * \[Link to related ADRs] * \[Link to implementation documentation] * \[Link to example implementations] ### ADR-001: Three-Layer UI Architecture **Date**: 2024-01-15\ **Status**: Accepted\ **Deciders**: Tech Lead, Senior UI Engineers, Frontend Team\ **Technical Story**: JIRA-1234 ### Context and Problem Statement Our UI components have grown organically without clear categorization guidelines. Developers spend significant time deciding where to place new components, and we're seeing duplication because existing components are hard to discover. We need a clear, scalable approach to organizing our UI layer. ### Why is this decision necessary? Without clear component organization: * Developers waste 15-30 minutes per component deciding placement * We've found 23 duplicate button variations across features * Business logic is leaking into what should be presentational components * Component reusability is suffering, increasing bundle size If we don't address this now, these issues will compound as the team grows. ### Decision Drivers Key factors influencing our decision: * **Clarity**: Need unambiguous rules for component placement * **Reusability**: Must maximize component sharing across features * **Separation of Concerns**: Keep business logic separate from presentation * **Team Scalability**: System must work for both junior and senior developers * **Migration Path**: Solution must allow incremental adoption ### Considered Options We evaluated four architectural approaches: 1. **Three-layer architecture** (foundation/patterns/business) * Clear separation between atomic, composed, and domain-aware components 2. **Two-layer architecture** (components/containers) * Simple split between presentational and container components 3. **Feature-first organization** (all UI in features) * Co-locate all UI with its feature code 4. **Atomic design** (atoms/molecules/organisms/templates/pages) * Industry-standard five-level hierarchy ### Decision Outcome **Chosen option**: "Three-layer architecture" We selected this approach because it provides the optimal balance of clarity and flexibility while addressing all our key requirements. #### Why this option? The three-layer architecture best serves our needs because: * **Foundation layer** (atoms) enforces zero business logic in basic components, ensuring maximum reusability * **Patterns layer** (molecules) allows sophisticated UI composition without domain coupling * **Business layer** (organisms) provides a clear home for domain-aware components shared across features This approach is superior to alternatives because: * Clearer than atomic design's 5 levels (which often confuse developers) * More flexible than the rigid container/component split * Better for reusability than feature-first organization * Maps naturally to how our team already thinks about components ### Consequences #### Positive Outcomes * Clear, memorable rules reduce decision time to under 2 minutes * Improved component discovery through logical organization * Better code reuse (estimated 40% reduction in duplicate components) * Easier onboarding - new developers understand the system in hours, not days * Natural enforcement of separation of concerns #### Trade-offs and Considerations * Initial migration effort (estimated 2 sprints for full adoption) * Some components may sit at layer boundaries (we'll document edge cases) * Team training required (planned lunch-and-learn sessions) * Need to update build tools for new structure ### Implementation Details #### File Structure Impact ``` src/ui/ ├── foundation/ # Atomic components (no business logic) │ ├── Button/ │ ├── Input/ │ ├── Card/ │ └── Icon/ ├── patterns/ # Composed UI patterns (domain-agnostic) │ ├── Modal/ │ ├── Form/ │ ├── DataTable/ │ └── SearchBox/ └── business/ # Domain-aware components (3+ features) ├── ProductCard/ ├── UserAvatar/ └── OrderStatus/ ``` #### Code Examples ```typescript // ui/foundation/Button/Button.tsx import { theme } from '@/core/shared/styles/theme'; interface ButtonProps { variant?: 'primary' | 'secondary'; size?: 'small' | 'medium' | 'large'; onPress: () => void; children: React.ReactNode; } // Pure presentational component - no business logic export function Button({ variant = 'primary', size = 'medium', onPress, children }: ButtonProps) { return ( {children} ); } ``` ```typescript // ui/patterns/Modal/Modal.tsx import { Button } from '@/ui/foundation/Button'; import { Card } from '@/ui/foundation/Card'; interface ModalProps { visible: boolean; onClose: () => void; title: string; children: React.ReactNode; } // Composed from foundation components, handles UI behavior only export function Modal({ visible, onClose, title, children }: ModalProps) { if (!visible) return null; return ( {title} {children} ); } ``` ```typescript // ui/business/ProductCard/ProductCard.tsx import { Card } from '@/ui/foundation/Card'; import { Button } from '@/ui/foundation/Button'; import { PriceTag } from '@/ui/patterns/PriceTag'; import { useProduct } from '@/core/domains/products/hooks'; import { useCart } from '@/core/domains/cart/hooks'; interface ProductCardProps { productId: string; } // Domain-aware component that uses business logic export function ProductCard({ productId }: ProductCardProps) { const { data: product, isLoading } = useProduct(productId); const { addToCart } = useCart(); if (isLoading) return ; if (!product) return null; return ( {product.name} ); } ``` ### Validation We'll measure success through: * **Placement Decision Time**: Target \< 2 minutes (from current 15-30 min) * **Component Duplication**: 40% reduction within 3 months * **Developer Satisfaction**: Survey scores > 4/5 on component organization * **Onboarding Efficiency**: New developers productive within 1 week * **Code Review Mentions**: 80% reduction in component placement discussions ### Related Resources * [UI Architecture Guide](/docs/architecture/ui-architecture) - Detailed implementation guide * [Component Development Guide](/docs/guides/component-development) - Best practices * [Migration Guide](/docs/guides/ui-migration) - Step-by-step migration plan ### Writing Effective ADRs **Remember**: ADRs are written for future readers, including yourself in 6 months. Be kind to your future self! #### Key Principles 1. **Be Concise but Complete** * Include enough context for understanding * Don't write a novel - aim for 2-3 pages max * Focus on the "why" more than the "how" 2. **Consider Your Audience** * Write for developers who might join the team later * Avoid assuming deep knowledge of current context * Define project-specific terms 3. **Make it Discoverable** * Use descriptive titles * Include relevant keywords * Link from related documentation #### Common Pitfalls to Avoid * **Too Technical**: Balance technical accuracy with readability * **Missing Context**: Always explain the problem before the solution * **No Alternatives**: Show that you considered multiple options * **Vague Consequences**: Be specific about trade-offs #### When to Write an ADR Create an ADR when: * Choosing between architectural patterns * Adopting new technologies or libraries * Making changes that affect multiple teams/features * Decisions that are expensive to reverse * Team debates indicate lack of clarity **Pro Tip**: If you find yourself explaining the same decision multiple times, it probably needs an ADR! ## Related Documents * [Project Structure](/docs/architecture/project-structure) - Understanding our codebase organization * [Domain Architecture](/docs/architecture/domain-architecture) - How we structure business logic # UTA State Management: State Management Architecture URL: https://dev.updatetheapp.com/docs/state-management/state-management-architecture Documentation: https://dev.updatetheapp.com/docs/state-management/state-management-architecture Overview of our state management approach and the golden rule for separating server and client state *** title: State Management Architecture description: Overview of our state management approach and the golden rule for separating server and client state ----------------------------------------------------------------------------------------------------------------- import { File, Folder, Files } from 'fumadocs-ui/components/files'; # State Management Architecture ## Overview This document outlines our application's state management architecture, built around the **Golden Rule** - our fundamental principle for state separation. This architecture optimizes for performance, maintainability, and developer experience by ensuring each type of state (server, client, and component) is handled by the tool best suited for its characteristics. By following a clear separation of concerns, we reduce complexity and improve both runtime performance and developer productivity. ## Purpose & Scope * **Target Audience**: React Native developers working on this application who need to understand when and how to use different state management tools * **Problems Addressed**: State management complexity, prop drilling, unnecessary re-renders, and data synchronization challenges * **Scope Boundaries**: Covers the three primary state management patterns and their integration, but does not cover persistence strategies or offline capabilities in depth ## The Golden Rule > **Server state belongs in TanStack Query, Client state belongs in Zustand, Component state belongs in React** This foundational principle guides all state management decisions in our application: Remote Data] ClientState[Client State
UI State] ComponentState[Component State
Local State] end subgraph "Management Tools" TanStack[TanStack Query] Zustand[Zustand] React[React useState/useReducer] end ServerState --> TanStack ClientState --> Zustand ComponentState --> React style ServerState fill:#bbf,stroke:#333,stroke-width:2px style ClientState fill:#ff9,stroke:#333,stroke-width:2px style ComponentState fill:#f9f,stroke:#333,stroke-width:2px style TanStack fill:#bbf,stroke:#333,stroke-width:2px style Zustand fill:#ff9,stroke:#333,stroke-width:2px style React fill:#f9f,stroke:#333,stroke-width:2px `} /> ### When to Apply the Golden Rule * **(Do ✅)** Use TanStack Query when the data comes from an API or remote source * **(Do ✅)** Use Zustand when state needs to be shared across components or persisted * **(Do ✅)** Use React state when the state is only relevant to a single component ## Core Components/Architecture ### Component Types | Component | Responsibility | Implementation | | --------------- | ---------------------------- | ------------------------- | | Server State | Remote data from APIs | TanStack Query | | Client State | Application-wide UI state | Zustand | | Component State | Local component interactions | React useState/useReducer | ### When to Apply Each Component * **(Do ✅)** Use TanStack Query when the data comes from an API * **(Do ✅)** Use Zustand when state needs to be shared across components or persisted * **(Do ✅)** Use React state when the state is only relevant to a single component ### Communication Patterns Query[TanStack Query Hooks] Component --> Store[Zustand Store] Component --> Local[React useState] Query --> QueryClient[Query Client] QueryClient --> API[API Layer] API --> Backend[Backend Server] Store --> PersistLayer[Optional Persist Layer] PersistLayer --> Storage[AsyncStorage] style Component fill:#f5f5f5,stroke:#333,stroke-width:2px style Query fill:#bbf,stroke:#333,stroke-width:2px style Store fill:#ff9,stroke:#333,stroke-width:2px style Local fill:#f9f,stroke:#333,stroke-width:2px `} /> ## Design Principles ### Core Architectural Principles * **(Do ✅)** Maintain a clear separation of concerns between server and client state * Server state and client state have fundamentally different characteristics and should be treated differently * Mixing concerns leads to unnecessary complexity and performance issues * **(Do ✅)** Use specialized tools optimized for each state type * TanStack Query handles caching, invalidation, and refetching * Zustand provides a simple API for global state with minimal boilerplate * React state is perfect for component-level concerns * **(Do ✅)** Keep components pure and focused on rendering * Components should primarily transform props into UI * Delegate state management logic to hooks and stores ### Trade-offs and Design Decisions | Decision | Benefits | Trade-offs | Why We Chose It | | ------------------------------- | ------------------------------------------------- | --------------------------- | --------------------------- | | Separate server & client state | Better performance, clearer code | Learning multiple tools | Benefits outweigh costs | | TanStack Query for server state | Automatic caching, refetching, optimistic updates | More code than simple fetch | Drastically improves UX | | Zustand for global state | Simple API, minimal boilerplate | Another dependency | Less complex than Redux | | React state for component state | Built-in, familiar | No persistence | Perfect for ephemeral state | ## Implementation Considerations ### Performance Implications * **Query Caching**: Server state is automatically cached by TanStack Query, reducing unnecessary network requests * **Zustand Selectors**: Use selectors to prevent unnecessary re-renders by only subscribing to relevant slices of state * **Component Memoization**: Leverage React.memo and useMemo to prevent wasteful re-renders ### Scaling Considerations * **Query Client Configuration**: Set up appropriate staleTime and cacheTime based on data volatility * **Store Slicing**: Break down Zustand stores by domain as the application grows * **Persistence Strategy**: Configure storage persistence only for critical state to avoid bloat ## Related Documents * [Server State Management](/docs/state-management/server-state) - Detailed guide on TanStack Query implementation * [Client State Management](/docs/state-management/client-state) - Zustand patterns and best practices * [Component State](/docs/state-management/component-state) - React state management techniques * [State Integration Patterns](/docs/state-management/integration-patterns) - How different state types work together # UTA State Management: Server State Management URL: https://dev.updatetheapp.com/docs/state-management/server-state Documentation: https://dev.updatetheapp.com/docs/state-management/server-state Comprehensive guide for managing server state with TanStack Query in our React Native application *** title: Server State Management description: Comprehensive guide for managing server state with TanStack Query in our React Native application -------------------------------------------------------------------------------------------------------------- import { File, Folder, Files } from 'fumadocs-ui/components/files'; import { Tab, Tabs } from 'fumadocs-ui/components/tabs'; import { Accordion, Accordions } from 'fumadocs-ui/components/accordion'; # Server State Management ## Overview This document outlines our approach to managing server state - data that originates from external APIs and requires caching, synchronization, and complex loading states. Our architecture implements TanStack Query (React Query) as the dedicated solution for all server state management, following our application's **Golden Rule**: "Server state belongs in TanStack Query, not in Zustand or Redux". This approach optimizes for performance, developer experience, and data consistency across the application. ## Purpose & Scope * **Target Audience**: React Native developers implementing features that require API data fetching, caching, or state synchronization * **Problems Addressed**: API data management, caching strategies, loading/error states, and data synchronization challenges * **Scope Boundaries**: Covers TanStack Query implementation, query patterns, mutation strategies, and caching approaches but does not cover API service implementation details or authentication token management ## Core Components/Architecture QueryHook[Query Hook] Component --> MutationHook[Mutation Hook] QueryHook --> QueryClient[Query Client] MutationHook --> QueryClient QueryClient --> Cache[(Query Cache)] QueryClient --> API[API Layer] API --> Backend[Backend Server] subgraph "Server State Layer" QueryHook MutationHook QueryClient Cache end style QueryHook fill:#bbf,stroke:#333,stroke-width:2px style MutationHook fill:#bbf,stroke:#333,stroke-width:2px style QueryClient fill:#bbf,stroke:#333,stroke-width:2px style Cache fill:#bbf,stroke:#333,stroke-width:2px `} /> ### Component Types | Component | Responsibility | Implementation | | -------------- | ------------------------------------- | ----------------------------- | | Query Hooks | Data fetching and caching | Feature-specific custom hooks | | Mutation Hooks | Data updating and optimistic updates | Feature-specific custom hooks | | Query Client | Central management and configuration | Globally configured instance | | Query Cache | In-memory data storage | Managed by Query Client | | Query Keys | Cache identification and organization | Structured array patterns | ### When to Use Server State TanStack Query should be used for any data that meets these criteria: * **(Do ✅)** Use for data fetched from external APIs or backend services * **(Do ✅)** Use for data that requires caching and deduplication * **(Do ✅)** Use for data with complex loading or error states * **(Do ✅)** Use for data that needs synchronization across components * **(Do ✅)** Use for real-time data that requires polling or refetching ## Design Principles ### Core Architectural Principles * **(Do ✅)** Keep server state separate from client state * Server state should be managed by TanStack Query * Client state should be managed by Zustand * Mixing these concerns leads to unnecessary complexity * **(Do ✅)** Create focused, reusable query hooks * Each query hook should serve a specific data need * Hooks should be feature-specific and located in the appropriate feature directory * Abstract complex query logic into custom hooks * **(Don't ❌)** Store server data in Zustand or React context * Avoid creating duplicate copies of server state * Let TanStack Query handle caching and synchronization ### Data Access Patterns | Pattern | Used For | Implementation | | ------------------ | ---------------------------------- | ------------------------------------------ | | Basic Queries | Simple data fetching | `useQuery` with query keys | | Dependent Queries | Data that depends on other queries | `useQuery` with `enabled` option | | Paginated Queries | Lists with pagination | `useQuery` with pagination params | | Infinite Queries | Endless scrolling lists | `useInfiniteQuery` with `getNextPageParam` | | Mutations | Creating, updating, deleting data | `useMutation` with callbacks | | Optimistic Updates | Immediate UI feedback | `useMutation` with `onMutate` | ### Query Key Strategies Entity["Entity Type"] Entity --> Collection["Collection/Detail"] Collection --> Params["Parameters"] style Root fill:#bbf,stroke:#333,stroke-width:2px style Entity fill:#bbf,stroke:#333,stroke-width:2px style Collection fill:#bbf,stroke:#333,stroke-width:2px style Params fill:#bbf,stroke:#333,stroke-width:2px `} /> Query keys should follow a consistent structure: ```typescript // Pattern for detail queries ['entity', 'detail', entityId, ...params] // Pattern for list queries ['entity', 'list', { filters }] ``` ### Trade-offs and Design Decisions **Centralized vs. Distributed Query Hooks** | Approach | Benefits | Drawbacks | Best For | | --------------------- | ------------------------------------------- | ----------------------------------------- | -------------------- | | Centralized API hooks | Consistency, single source of truth | Potential for large files, tight coupling | Small to medium apps | | Feature-based hooks | Better code organization, feature isolation | Potential duplication | Medium to large apps | **Our Decision**: We use a feature-based organization for query hooks while sharing common functionality through a factory pattern, giving us both organization and reusability. ## Implementation Considerations ### Performance Implications * **(Do ✅)** Configure appropriate staleTime based on data volatility * More static data (e.g., product details): Longer staleTime (5-10 minutes) * More dynamic data (e.g., cart): Shorter staleTime (0-30 seconds) * **(Do ✅)** Use the `select` option to transform and minimize data * Transform and filter data on selection to minimize re-renders * Extract only the needed fields from larger response objects * **(Don't ❌)** Fetch the same data in multiple components * Create reusable query hooks to share data between components * Let TanStack Query handle the caching and deduplication ### Security Considerations * **(Do ✅)** Validate all data from server responses * Use TypeScript interfaces to enforce data shape * Consider runtime validation with libraries like Zod or Yup * **(Do ✅)** Properly handle authentication errors * Set up global error handling for 401 responses * Redirect to login page or refresh tokens as needed * **(Don't ❌)** Store sensitive data in the query cache without encryption * Be cautious when persisting the query cache to storage * Consider what data should be excluded from persistence ### Scalability Aspects * **(Do ✅)** Implement structured query keys for efficient invalidation * Create a query key factory for consistent key structure * Use parent keys to invalidate related queries * **(Consider 🤔)** Setting up query cache persistence * Improves offline experience and reduces initial loading * Balance with cache size management and potential staleness * **(Be Aware ❗)** Of memory usage with large datasets * Implement pagination or infinite queries for large lists * Consider data normalization for frequently used entities ## Implementation Examples ### Setting Up TanStack Query ```typescript // core/query/queryClient.ts import { QueryClient } from '@tanstack/react-query'; export const queryClient = new QueryClient({ defaultOptions: { queries: { staleTime: 5 * 60 * 1000, // 5 minutes cacheTime: 10 * 60 * 1000, // 10 minutes retry: 3, retryDelay: (attemptIndex) => Math.min(1000 * 2 ** attemptIndex, 30000), refetchOnWindowFocus: false, refetchOnMount: true, }, mutations: { retry: 1, }, }, }); // App.tsx import { QueryClientProvider } from '@tanstack/react-query'; import { queryClient } from '@/core/query'; export function App() { return ( {/* Your app components */} ); } ``` ### Query Key Factory ```typescript // core/query/queryKeys.ts export const queryKeys = { all: ['queries'] as const, users: { all: ['users'] as const, lists: () => [...queryKeys.users.all, 'list'] as const, list: (filters: string) => [...queryKeys.users.lists(), { filters }] as const, details: () => [...queryKeys.users.all, 'detail'] as const, detail: (id: string) => [...queryKeys.users.details(), id] as const, }, products: { all: ['products'] as const, lists: () => [...queryKeys.products.all, 'list'] as const, list: (filters: any) => [...queryKeys.products.lists(), { filters }] as const, details: () => [...queryKeys.products.all, 'detail'] as const, detail: (id: string) => [...queryKeys.products.details(), id] as const, }, cart: { all: ['cart'] as const, current: () => [...queryKeys.cart.all, 'current'] as const, }, }; ``` ### Query Patterns ```typescript // features/products/api/productHooks.ts import { useQuery } from '@tanstack/react-query'; import { productService } from '@/core/api/services'; import { queryKeys } from '@/core/query'; export const useProductsQuery = (filters = {}) => { return useQuery({ queryKey: queryKeys.products.list(filters), queryFn: () => productService.getProducts(filters), staleTime: 5 * 60 * 1000, // 5 minutes }); }; export const useProductQuery = (productId: string) => { return useQuery({ queryKey: queryKeys.products.detail(productId), queryFn: () => productService.getProduct(productId), enabled: !!productId, }); }; ``` ```typescript // features/users/api/userHooks.ts interface UserResponse { id: string; first_name: string; last_name: string; email_address: string; } interface User { id: string; name: string; email: string; } export const useUserQuery = (userId: string) => { return useQuery({ queryKey: queryKeys.users.detail(userId), queryFn: () => userService.getUser(userId), select: (data: UserResponse): User => ({ id: data.id, name: `${data.first_name} ${data.last_name}`, email: data.email_address, }), }); }; ``` ```typescript // features/orders/api/orderHooks.ts export const useOrderWithDetailsQuery = (orderId: string) => { const orderQuery = useQuery({ queryKey: queryKeys.orders.detail(orderId), queryFn: () => orderService.getOrder(orderId), }); const orderItemsQuery = useQuery({ queryKey: queryKeys.orders.items(orderId), queryFn: () => orderService.getOrderItems(orderId), enabled: !!orderQuery.data, }); return { order: orderQuery.data, items: orderItemsQuery.data, isLoading: orderQuery.isLoading || orderItemsQuery.isLoading, error: orderQuery.error || orderItemsQuery.error, }; }; ``` ### Mutation Patterns ```typescript // features/products/api/productMutations.ts import { useMutation, useQueryClient } from '@tanstack/react-query'; import { productService } from '@/core/api/services'; import { queryKeys } from '@/core/query'; export const useCreateProductMutation = () => { const queryClient = useQueryClient(); return useMutation({ mutationFn: productService.createProduct, onSuccess: (newProduct) => { // Invalidate and refetch products list queryClient.invalidateQueries({ queryKey: queryKeys.products.lists() }); // Add the new product to the cache queryClient.setQueryData( queryKeys.products.detail(newProduct.id), newProduct ); }, }); }; ``` ```typescript // features/cart/api/cartMutations.ts export const useUpdateCartItemMutation = () => { const queryClient = useQueryClient(); return useMutation({ mutationFn: cartService.updateCartItem, onMutate: async ({ itemId, quantity }) => { // Cancel any outgoing refetches await queryClient.cancelQueries({ queryKey: queryKeys.cart.current() }); // Snapshot the previous value const previousCart = queryClient.getQueryData(queryKeys.cart.current()); // Optimistically update queryClient.setQueryData(queryKeys.cart.current(), (old: Cart) => ({ ...old, items: old.items.map(item => item.id === itemId ? { ...item, quantity } : item ), })); // Return a context with the previous value return { previousCart }; }, onError: (err, variables, context) => { // If the mutation fails, use the context to roll back if (context?.previousCart) { queryClient.setQueryData(queryKeys.cart.current(), context.previousCart); } }, onSettled: () => { // Always refetch after error or success queryClient.invalidateQueries({ queryKey: queryKeys.cart.current() }); }, }); }; ``` ```typescript // features/checkout/api/checkoutMutations.ts export const useCheckoutMutation = () => { const queryClient = useQueryClient(); const createOrderMutation = useMutation({ mutationFn: orderService.createOrder, }); const processPaymentMutation = useMutation({ mutationFn: paymentService.processPayment, }); return useMutation({ mutationFn: async (checkoutData: CheckoutData) => { // Create order first const order = await createOrderMutation.mutateAsync({ items: checkoutData.items, shippingAddress: checkoutData.shippingAddress, }); // Then process payment const payment = await processPaymentMutation.mutateAsync({ orderId: order.id, paymentMethod: checkoutData.paymentMethod, amount: order.total, }); return { order, payment }; }, onSuccess: () => { // Clear cart and invalidate related queries queryClient.invalidateQueries({ queryKey: queryKeys.cart.all }); queryClient.invalidateQueries({ queryKey: queryKeys.orders.all }); }, }); }; ``` ### Advanced Patterns ```typescript // features/feed/api/feedHooks.ts import { useInfiniteQuery } from '@tanstack/react-query'; export const useFeedQuery = () => { return useInfiniteQuery({ queryKey: queryKeys.feed.all, queryFn: ({ pageParam = 1 }) => feedService.getFeedItems({ page: pageParam }), getNextPageParam: (lastPage, pages) => { return lastPage.hasMore ? lastPage.nextPage : undefined; }, initialPageParam: 1, }); }; // Component usage const FeedList = () => { const { data, fetchNextPage, hasNextPage, isFetchingNextPage, } = useFeedQuery(); const items = data?.pages.flatMap(page => page.items) ?? []; return ( } onEndReached={() => { if (hasNextPage && !isFetchingNextPage) { fetchNextPage(); } }} ListFooterComponent={() => isFetchingNextPage ? : null } /> ); }; ``` ```typescript // features/notifications/api/notificationHooks.ts export const useNotificationsQuery = () => { return useQuery({ queryKey: queryKeys.notifications.unread, queryFn: notificationService.getUnreadNotifications, refetchInterval: 30 * 1000, // Poll every 30 seconds refetchIntervalInBackground: true, }); }; // App state change refetching import { useEffect } from 'react'; import { AppState, AppStateStatus } from 'react-native'; import { useQueryClient } from '@tanstack/react-query'; export const useAppStateRefetch = () => { const queryClient = useQueryClient(); useEffect(() => { const handleAppStateChange = (nextAppState: AppStateStatus) => { if (nextAppState === 'active') { // Refetch all queries when app comes to foreground queryClient.invalidateQueries(); } }; const subscription = AppState.addEventListener('change', handleAppStateChange); return () => subscription.remove(); }, [queryClient]); }; ``` ```typescript // features/products/screens/ProductListScreen.tsx export const ProductListScreen = () => { const queryClient = useQueryClient(); const { data: products } = useProductsQuery(); const handleProductPress = async (productId: string) => { // Prefetch product details before navigation await queryClient.prefetchQuery({ queryKey: queryKeys.products.detail(productId), queryFn: () => productService.getProduct(productId), }); navigation.navigate('ProductDetail', { productId }); }; return ( ( handleProductPress(item.id)} /> )} /> ); }; ``` ### Error Handling Strategies ```typescript // core/query/queryClient.ts export const queryClient = new QueryClient({ defaultOptions: { queries: { staleTime: 5 * 60 * 1000, retry: 3, retryDelay: (attemptIndex) => Math.min(1000 * 2 ** attemptIndex, 30000), onError: (error) => { // Log the error console.error('Query error:', error); // You could trigger a global notification here }, }, mutations: { retry: 1, onError: (error, variables, context) => { // Log mutation errors console.error('Mutation error:', error, variables); // Show a toast message Toast.show({ type: 'error', text1: 'Operation failed', text2: getErrorMessage(error), }); }, }, }, }); ``` ````typescript // core/hooks/useErrorHandler.ts import { useCallback } from 'react'; import { Toast } from 'react-native-toast-message'; import { isAxiosError } from 'axios'; export const useErrorHandler = () => { const handleError = useCallback((error: unknown, fallbackMessage = 'An error occurred') => { let message = fallbackMessage; if (isAxiosError(error)) { if (error.response) { // Server responded with an error status const serverMessage = error.response.data?.message; message = serverMessage || `Error ${error.response.status}: ${error.response.statusText}`; // Handle specific status codes if (error.response.status === 401) { // Trigger auth refresh or logout workflow // ... return; } } else if (error.request) { // Request was made but no response message = 'No response from server. Please check your connection.'; } } else if (error instanceof Error) { message = error.message; } ## Caching Strategies ```typescript // core/query/queryClient.ts export const queryClient = new QueryClient({ defaultOptions: { queries: { // How long until the data is considered stale staleTime: 5 * 60 * 1000, // 5 minutes // How long to keep data in cache while unused cacheTime: 10 * 60 * 1000, // 10 minutes // Whether to refetch when window regains focus refetchOnWindowFocus: true, // Whether to refetch when component remounts refetchOnMount: true, // Whether to refetch when reconnecting refetchOnReconnect: true, }, }, }); ```` Default settings establish baseline behavior for all queries. Adjust these settings based on app needs: * **Short-lived data**: Low staleTime (30s) * **Relatively stable data**: Medium staleTime (5-10min) * **Rarely changing data**: Higher staleTime (30min-1hr) ```typescript // features/products/api/productHooks.ts // Frequently changing data - short staleTime export const useProductInventoryQuery = (productId: string) => { return useQuery({ queryKey: queryKeys.products.inventory(productId), queryFn: () => productService.getInventory(productId), staleTime: 30 * 1000, // 30 seconds refetchInterval: 60 * 1000, // Poll every minute }); }; // Stable reference data - long staleTime export const useProductCategoriesQuery = () => { return useQuery({ queryKey: queryKeys.products.categories(), queryFn: productService.getCategories, staleTime: 24 * 60 * 60 * 1000, // 24 hours cacheTime: 7 * 24 * 60 * 60 * 1000, // 7 days }); }; // Critical user data - always refetch on mount export const useUserProfileQuery = (userId: string) => { return useQuery({ queryKey: queryKeys.users.detail(userId), queryFn: () => userService.getUser(userId), staleTime: 0, // Always considered stale refetchOnMount: 'always', }); }; ``` ## Advanced Usage Patterns ```typescript // features/products/api/productMutations.ts export const useUpdateProductMutation = () => { const queryClient = useQueryClient(); return useMutation({ mutationFn: productService.updateProduct, onSuccess: (updatedProduct) => { // Update specific product in cache queryClient.setQueryData( queryKeys.products.detail(updatedProduct.id), updatedProduct ); // Update product in any lists that might contain it queryClient.setQueriesData( { queryKey: queryKeys.products.lists() }, (old: { products: Product[] }) => { if (!old) return old; return { ...old, products: old.products.map(product => product.id === updatedProduct.id ? updatedProduct : product ), }; } ); }, }); }; ``` ```typescript // features/cart/api/cartMutations.ts export const useRemoveCartItemMutation = () => { const queryClient = useQueryClient(); return useMutation({ mutationFn: cartService.removeItem, onMutate: async (itemId) => { // Cancel any outgoing refetches await queryClient.cancelQueries({ queryKey: queryKeys.cart.current(), }); // Snapshot the current cart const previousCart = queryClient.getQueryData(queryKeys.cart.current()); // Optimistically update the cart queryClient.setQueryData(queryKeys.cart.current(), (old) => ({ ...old, items: old.items.filter(item => item.id !== itemId), totalItems: old.totalItems - 1, totalPrice: old.totalPrice - (old.items.find(i => i.id === itemId)?.price || 0), })); return { previousCart }; }, onError: (_, __, context) => { // Rollback on error if (context?.previousCart) { queryClient.setQueryData(queryKeys.cart.current(), context.previousCart); } }, onSettled: () => { // Refetch to ensure data consistency queryClient.invalidateQueries({ queryKey: queryKeys.cart.current() }); }, }); }; ``` ```typescript // features/navigation/navigationHelpers.ts import { queryClient } from '@/core/query'; import { queryKeys } from '@/core/query'; import { productService } from '@/core/api/services'; export const navigateToProductDetail = async (navigation, productId) => { // Prefetch product details before navigation await queryClient.prefetchQuery({ queryKey: queryKeys.products.detail(productId), queryFn: () => productService.getProduct(productId), }); // Prefetch related data in parallel await Promise.all([ queryClient.prefetchQuery({ queryKey: queryKeys.products.reviews(productId), queryFn: () => productService.getProductReviews(productId), }), queryClient.prefetchQuery({ queryKey: queryKeys.products.related(productId), queryFn: () => productService.getRelatedProducts(productId), }), ]); // Navigate after data is in cache navigation.navigate('ProductDetail', { productId }); }; ``` ## Testing Server State ```typescript // features/products/api/__tests__/productHooks.test.ts import { renderHook, waitFor } from '@testing-library/react-native'; import { useProductsQuery } from '../productHooks'; import { productService } from '@/core/api/services'; import { createWrapper } from '@/test/utils'; jest.mock('@/core/api/services'); describe('useProductsQuery', () => { it('should fetch products successfully', async () => { const mockProducts = [{ id: '1', name: 'Test Product' }]; (productService.getProducts as jest.Mock).mockResolvedValue(mockProducts); const { result } = renderHook(() => useProductsQuery(), { wrapper: createWrapper(), }); expect(result.current.isLoading).toBe(true); await waitFor(() => { expect(result.current.isSuccess).toBe(true); }); expect(result.current.data).toEqual(mockProducts); }); it('should handle errors', async () => { const error = new Error('Failed to fetch'); (productService.getProducts as jest.Mock).mockRejectedValue(error); const { result } = renderHook(() => useProductsQuery(), { wrapper: createWrapper(), }); await waitFor(() => { expect(result.current.isError).toBe(true); }); expect(result.current.error).toEqual(error); }); }); ``` ```typescript // test/utils/createWrapper.tsx import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; import { PropsWithChildren } from 'react'; export function createWrapper() { const queryClient = new QueryClient({ defaultOptions: { queries: { retry: false, cacheTime: 0, }, }, logger: { log: console.log, warn: console.warn, error: () => {}, // Silent errors in tests }, }); return ({ children }: PropsWithChildren<{}>) => ( {children} ); } ``` ```typescript // features/products/screens/__tests__/ProductListScreen.test.tsx import { render, screen, fireEvent, waitFor } from '@testing-library/react-native'; import { ProductListScreen } from '../ProductListScreen'; import { useProductsQuery } from '../../api/productHooks'; import { createWrapper } from '@/test/utils'; jest.mock('../../api/productHooks'); describe('ProductListScreen', () => { it('should render products and handle item press', async () => { const mockProducts = [ { id: '1', name: 'Product 1', price: '$10' }, { id: '2', name: 'Product 2', price: '$20' }, ]; (useProductsQuery as jest.Mock).mockReturnValue({ data: mockProducts, isLoading: false, isError: false, }); const mockNavigate = jest.fn(); jest.mock('@react-navigation/native', () => ({ useNavigation: () => ({ navigate: mockNavigate, }), })); const { getByText } = render(, { wrapper: createWrapper(), }); expect(getByText('Product 1')).toBeTruthy(); expect(getByText('Product 2')).toBeTruthy(); fireEvent.press(getByText('Product 1')); await waitFor(() => { expect(mockNavigate).toHaveBeenCalledWith('ProductDetail', { productId: '1' }); }); }); }); ``` ## Troubleshooting 1. **Stale Data Persisting**: If stale data persists longer than expected, check your `staleTime` configuration. You might need to adjust it for your specific use case. 2. **Multiple Network Requests**: If you're seeing duplicate requests, check the query key construction. Ensure keys are consistent and deterministic across renders. 3. **Memory Leaks**: When working with infinite queries or large data sets, monitor memory usage. Consider implementing pagination or windowing for large lists. 4. **Background Refetching Issues**: If background refetching isn't working as expected, verify `refetchOnWindowFocus` and `refetchOnMount` settings. The React Query Devtools are essential for debugging queries in development: ```typescript import { QueryClientProvider } from '@tanstack/react-query'; import { ReactQueryDevtools } from '@tanstack/react-query-devtools/build/web'; function App() { return ( {/* Your application */} {__DEV__ && } ); } ``` For React Native, consider using a custom logging middleware or Flipper plugin for TanStack Query. If you encounter performance issues: 1. **Enable Selective Updates**: Use the `select` option to transform and filter data, reducing component re-renders. 2. **Use Query Cancellation**: Cancel pending queries when they're no longer needed. 3. **Optimize Query Keys**: Structure keys appropriately to maximize cache hits. 4. **Analyze QueryClient Configuration**: Review `cacheTime`, `staleTime`, and garbage collection settings. ## Related Documents Overview of our complete state management approach including client, server, and component state. Companion documentation for handling client-specific state with Zustand. Patterns for integrating server and client state effectively. # UTA State Management: Client State Management URL: https://dev.updatetheapp.com/docs/state-management/client-state Documentation: https://dev.updatetheapp.com/docs/state-management/client-state Comprehensive guide for managing client state with Zustand in our React Native application *** title: Client State Management description: Comprehensive guide for managing client state with Zustand in our React Native application ------------------------------------------------------------------------------------------------------- import { File, Folder, Files } from 'fumadocs-ui/components/files'; import { Tab, Tabs } from 'fumadocs-ui/components/tabs'; import { Accordion, Accordions } from 'fumadocs-ui/components/accordion'; import { Cards, Card } from 'fumadocs-ui/components/card'; # Client State Management ## Overview This document outlines our approach to managing client-side state - data that exists exclusively within the application and doesn't need to be synchronized with external servers. Our architecture implements Zustand as the dedicated solution for all client state management, following our application's **Golden Rule**: "Client state belongs in Zustand, not in TanStack Query or Redux". This approach optimizes for simplicity, developer experience, and performance across the application. ## Purpose & Scope * **Target Audience**: React Native developers implementing features that require global state management, UI state coordination, or persistent local settings * **Problems Addressed**: Simplifying state management, reducing boilerplate, improving TypeScript integration, and providing performance optimizations * **Scope Boundaries**: Covers Zustand implementation, store patterns, performance optimization, and testing but does not cover server state management or data fetching strategies ## Core Components/Architecture Store[Zustand Store] Component --Call Actions--> Store subgraph "Client State Layer" Store --Update--> State[(Application State)] State --Notify--> Store end subgraph "Middleware" Store <--> Persist[Persistence] Store <--> Immer[Immer] Store <--> DevTools[DevTools] end State <-.Optional Persistence.-> LocalStorage[(AsyncStorage)] style Component fill:#bbf,stroke:#333,stroke-width:2px style Store fill:#bbf,stroke:#333,stroke-width:2px style State fill:#bbf,stroke:#333,stroke-width:2px `} /> ### Component Types | Component | Responsibility | Implementation | | ------------- | ---------------------------------------------- | ------------------------------------ | | Zustand Store | State container and update logic | Feature-specific store files | | Store Actions | Methods that modify state | Defined within store creation | | Selectors | Extract and subscribe to specific state slices | Used in components via hooks | | Middleware | Add functionality to stores | Persistence, immutability, debugging | | Store Hooks | Encapsulate store access patterns | Feature-specific custom hooks | ### When to Use Client State Zustand should be used for any data that meets these criteria: * **(Do ✅)** Use for UI state like modals, drawers, and navigation state * **(Do ✅)** Use for user preferences and settings that persist across sessions * **(Do ✅)** Use for temporary form data or wizard flows * **(Do ✅)** Use for cached data that doesn't require server synchronization * **(Do ✅)** Use for cross-component communication * **(Don't ❌)** Use for server data that requires synchronization * **(Don't ❌)** Use for form state in individual components * **(Don't ❌)** Use for ephemeral UI state in single components ## Design Principles ### Core Architectural Principles * **(Do ✅)** Keep stores small and focused * Create separate stores for distinct concerns * Avoid monolithic stores that handle multiple domains * **(Do ✅)** Use TypeScript interfaces for store definitions * Define explicit types for all state and actions * Leverage TypeScript for autocompletion and error checking * **(Do ✅)** Create selectors for performance optimization * Select only the specific state slices components need * Use stable references for selectors when appropriate * **(Don't ❌)** Mix server and client state in the same store * Server state belongs in TanStack Query * Client state belongs in Zustand ### Store Organization Patterns | Pattern | Used For | Implementation | | ----------------- | ----------------------------------- | -------------------------------------- | | Global Stores | Application-wide settings, UI state | Located in `core/store/` | | Feature Stores | Feature-specific state | Located in `features/[feature]/state/` | | Transient Stores | Temporary workflow state | Created and destroyed with features | | Persistent Stores | Settings, auth tokens, preferences | Implemented with persist middleware | ## Implementation Examples ### Basic Store Structure ```typescript // core/store/uiStore.ts import { create } from 'zustand'; import { devtools } from 'zustand/middleware'; interface UIState { // State theme: 'light' | 'dark'; sidebarOpen: boolean; activeModal: string | null; // Actions setTheme: (theme: 'light' | 'dark') => void; toggleSidebar: () => void; openModal: (modalId: string) => void; closeModal: () => void; } export const useUIStore = create()( devtools( (set) => ({ // Initial state theme: 'light', sidebarOpen: false, activeModal: null, // Actions setTheme: (theme) => set({ theme }), toggleSidebar: () => set((state) => ({ sidebarOpen: !state.sidebarOpen })), openModal: (modalId) => set({ activeModal: modalId }), closeModal: () => set({ activeModal: null }), }), { name: 'ui-store', } ) ); ``` ```typescript // features/auth/state/authStore.ts import { create } from 'zustand'; import { devtools, persist } from 'zustand/middleware'; import AsyncStorage from '@react-native-async-storage/async-storage'; interface AuthState { user: User | null; token: string | null; isAuthenticated: boolean; setUser: (user: User | null) => void; setToken: (token: string | null) => void; logout: () => void; } export const useAuthStore = create()( devtools( persist( (set) => ({ user: null, token: null, isAuthenticated: false, setUser: (user) => set({ user, isAuthenticated: !!user }), setToken: (token) => set({ token }), logout: () => set({ user: null, token: null, isAuthenticated: false }), }), { name: 'auth-storage', storage: AsyncStorage, partialize: (state) => ({ token: state.token }), // Only persist token } ) ) ); ``` ```typescript // core/store/settingsStore.ts import { create } from 'zustand'; import { devtools, persist } from 'zustand/middleware'; import AsyncStorage from '@react-native-async-storage/async-storage'; interface SettingsState { theme: 'light' | 'dark'; language: string; notifications: boolean; setTheme: (theme: 'light' | 'dark') => void; setLanguage: (language: string) => void; toggleNotifications: () => void; } export const useSettingsStore = create()( devtools( persist( (set) => ({ theme: 'light', language: 'en', notifications: true, setTheme: (theme) => set({ theme }), setLanguage: (language) => set({ language }), toggleNotifications: () => set((state) => ({ notifications: !state.notifications })), }), { name: 'settings-storage', storage: AsyncStorage, } ) ) ); ``` ```typescript // features/cart/state/cartStore.ts import { create } from 'zustand'; import { immer } from 'zustand/middleware/immer'; interface CartItem { id: string; productId: string; quantity: number; price: number; } interface CartState { items: CartItem[]; // Actions addItem: (item: CartItem) => void; removeItem: (itemId: string) => void; updateQuantity: (itemId: string, quantity: number) => void; clearCart: () => void; // Computed values as functions totalItems: () => number; totalPrice: () => number; } export const useCartStore = create()( immer((set, get) => ({ items: [], addItem: (item) => set((state) => { const existingItem = state.items.find(i => i.id === item.id); if (existingItem) { existingItem.quantity += item.quantity; } else { state.items.push(item); } }), removeItem: (itemId) => set((state) => { state.items = state.items.filter(item => item.id !== itemId); }), updateQuantity: (itemId, quantity) => set((state) => { const item = state.items.find(i => i.id === itemId); if (item) item.quantity = quantity; }), clearCart: () => set({ items: [] }), totalItems: () => get().items.reduce((sum, item) => sum + item.quantity, 0), totalPrice: () => get().items.reduce((sum, item) => sum + (item.price * item.quantity), 0) })) ); ``` ## Advanced Usage Patterns ```typescript // Poor performance - re-renders on any state change const Component = () => { const store = useCartStore(); return {store.items.length}; }; // Better performance - only re-renders when relevant state changes const Component = () => { const itemCount = useCartStore((state) => state.items.length); return {itemCount}; }; // Best practice - reusable selector export const useCartItemCount = () => useCartStore((state) => state.items.length); const Component = () => { const itemCount = useCartItemCount(); return {itemCount}; }; // Advanced - stable selector functions with shallow comparison import { shallow } from 'zustand/shallow'; const Component = () => { const { add, remove } = useCartStore( (state) => ({ add: state.addItem, remove: state.removeItem }), shallow ); return ( ); } // ✅ Solution: Use functional updates or useRef function Counter() { const [count, setCount] = useState(0); const countRef = useRef(count); // Update ref when count changes useEffect(() => { countRef.current = count; }, [count]); const handleClick = () => { setTimeout(() => { alert(countRef.current); // Always shows current value }, 1000); }; return ( <>

Count: {count}

); } ``` 2. **Batched Updates**: Multiple state updates in event handlers are batched ```typescript // ❌ Problem: Both setState calls only trigger one re-render function Counter() { const [count, setCount] = useState(0); const handleClick = () => { setCount(count + 1); // count is 0 setCount(count + 1); // count is still 0, so this sets it to 1 again }; return ; } // ✅ Solution: Use functional updates function Counter() { const [count, setCount] = useState(0); const handleClick = () => { setCount(c => c + 1); // c is 0, sets to 1 setCount(c => c + 1); // c is 1, sets to 2 }; return ; } ``` 1. **Memory Leaks**: Not cleaning up effects properly ```typescript // ❌ Problem: No cleanup causes memory leaks function UserStatus() { const [isOnline, setIsOnline] = useState(false); useEffect(() => { const subscription = subscribeToUserStatus(userId, (status) => { setIsOnline(status.isOnline); }); // No cleanup, subscription stays active after unmount }, [userId]); return
{isOnline ? 'Online' : 'Offline'}
; } // ✅ Solution: Proper cleanup function function UserStatus() { const [isOnline, setIsOnline] = useState(false); useEffect(() => { const subscription = subscribeToUserStatus(userId, (status) => { setIsOnline(status.isOnline); }); // Cleanup function that runs before next effect or on unmount return () => { subscription.unsubscribe(); }; }, [userId]); return
{isOnline ? 'Online' : 'Offline'}
; } ```
1. **Unnecessary Re-renders**: Components re-render when props or state haven't changed ```typescript // ❌ Problem: New function reference on every render function ParentComponent() { const [count, setCount] = useState(0); // This creates a new function reference every render const handleClick = () => { console.log('Clicked'); }; return ; } // ✅ Solution: Memoize function with useCallback function ParentComponent() { const [count, setCount] = useState(0); // Stable function reference between renders const handleClick = useCallback(() => { console.log('Clicked'); }, []); // Only changes if dependencies change return ; } ``` ## Related Documents Overview of our complete state management approach including client, server, and component state. Companion documentation for handling global client state with Zustand. Companion documentation for handling server state with TanStack Query. # UTA State Management: State Integration Patterns URL: https://dev.updatetheapp.com/docs/state-management/integration-patterns Documentation: https://dev.updatetheapp.com/docs/state-management/integration-patterns Common patterns for integrating server and client state in React Native applications *** title: State Integration Patterns description: Common patterns for integrating server and client state in React Native applications ------------------------------------------------------------------------------------------------- # State Integration Patterns ## Overview This document outlines architectural patterns for effectively integrating different types of state management in React Native applications. It focuses on the coordination between server state (TanStack Query), client state (Zustand), and component state (React), providing solutions for common state integration challenges while maintaining a clean separation of concerns. ## Purpose & Scope * **Target Audience**: React Native developers implementing features that require coordination between multiple state management approaches * **Problems Addressed**: Complex state interactions, synchronization challenges, and maintaining data consistency * **Scope**: Covers integration patterns specifically for authentication flows, shopping carts, forms with drafts, real-time updates, and filter/search functionality ## Authentication Flow Pattern A complete authentication flow demonstrates how to coordinate between different state types: >CS: User enters credentials UI->>API: Login request API-->>UI: Returns tokens & user data UI->>ZS: Save tokens to Zustand UI->>TQ: Update user cache UI->>TQ: Invalidate protected queries Note over ZS: Tokens persisted to AsyncStorage Note over TQ: User data cached `} /> ```typescript // features/auth/state/authStore.ts (Client State) interface AuthState { token: string | null; refreshToken: string | null; setTokens: (token: string, refreshToken: string) => void; clearTokens: () => void; } export const useAuthStore = create()( persist( (set) => ({ token: null, refreshToken: null, setTokens: (token, refreshToken) => set({ token, refreshToken }), clearTokens: () => set({ token: null, refreshToken: null }), }), { name: 'auth-storage', storage: AsyncStorage, } ) ); // features/auth/api/authHooks.ts (Server State) export const useCurrentUser = () => { const token = useAuthStore((state) => state.token); return useQuery({ queryKey: ['user', 'me'], queryFn: userService.getCurrentUser, enabled: !!token, }); }; export const useLogin = () => { const queryClient = useQueryClient(); const { setTokens } = useAuthStore(); return useMutation({ mutationFn: authService.login, onSuccess: (data) => { // Update client state with tokens setTokens(data.token, data.refreshToken); // Update server state cache queryClient.setQueryData(['user', 'me'], data.user); // Invalidate protected queries queryClient.invalidateQueries({ predicate: (query) => query.queryKey[0] === 'protected' }); }, }); }; export const useLogout = () => { const queryClient = useQueryClient(); const { clearTokens } = useAuthStore(); return useMutation({ mutationFn: authService.logout, onSuccess: () => { // Clear client state clearTokens(); // Clear all cached data queryClient.clear(); }, }); }; // features/auth/components/LoginScreen.tsx (Component State) const LoginScreen = () => { const [email, setEmail] = useState(''); const [password, setPassword] = useState(''); const [rememberMe, setRememberMe] = useState(false); const loginMutation = useLogin(); const navigation = useNavigation(); const handleLogin = async () => { try { await loginMutation.mutateAsync({ email, password, rememberMe }); navigation.navigate('Home'); } catch (error) { // Handle error } }; return ( // UI implementation ); }; ``` ## Shopping Cart with Sync Pattern Managing a shopping cart that syncs with the server: LocalCart[Local Cart - Zustand] LocalCart --> Login{User Logs In} Login -->|Yes| SyncProcess[Sync Process] Login -->|No| StayLocal[Continue Local] SyncProcess --> MergeCart[Merge Local + Server Cart] MergeCart --> ServerUpdate[Update Server Cart] ServerUpdate --> ClearLocal[Clear Local Cart] ClearLocal --> UseServer[Use Server Cart] LoggedIn[Logged In User] --> ServerCart[Server Cart - TanStack Query] ServerCart --> Updates[Direct Server Updates] style LocalCart fill:#ff9,stroke:#333,stroke-width:2px style ServerCart fill:#bbf,stroke:#333,stroke-width:2px `} /> >LC: Add item to cart LC-->>U: Update UI count U->>U: Login U->>SC: Check server cart SC->>API: Get server cart API-->>SC: Return cart data SC->>LC: Get local items SC->>API: Sync carts API-->>SC: Return merged cart SC->>LC: Clear local cart Note over U,SC: Now using server cart `} /> ```typescript // features/cart/state/cartStore.ts (Client State) interface CartState { localItems: CartItem[]; addItem: (item: CartItem) => void; removeItem: (itemId: string) => void; updateQuantity: (itemId: string, quantity: number) => void; clearLocalCart: () => void; } export const useCartStore = create((set) => ({ localItems: [], addItem: (item) => set((state) => { const existing = state.localItems.find(i => i.id === item.id); if (existing) { return { localItems: state.localItems.map(i => i.id === item.id ? { ...i, quantity: i.quantity + item.quantity } : i ), }; } return { localItems: [...state.localItems, item] }; }), removeItem: (itemId) => set((state) => ({ localItems: state.localItems.filter(item => item.id !== itemId), })), updateQuantity: (itemId, quantity) => set((state) => ({ localItems: state.localItems.map(item => item.id === itemId ? { ...item, quantity } : item ), })), clearLocalCart: () => set({ localItems: [] }), })); // features/cart/api/cartHooks.ts (Server State) export const useCart = () => { const { data: user } = useCurrentUser(); return useQuery({ queryKey: ['cart', user?.id], queryFn: () => cartService.getCart(), enabled: !!user, }); }; export const useSyncCart = () => { const queryClient = useQueryClient(); const localItems = useCartStore((state) => state.localItems); const { clearLocalCart } = useCartStore(); const { data: user } = useCurrentUser(); const syncMutation = useMutation({ mutationFn: (items: CartItem[]) => cartService.syncCart(items), onSuccess: (data) => { // Update server state queryClient.setQueryData(['cart', user?.id], data); // Clear local cart after successful sync clearLocalCart(); }, }); // Sync local cart with server when user logs in useEffect(() => { if (user && localItems.length > 0) { syncMutation.mutate(localItems); } }, [user]); return syncMutation; }; // features/cart/components/CartButton.tsx const CartButton = () => { const { data: serverCart } = useCart(); const localItems = useCartStore((state) => state.localItems); const { data: user } = useCurrentUser(); // Show server cart count for logged-in users, local cart for guests const itemCount = user ? serverCart?.items.length || 0 : localItems.length; return ( ); }; ``` ## Form with Draft Saving Pattern Complex form with local state and server persistence: FormInit: Load Form FormInit --> CheckDraft: Check for draft FormInit --> LoadServer: Load existing post CheckDraft --> PopulateForm: Draft exists LoadServer --> PopulateForm: Server data exists PopulateForm --> Editing: User edits Editing --> AutoSave: On change AutoSave --> SaveDraft: After 1s delay SaveDraft --> Editing: Continue editing Editing --> Submit: User saves Submit --> ServerSave: Save to server ServerSave --> ClearDraft: Success ServerSave --> ShowError: Error ClearDraft --> [*] ShowError --> Editing `} /> |Auto-save| DS[Draft Store - Zustand] CS --> |Submit| API[Server API] API --> |Success| TQ[TanStack Query Cache] API --> |Success| DS DS --> |Delete draft| X[Clear] subgraph "Form State Flow" CS -.-> |Initialize| DS CS -.-> |Initialize| TQ end style CS fill:#f9f,stroke:#333,stroke-width:2px style DS fill:#ff9,stroke:#333,stroke-width:2px style TQ fill:#bbf,stroke:#333,stroke-width:2px `} /> ```typescript // features/posts/state/postDraftStore.ts (Client State) interface DraftState { drafts: Record; saveDraft: (id: string, draft: PostDraft) => void; getDraft: (id: string) => PostDraft | undefined; deleteDraft: (id: string) => void; } export const useDraftStore = create()( persist( (set, get) => ({ drafts: {}, saveDraft: (id, draft) => set((state) => ({ drafts: { ...state.drafts, [id]: draft }, })), getDraft: (id) => get().drafts[id], deleteDraft: (id) => set((state) => { const { [id]: _, ...rest } = state.drafts; return { drafts: rest }; }), }), { name: 'post-drafts', storage: AsyncStorage, } ) ); // features/posts/components/PostEditor.tsx (Component State + Integration) const PostEditor = ({ postId }: { postId?: string }) => { const queryClient = useQueryClient(); const { saveDraft, getDraft, deleteDraft } = useDraftStore(); // Server state for existing post const { data: existingPost } = useQuery({ queryKey: ['post', postId], queryFn: () => postService.getPost(postId!), enabled: !!postId, }); // Component state for form const [title, setTitle] = useState(''); const [content, setContent] = useState(''); const [isDirty, setIsDirty] = useState(false); // Initialize from server data or draft useEffect(() => { if (existingPost) { setTitle(existingPost.title); setContent(existingPost.content); } else { const draft = getDraft(postId || 'new'); if (draft) { setTitle(draft.title); setContent(draft.content); } } }, [existingPost, postId]); // Auto-save draft useEffect(() => { if (isDirty) { const timeoutId = setTimeout(() => { saveDraft(postId || 'new', { title, content }); setIsDirty(false); }, 1000); return () => clearTimeout(timeoutId); } }, [title, content, isDirty]); // Save to server const saveMutation = useMutation({ mutationFn: (data: PostData) => postId ? postService.updatePost(postId, data) : postService.createPost(data), onSuccess: (savedPost) => { // Clear draft after successful save deleteDraft(postId || 'new'); // Update cache queryClient.setQueryData(['post', savedPost.id], savedPost); queryClient.invalidateQueries({ queryKey: ['posts', 'list'] }); // Navigate to saved post navigation.navigate('PostDetail', { postId: savedPost.id }); }, }); const handleSave = () => { saveMutation.mutate({ title, content }); }; return ( { setTitle(text); setIsDirty(true); }} placeholder="Post title" /> { setContent(text); setIsDirty(true); }} placeholder="Post content" multiline /> ); }; ``` Pattern components should be: * Composed of multiple foundation components * Handling complex UI behavior but no domain-specific logic * Reusable across different features and contexts * Examples: Modal, Form, DataTable, Carousel, Tabs **(Consider 🤔) Extracting common UI interaction patterns into pattern components** When you notice the same combination of foundation components appearing together with similar behavior, consider creating a pattern component. **(Don't ❌) Mix domain-specific logic with pattern components** ```typescript // DON'T do this in pattern components import { useProducts } from '@/core/domains/products'; // Wrong: Pattern components shouldn't be tied to specific domains export const ProductsTable = () => { const { data: products } = useProducts(); // Domain-specific data fetching return ( ); }; ``` Instead, create a generic `DataTable` pattern component and let business components or features handle the domain-specific aspects. #### Business Components **(Do ✅) Create business components for domain-specific UI needs** Business components integrate with specific domains and display domain-specific data: ```typescript // ui/business/ProductCard/ProductCard.tsx import { Card } from '@/ui/foundation/Card'; import { Text } from '@/ui/foundation/Text'; import { Button } from '@/ui/foundation/Button'; import { useAddToCart } from '@/core/domains/cart'; export const ProductCard = ({ product }: ProductCardProps) => { const addToCart = useAddToCart(); return ( {product.name} ${product.price} ); }; ``` Business components should: * Integrate with specific domain logic and APIs * Use hooks from the domains layer * Display domain-specific data in a consistent way * Examples: ProductCard, UserAvatar, OrderSummary, PaymentForm **(Consider 🤔) Creating business components when the same domain entity UI appears in multiple features** When multiple features need to display the same type of domain entity in a consistent way, a business component helps maintain consistency and reduces duplication. **(Don't ❌) Make business components too specific to a single feature context** Business components should be reusable across different features that work with the same domain entity: ```typescript // DON'T do this - too feature-specific export const ProductDetailPageCard = ({ /* specific to one page */ }) => { // Implementation tied to one specific feature's needs }; // INSTEAD - create components that work across features export const ProductCard = ({ product, variant = 'default' }) => { // Implementation that works in product listings, search results, favorites, etc. }; ``` * Mixing component types within same folder ## 2. Component Development Standards Well-structured components make code more maintainable, testable, and easier to understand. These standards represent patterns we've found effective across many projects. ### 2.1 Component File Organization **(Do ✅) Organize each component in a dedicated folder with consistent structure** A well-organized component structure makes it easier to find files and understand their purpose: ``` Button/ ├── Button.tsx # Main component implementation ├── Button.test.tsx # Unit and integration tests ├── types.ts # TypeScript interfaces and types ├── styles.ts # Component styles (when separated) └── index.ts # Barrel export for clean imports ``` This structure provides several benefits: * **Separation of concerns**: Each file has a clear, single responsibility * **Testing clarity**: Tests are co-located with the component they test * **Import simplicity**: Barrel exports create clean import statements * **Discoverability**: Consistent structure makes finding things predictable **(Do ✅) Type Definitions:** ```typescript // ui/foundation/Button/types.ts import type { TouchableOpacityProps } from 'react-native'; export interface ButtonProps extends TouchableOpacityProps { variant?: 'primary' | 'secondary' | 'ghost'; size?: 'small' | 'medium' | 'large'; children: React.ReactNode; } ``` **(Do ✅) Barrel Export:** ```typescript // ui/foundation/Button/index.ts export { Button } from './Button'; export type { ButtonProps } from './types'; ``` ### 2.3 Design Token Integration Design tokens are the foundation of a consistent visual language. They help maintain a cohesive look and feel across the application and make theme changes much simpler to implement. **(Do ✅) Always use design tokens for visual properties** Design tokens provide a single source of truth for visual styles and enable easier theming and maintenance: ```typescript import { theme } from '@/core/shared/styles/theme'; import { typography } from '@/core/shared/styles/typography'; import { spacing } from '@/core/shared/styles/spacing'; // Good: Using design tokens for all visual properties const styles = StyleSheet.create({ container: { backgroundColor: theme.colors.surface, padding: spacing.md, borderRadius: theme.radii.md, borderWidth: theme.borders.thin, borderColor: theme.colors.border, }, text: { color: theme.colors.textPrimary, fontSize: typography.size.body, fontFamily: typography.families.regular, fontWeight: typography.weights.medium, lineHeight: typography.lineHeights.normal, }, }); ``` This approach provides several benefits: * **Consistency**: All components use the same visual values * **Themability**: Changing theme is centralized in token files * **Maintainability**: Visual changes can be made in one place * **Design alignment**: Tokens match design system specifications **(Consider 🤔) Creating component-specific token extensions when needed** For specialized components, consider extending the token system in a structured way: ```typescript // ui/foundation/Card/tokens.ts import { theme } from '@/core/shared/styles/theme'; export const cardTokens = { elevation: { low: theme.elevation.low, medium: theme.elevation.medium, high: theme.elevation.high, }, padding: { compact: theme.spacing.sm, default: theme.spacing.md, spacious: theme.spacing.lg, } }; ``` **(Don't ❌) Use arbitrary hardcoded values for visual properties** Hardcoded values create inconsistency and make maintenance difficult: ```typescript // DON'T do this: Hardcoded values with no connection to design system const styles = StyleSheet.create({ container: { backgroundColor: '#F5F5F5', // Hardcoded color padding: 16, // Arbitrary spacing borderRadius: 8, // Arbitrary radius marginBottom: 24, // Arbitrary margin }, text: { color: '#333333', // Hardcoded color fontSize: 16, // Arbitrary size fontFamily: 'Roboto', // Hardcoded font lineHeight: 24, // Arbitrary line height }, }); ``` This creates several problems: * **Inconsistency**: Different components use different values * **Maintenance challenges**: Changes require updating many files * **Theme limitations**: Dark mode and other themes become difficult to implement * **Design drift**: No guarantee that values match design specifications ### Domain Integration Rules **(Do ✅) Business components can import from domains:** ```typescript // ui/business/ProductCard/ProductCard.tsx import { useProduct } from '@/core/domains/products/hooks'; // ✅ Allowed import type { Product } from '@/core/domains/products/types'; // ✅ Allowed ``` **(Don't ❌) Foundation/Pattern components cannot import domains:** ```typescript // ui/foundation/Button/Button.tsx import { useAuth } from '@/core/domains/auth/hooks'; // ❌ Not allowed ``` ### Feature Integration Rules **(Do ✅) Features can import any UI components:** ```typescript // features/product-catalog/screens/ProductList.tsx import { Button } from '@/ui/foundation/Button'; // ✅ Allowed import { DataTable } from '@/ui/patterns/DataTable'; // ✅ Allowed import { ProductCard } from '@/ui/business/ProductCard'; // ✅ Allowed ``` **(Don't ❌) UI components cannot import from features:** ```typescript // ui/foundation/Button/Button.tsx import { ProductService } from '@/features/catalog/services'; // ❌ Not allowed ``` ## 3. Integration with Existing Architecture ### Domain Integration Rules **(Do ✅) Business components can import from domains:** ```typescript // ui/business/ProductCard/ProductCard.tsx import { useProduct } from '@/core/domains/products/hooks'; // ✅ Allowed import type { Product } from '@/core/domains/products/types'; // ✅ Allowed ``` **(Don't ❌) Foundation/Pattern components cannot import domains:** ```typescript // ui/foundation/Button/Button.tsx import { useAuth } from '@/core/domains/auth/hooks'; // ❌ Not allowed ``` ### Feature Integration Rules **(Do ✅) Features can import any UI components:** ```typescript // features/product-catalog/screens/ProductList.tsx import { Button } from '@/ui/foundation/Button'; // ✅ Allowed import { DataTable } from '@/ui/patterns/DataTable'; // ✅ Allowed import { ProductCard } from '@/ui/business/ProductCard'; // ✅ Allowed ``` **(Don't ❌) UI components cannot import from features:** ```typescript // ui/foundation/Button/Button.tsx import { ProductService } from '@/features/catalog/services'; // ❌ Not allowed ``` ## 4. Naming Conventions (Enforced) ### Component Naming * **Files**: PascalCase (e.g., `Button.tsx`, `ProductCard.tsx`) * **Folders**: PascalCase (e.g., `Button/`, `DataTable/`) * **Props Interfaces**: ComponentName + Props (e.g., `ButtonProps`, `ModalProps`) ### Consistent Export Patterns ```typescript // Always use named exports for components export const Button = ...; // ✅ Named export export default Button; // ❌ Avoid default exports // Always export types export type { ButtonProps } from './types'; // ✅ Type export ``` ## 5. Accessibility Requirements ### Required Accessibility Features Every component **must** implement these accessibility features: #### 1. Semantic Roles ```typescript // Button component // Text Input component ``` #### 2. State Communication ```typescript // Button states // Input states ``` #### 3. Dynamic Content ```typescript // Loading states {isLoading && ( )} // Error states {error && ( {error} )} ``` ## 6. Testing Requirements ### Required Test Patterns **(Do ✅) Every component must have:** * Render test * Props validation test * User interaction test (if applicable) * Accessibility test ```typescript // ComponentName.test.tsx template import { render, fireEvent } from '@testing-library/react-native'; import { Button } from './Button'; describe('Button', () => { it('renders correctly', () => { const { getByText } = render(); expect(getByText('Test')).toBeTruthy(); }); it('handles press events', () => { const onPress = jest.fn(); const { getByText } = render(); fireEvent.press(getByText('Test')); expect(onPress).toHaveBeenCalled(); }); it('has correct accessibility role', () => { const { getByRole } = render(); expect(getByRole('button')).toBeTruthy(); }); }); ``` ### Testing Coverage Requirements * **Unit Tests**: 80%+ coverage required * **Accessibility Tests**: All accessibility features must be tested * **Visual Regression Tests**: Required for all visual states * **Integration Tests**: Required for complex business components ## 7. Performance Requirements **(Do ✅) Performance Standards:** * Use React.memo for components with complex props * Implement proper prop comparison for expensive re-renders * Avoid inline styles in render methods * Use StyleSheet.create for static styles **(Don't ❌) Performance Anti-Patterns:** * Creating style objects in render * Missing memoization for expensive computations * Unnecessary re-renders from prop drilling ## 8. Documentation Requirements **(Do ✅) Every component must have:** * JSDoc comments for component and props * Usage examples in comments * Accessibility notes ```typescript /** * Primary button component for user actions * * @example * */ export const Button: React.FC = ({ ... }) => { // Implementation }; ``` ## 9. Quality Control and Enforcement ### Required Checks Before Merge * [ ] Component in correct folder (foundation/patterns/business) * [ ] Uses design tokens (no hardcoded values) * [ ] Proper TypeScript interfaces * [ ] Barrel exports implemented * [ ] Unit tests included * [ ] Accessibility features complete * [ ] Documentation complete * [ ] Performance optimizations applied ### ESLint Rules Integration ```javascript // Recommended ESLint rules { "no-restricted-imports": [ "error", { "patterns": [ { "group": ["../features/*"], "message": "UI components cannot import from features" } ] } ] } ``` ### Code Review Checklist **Architecture Compliance** * Component placed in correct folder * Follows naming conventions * Proper import/export patterns * No architecture rule violations **Design Token Usage** * All colors use theme tokens * Typography follows token system * Spacing uses consistent tokens * No hardcoded values **Accessibility** * Proper semantic roles * Screen reader support * Keyboard navigation * Focus management **Performance** * Appropriate memoization * Efficient re-rendering * Bundle size impact * Runtime performance ## 10. Migration and Adoption ### Project Migration Steps 1. Create ui/ folder with foundation/patterns/business structure 2. Move existing components to appropriate folders 3. Update all imports across the project 4. Add missing TypeScript interfaces 5. Integrate design tokens 6. Add required tests ### Legacy Component Handling * **Deprecation Period**: 3 months for existing components * **Migration Support**: Provide migration guides * **Backward Compatibility**: Maintain during transition * **Documentation**: Clear migration paths ## 11. Enforcement and Compliance ### Automated Checks * ESLint rules for import restrictions * TypeScript strict mode compliance * Test coverage requirements (80%+) * Design token usage validation ### Manual Review Process * Architecture compliance review * Accessibility audit * Performance assessment * Documentation review ### Non-Compliance Consequences * **Warning Phase**: First violation gets warning and guidance * **Blocking Phase**: Second violation blocks merge until fixed * **Training Phase**: Repeated violations require team training ## Related Documents * [UI Architecture Overview](/docs/ui-development/architecture) - Complete architecture guide * [Foundation Components Guide](/docs/ui-development/architecture/foundation-guide) - Component development patterns * [Component Development Workflow](/docs/ui-development/architecture/development-workflow) - Step-by-step development process * [AI Collaboration Guide](/docs/ui-development/architecture/ai-collaboration) - AI consistency patterns * [Core Architecture Reference](/docs/architecture/core-architecture) - System architecture * [Domain Architecture Guide](/docs/architecture/domain-architecture) - Domain integration patterns * [Project Structure Reference](/docs/architecture/project-structure) - File organization * [File Naming Conventions](/docs/coding-standards/file-naming-conventions) - Naming standards * [Communication Patterns](/docs/features/communication-patterns) - Cross-feature integration These standards ensure consistent, maintainable, and high-quality UI components across all frontend projects. # UTA UI Development: AI Collaboration Guide for UI Development URL: https://dev.updatetheapp.com/docs/ui-development/architecture/ai-collaboration Documentation: https://dev.updatetheapp.com/docs/ui-development/architecture/ai-collaboration Standardized AI prompts, patterns, and quality validation for consistent UI component development across all projects. *** title: AI Collaboration Guide for UI Development description: Standardized AI prompts, patterns, and quality validation for consistent UI component development across all projects. ----------------------------------------------------------------------------------------------------------------------------------- import { Tab, Tabs } from 'fumadocs-ui/components/tabs'; import { Callout } from 'fumadocs-ui/components/callout'; import { Steps, Step } from 'fumadocs-ui/components/steps'; # AI Collaboration Guide for UI Development ## Overview This guide provides standardized AI prompts, patterns, and quality validation techniques to help create consistent UI components across projects. It aims to enhance collaboration between developers and AI assistants while maintaining architectural standards and code quality. **Why AI Collaboration Patterns Matter** We've found that AI tools can significantly accelerate UI development, but consistency requires thoughtful prompting. These patterns have evolved through extensive experimentation to solve common challenges: * **Consistency**: Ensuring all AI-generated components follow the same standards * **Quality**: Maintaining high-quality code regardless of who (or what) wrote it * **Learning curve**: Reducing the time needed to effectively collaborate with AI * **Maintainability**: Creating components that integrate seamlessly with existing code ## Purpose & Scope This guide will help you: * Craft effective, consistent AI prompts for predictable results * Ensure AI-generated components align with our architectural standards * Validate and improve AI-generated code quality * Create components that seamlessly integrate with existing codebase * Develop a workflow that combines human expertise with AI capabilities This guide is valuable for: * **UI developers** working with AI tools on component development * **Team leads** establishing AI collaboration workflows * **New team members** learning our component patterns * **Technical writers** documenting component usage * **AI systems** being instructed on our development patterns These patterns are particularly useful when: * Creating new UI components from scratch * Extending existing components with new features * Refactoring components to follow our architecture * Documenting components for team use * Onboarding new team members to our UI development approach ## Core AI Collaboration Principles Our approach to AI collaboration is built on four key principles that help maintain quality and consistency: ### 1. Predictable Structure We help AI understand our three-layer architecture by being explicit about component categories and their characteristics: Structure["Predictable Structure"] Structure --> Foundation["Foundation Layer\nBasic, single-purpose components"] Structure --> Patterns["Patterns Layer\nComposed, complex components"] Structure --> Business["Business Layer\nDomain-aware components"] style AI fill:#f9f,stroke:#333,stroke-width:2px style Structure fill:#ffd,stroke:#333,stroke-width:2px style Foundation fill:#e1f5fe,stroke:#01579b,stroke-width:2px style Patterns fill:#f3e5f5,stroke:#4a148c,stroke-width:2px style Business fill:#e8f5e8,stroke:#1b5e20,stroke-width:2px `} /> By consistently using these structural concepts in our prompts, AI can better understand which type of component we're creating and what standards should apply. ### 2. Consistent Prompting Standardized prompts provide a common language between developers and AI systems, leading to more predictable results and reducing miscommunication. **(Consider 🤔) Using the template prompts provided in this guide** These templates were developed through iterative testing to produce high-quality, compliant components consistently. ### 3. Quality Validation All AI-generated components benefit from validation against our quality standards, covering areas like accessibility, performance, and testing. **(Do ✅) Review AI-generated code against our quality checklist** This helps catch common issues before integration and ensures all code meets our standards, regardless of its origin. ### 4. Architectural Compliance Our prompts explicitly guide AI to follow established architecture patterns, ensuring generated code fits seamlessly into our codebase. ## Standard AI Prompts for UI Components ### Foundation Component Creation #### Basic Foundation Component ``` Create a foundation [ComponentName] component following our UI architecture standards: Requirements: - Location: ui/foundation/[ComponentName]/ - Use design tokens from core/shared/styles/theme - Include TypeScript props interface with JSDoc comments - Add accessibility support with proper roles and states - Include unit tests with rendering, interaction, and accessibility coverage - Create barrel export in index.ts - Follow our established naming conventions Component should: - Accept variant, size, and disabled props as minimum - Use theme colors, spacing, and typography tokens exclusively - Handle loading and error states appropriately - Include proper accessibility labels and hints - Be memoized for performance if props are complex Example structure: - variant?: 'primary' | 'secondary' | 'ghost' - size?: 'small' | 'medium' | 'large' - disabled?: boolean - loading?: boolean - children: React.ReactNode ``` #### Interactive Foundation Component ``` Create a foundation [ComponentName] component for user interaction: Props interface should include: - variant: Visual style variants (primary, secondary, ghost, danger) - size: Component sizing options (small, medium, large) - disabled: Disabled state handling - loading: Loading state with spinner/indicator - onPress/onChange: Event handlers - children/value: Content props Implementation requirements: - Use TouchableOpacity/TextInput/Pressable as base - Integrate theme tokens for all colors, spacing, and typography - Add accessibility role and state communication - Handle disabled/loading states visually and functionally - Include proper TypeScript types with JSDoc - Add comprehensive test coverage (render, interaction, accessibility) - Use React.memo for performance optimization ``` ### Pattern Component Creation #### Composed Pattern Component ``` Create a pattern [ComponentName] component following our UI architecture standards: Requirements: - Location: ui/patterns/[ComponentName]/ - Compose using foundation components from ui/foundation/ - Use design tokens for any additional styling - Include complex UI behavior but remain domain-agnostic - Add proper TypeScript interfaces with comprehensive JSDoc - Include unit tests covering all interactions and states - Create barrel exports Component should: - Be composed from 2+ foundation components - Handle complex user interactions (multi-step, validation, etc.) - Remain reusable across different features - Include loading, error, and success states - Have comprehensive prop interfaces - Be optimized for performance with proper memoization Example: Modal component using Card, Button, Text from foundation Example: Form component using Input, Button, Text from foundation ``` #### Data Display Pattern ``` Create a pattern [ComponentName] component for data display: Props interface should include: - data: Array or object of data to display - loading: Loading state handling - error: Error state handling - onAction: Callback for user actions - variant: Display style options - pagination: Pagination controls if applicable Implementation requirements: - Use foundation components for all UI elements - Handle empty states gracefully - Include sorting/filtering if applicable - Add accessibility support for data tables/lists - Implement virtualization for large datasets - Include comprehensive loading and error states - Add proper TypeScript interfaces for data structures ``` ### Business Component Creation #### Domain-Aware Business Component ``` Create a business [ComponentName] component following our UI architecture standards: Requirements: - Location: ui/business/[ComponentName]/ - Accept [Domain] objects as props (e.g., Product, User, Order) - Use foundation/pattern components for all UI rendering - Integrate with domain hooks from core/domains/[domain]/hooks - Include domain-specific logic and calculations - Add proper TypeScript interfaces for domain objects - Include comprehensive tests including domain integration Component should: - Accept domain entities as primary props - Use domain hooks for data fetching (useProduct, useUser, etc.) - Contain business logic specific to the domain - Render using foundation/pattern components only - Handle domain-specific states (out of stock, expired, etc.) - Include domain-specific actions (add to cart, save profile, etc.) - Be reusable across multiple features Example: ProductCard accepting Product object and using useProduct hook Example: UserAvatar accepting User object and using useUser hook ``` #### Entity Management Component ``` Create a business [ComponentName] component for [Domain] entity management: Domain integration: - Import hooks from core/domains/[domain]/hooks - Import types from core/domains/[domain]/types - Use domain API methods through hooks - Handle domain-specific validation - Include domain-specific error handling Props interface should include: - [entity]Id: Identifier for the entity - on[Action]: Callback functions for entity actions - variant: Display/behavior variants - permissions: User permission handling - editable: Edit mode support Implementation requirements: - Use domain hooks for data fetching and mutations - Implement optimistic updates where appropriate - Handle domain-specific loading and error states - Include proper form validation for entity editing - Add accessibility support for all interactions - Include comprehensive tests with domain mock data ``` ## AI Quality Validation Prompts ### Pre-Implementation Validation ``` Review this component requirement and provide feedback: Component: [ComponentName] Category: [foundation/patterns/business] Purpose: [Brief description] Please validate: 1. Is the component category correct based on the requirements? 2. Are there any missing requirements for this component type? 3. What accessibility considerations should be included? 4. What testing scenarios should be covered? 5. Are there any performance considerations? 6. Does this fit well with our three-layer architecture? Provide specific recommendations for any issues found. ``` ### Post-Implementation Review ``` Review this [ComponentName] component for compliance with our UI architecture standards: Please check: 1. **Architecture Compliance**: - Component placed in correct folder (foundation/patterns/business) - Follows proper import/export patterns - No architecture rule violations 2. **Design Token Usage**: - All colors use theme.colors.* tokens - Typography uses typography.* tokens - Spacing uses theme.spacing.* tokens - No hardcoded values anywhere 3. **TypeScript Quality**: - Proper interfaces with JSDoc comments - Correct prop typing and defaults - Proper component typing 4. **Accessibility Implementation**: - Appropriate accessibilityRole set - Proper accessibilityLabel and accessibilityHint - Correct accessibilityState usage - Screen reader compatibility 5. **Performance Optimization**: - Appropriate use of React.memo - Proper memoization of expensive calculations - Efficient re-rendering patterns 6. **Testing Coverage**: - Unit tests for all functionality - Accessibility tests included - Integration tests if applicable Provide specific feedback on any issues found with code examples for fixes. ``` ### Code Migration Validation ``` Help me migrate this existing component to follow our UI architecture standards: Current component: [Paste component code] Please: 1. Analyze the current component and determine correct category (foundation/patterns/business) 2. Identify what needs to be updated to meet our standards 3. Check for hardcoded values that should use design tokens 4. Identify missing accessibility features 5. Suggest performance optimizations 6. Recommend additional tests needed 7. Provide the updated component code following our patterns Focus on maintaining existing functionality while improving architecture compliance. ``` ## AI-Assisted Development Workflows ### New Component Development Flow #### Step 1: Requirements Analysis ``` Help me design a [ComponentName] component: Context: - Purpose: [Describe what the component does] - Usage: [Describe where it will be used] - User interactions: [Describe how users interact with it] - Data requirements: [Describe what data it needs] Please help me: 1. Determine the correct component category (foundation/patterns/business) 2. Define the props interface with proper TypeScript types 3. Identify required accessibility features 4. Suggest design token usage patterns 5. Outline testing requirements 6. Identify potential performance considerations Provide a complete component specification ready for implementation. ``` #### Step 2: Implementation ``` Implement the [ComponentName] component based on this specification: [Paste specification from Step 1] Requirements: - Follow our UI architecture standards exactly - Use design tokens from core/shared/styles/ for all styling - Include comprehensive TypeScript interfaces - Add full accessibility support - Handle all specified states and interactions - Include proper error handling - Optimize for performance - Follow our established patterns and conventions Create: 1. Component implementation file 2. TypeScript interfaces file 3. Barrel export file 4. Comprehensive unit tests ``` #### Step 3: Quality Assurance ``` Perform a quality review of this implemented component: [Paste implementation] Check against our standards: - Architecture rule compliance - Design token usage (no hardcoded values) - TypeScript interface completeness - Accessibility feature implementation - Performance optimization - Testing coverage - Documentation quality Provide a detailed quality report with any issues found and specific fixes needed. ``` ### Maintenance and Enhancement Flow #### Bug Fix Assistance ``` Help me fix this issue in [ComponentName]: Issue description: [Describe the bug] Current behavior: [What's happening now] Expected behavior: [What should happen] Component code: [Paste relevant code] Please: 1. Identify the root cause of the issue 2. Provide a fix that maintains our architecture standards 3. Ensure the fix doesn't break existing functionality 4. Suggest additional tests to prevent regression 5. Check if similar issues might exist in related components Provide the corrected code with explanation of changes. ``` #### Feature Enhancement ``` Help me add [feature] to the existing [ComponentName] component: Current component: [Paste component code] New feature requirements: [Describe what needs to be added] Requirements: - Maintain backward compatibility with existing props - Follow our architecture standards - Use design tokens for any new styling - Add proper TypeScript types for new features - Include accessibility support for new functionality - Add comprehensive tests for new features - Update documentation as needed Provide the enhanced component with all changes clearly marked. ``` ## AI Prompt Templates by Use Case ### Component Creation Templates #### Quick Foundation Component ``` Create a foundation [ComponentName] that: - Uses design tokens from core/shared/styles/ - Has variant and size props - Includes accessibility support - Has comprehensive TypeScript types - Includes unit tests ``` #### Complex Pattern Component ``` Create a pattern [ComponentName] that: - Composes [list foundation components] - Handles [specific interactions] - Manages [specific states] - Remains domain-agnostic - Includes comprehensive props interface ``` #### Business Domain Component ``` Create a business [ComponentName] that: - Works with [Domain] entities - Uses [specific domain hooks] - Contains [specific business logic] - Integrates with our domain architecture - Handles [domain-specific scenarios] ``` ### Testing Templates #### Comprehensive Test Generation ``` Generate comprehensive tests for [ComponentName]: Component behavior: - Props: [list all props and expected behaviors] - Interactions: [list all user interactions] - States: [list all visual/logical states] - Accessibility: [list all a11y features] Test requirements: - Unit tests with @testing-library/react-native - Accessibility tests for all a11y features - Visual regression tests for all states - Integration tests for complex behavior - Mock any external dependencies properly ``` #### Accessibility Test Focus ``` Generate accessibility-focused tests for [ComponentName]: Please test: - Proper accessibility roles and labels - Screen reader announcements - Keyboard navigation support - State change announcements - Focus management - High contrast mode compatibility - Large text scaling support Use @testing-library/react-native patterns and include both automated and manual testing guidance. ``` ### Refactoring Templates #### Architecture Compliance Update ``` Refactor this component to follow our UI architecture standards: Current code: [paste code] Updates needed: - Move to correct folder structure - Update to use design tokens - Add missing accessibility features - Improve TypeScript interfaces - Add comprehensive tests - Optimize performance - Update documentation Maintain all existing functionality while improving compliance. ``` #### Performance Optimization ``` Optimize this component for performance: Component code: [paste code] Focus on: - Proper memoization strategies - Efficient re-rendering patterns - Bundle size optimization - Runtime performance improvements - Memory usage optimization Provide optimized code with explanations of improvements made. ``` ## Quality Assurance Integration ### Pre-Merge Checklist for AI-Generated Code Use this prompt to validate any AI-generated component: ``` Validate this component against our UI architecture standards: Component: [paste component code] Checklist: - [ ] Correct folder placement (foundation/patterns/business) - [ ] Uses design tokens exclusively (no hardcoded values) - [ ] Proper TypeScript interfaces with JSDoc - [ ] Comprehensive accessibility support - [ ] Includes unit tests (render, interaction, accessibility) - [ ] Performance optimized (memoization where needed) - [ ] Follows naming conventions - [ ] Proper barrel exports - [ ] Documentation complete - [ ] Integration with existing architecture Provide pass/fail status for each item with specific fixes for any failures. ``` ### Continuous Quality Monitoring ``` Review our recent AI-generated components for consistency: Components to review: [list recent components] Check for: 1. Pattern consistency across components 2. Design token usage compliance 3. Accessibility implementation consistency 4. TypeScript interface patterns 5. Testing pattern adherence 6. Performance optimization consistency Identify any inconsistencies and provide standardization recommendations. ``` ## Integration with Development Workflow ### Workflow Integration Points Our AI collaboration patterns integrate seamlessly with the existing development workflow: #### Planning Phase Integration ``` Combine with existing planning processes: - Requirements gathering (existing workflow) - AI-assisted component specification (this guide) - Design review (existing workflow) - Implementation planning (this guide + existing workflow) ``` #### Development Phase Integration ``` Enhanced development process: - Component structure creation (existing standards) - AI-assisted implementation (this guide) - Design token integration (existing architecture) - Code review process (existing workflow + AI validation) ``` #### Quality Assurance Integration ``` Comprehensive QA process: - Automated testing (existing standards) - AI-assisted test generation (this guide) - Manual review process (existing workflow) - Performance validation (existing standards) ``` For complete development workflow details, see [Component Development Workflow](/docs/ui-development/architecture/development-workflow). ## Troubleshooting Common AI Issues ### Issue: AI Not Using Design Tokens **Problem**: AI generates hardcoded values instead of theme tokens **Solution**: Use this corrective prompt: ``` The component you generated uses hardcoded values. Please update it to use design tokens: Replace all hardcoded values with: - Colors: theme.colors.[semantic-name][shade] - Spacing: theme.spacing.[size] - Typography: typography.sizes.[size], typography.weights.[weight] - Border radius: theme.radii.[size] Show the corrected component with all hardcoded values replaced. ``` ### Issue: AI Missing Accessibility Features **Problem**: AI generates components without proper accessibility **Solution**: Use this enhancement prompt: ``` Add comprehensive accessibility support to this component: Component: [paste code] Add: - Appropriate accessibilityRole - Descriptive accessibilityLabel - Helpful accessibilityHint - Proper accessibilityState for all states - accessibilityLiveRegion for dynamic content - Keyboard navigation support Provide the updated component with full accessibility implementation. ``` ### Issue: AI Creating Wrong Component Category **Problem**: AI places components in wrong folders or mixes concerns **Solution**: Use this correction prompt: ``` This component is in the wrong category. Please recategorize and fix: Current: [describe current implementation] Issue: [explain why it's wrong category] Correct approach: - Category: [foundation/patterns/business] - Location: ui/[category]/[ComponentName]/ - Concerns: [what it should and shouldn't do] Provide the corrected component in the right category with proper separation of concerns. ``` ## Related Documents * [UI Architecture Standards](/docs/ui-development/architecture/standards) - Mandatory development standards * [UI Architecture Overview](/docs/ui-development/architecture) - Complete architecture guide * [Foundation Components Guide](/docs/ui-development/architecture/foundation-guide) - Component development patterns * [Component Development Workflow](/docs/ui-development/architecture/development-workflow) - Step-by-step development process * [Core Architecture Reference](/docs/architecture/core-architecture) - System architecture * [Project Structure Reference](/docs/architecture/project-structure) - File organization This guide ensures consistent, high-quality AI collaboration for UI component development across all projects. # UTA UI Development: Foundation Components Guide URL: https://dev.updatetheapp.com/docs/ui-development/architecture/foundation-guide Documentation: https://dev.updatetheapp.com/docs/ui-development/architecture/foundation-guide Comprehensive guide to creating, implementing, and maintaining foundation components with design token integration and AI collaboration patterns. *** title: Foundation Components Guide description: Comprehensive guide to creating, implementing, and maintaining foundation components with design token integration and AI collaboration patterns. -------------------------------------------------------------------------------------------------------------------------------------------------------------- import { Tab, Tabs } from 'fumadocs-ui/components/tabs'; import { Callout } from 'fumadocs-ui/components/callout'; import { Steps, Step } from 'fumadocs-ui/components/steps'; # Foundation Components Guide ## Overview This guide explores patterns and practices for creating foundation components - the atomic building blocks of our design system. Foundation components serve as pure presentation components that form the basis for all other UI elements in our applications. **Why Foundation Components Matter** Foundation components solve several critical challenges in React Native development: * **Consistency**: They ensure visual and behavioral consistency across the application * **Efficiency**: They reduce duplication by centralizing common UI elements * **Maintainability**: Changes to foundation components automatically propagate throughout the app * **Collaboration**: They create a shared vocabulary between designers and developers * **Accessibility**: They encapsulate accessibility best practices in reusable units ## Purpose & Scope This guide will help you: * Understand what makes an effective foundation component * Implement components that integrate with our design token system * Create accessible, reusable, and performant UI building blocks * Maintain consistency through established patterns * Follow patterns that work well with AI collaboration This guide is valuable for: * **UI developers** building foundation components * **Design system maintainers** establishing UI standards * **Feature developers** using foundation components * **Designers** collaborating on component implementation * **QA engineers** testing component functionality To get the most from this guide, you should be familiar with: * Basic React and React Native concepts * TypeScript fundamentals * [UI Architecture Overview](/docs/ui-development/architecture) (recommended first) * [Design Token System](/docs/ui-development/theme-management) (helpful context) ## Foundation Component Principles ### Core Characteristics Effective foundation components generally share these key characteristics: 1. **Single Responsibility**: Each component focuses on one clear purpose, making it easier to understand, test, and maintain 2. **No Business Logic**: Foundation components remain free from domain knowledge or business rules, ensuring they're reusable across different contexts 3. **Design Token Driven**: Styling comes from centralized design tokens rather than hardcoded values, enabling consistent theming and easier updates 4. **Highly Reusable**: These components are designed to be used across multiple patterns, features, and projects 5. **Accessible by Default**: Accessibility is built in from the start, not added as an afterthought 6. **Predictable API**: Consistent prop patterns and naming conventions make components intuitive to use ## Guide Structure This comprehensive guide is organized into focused sections to help you master foundation component development: ### 📱 [Interactive Elements](/docs/ui-development/architecture/foundation-guide/interactive-elements) Components that respond to user input - buttons, inputs, switches, and other controls. Learn how to build accessible, responsive interactive components. ### 🎨 [Display Components](/docs/ui-development/architecture/foundation-guide/display-components) Visual elements that present information - text, cards, images, and containers. Master typography, layout, and visual hierarchy. ### 🎯 [Design Token Integration](/docs/ui-development/architecture/foundation-guide/design-tokens) Deep dive into using design tokens for colors, typography, spacing, and shadows. Ensure consistency across your component library. ### ♿ [Accessibility Patterns](/docs/ui-development/architecture/foundation-guide/accessibility-patterns) Comprehensive accessibility implementation including roles, states, focus management, and testing strategies. ### 🧪 [Testing & Performance](/docs/ui-development/architecture/foundation-guide/testing-performance) Testing strategies, visual regression, performance optimization, and memoization patterns for reliable components. ### 🤖 [AI Collaboration & Documentation](/docs/ui-development/architecture/foundation-guide/ai-collaboration-docs) Effective AI prompts, documentation standards, and maintenance strategies for sustainable component development. ## Foundation Component Categories Foundation components form the basis of our entire UI system. By grouping them into logical categories, we can ensure consistent patterns across similar component types and make them easier to discover and use. The foundation layer includes these primary categories: * **Interactive Elements**: Components that handle user interaction * **Display Elements**: Components that present information * **Layout Components**: Components that structure content * **Feedback Components**: Components that communicate state Each category follows consistent patterns while addressing its specific requirements. ## Best Practices Summary When building foundation components: * ✅ **Start with the interface**: Define clear TypeScript types * ✅ **Use design tokens**: Never hardcode colors, spacing, or typography * ✅ **Build accessible**: Include proper roles, states, and labels * ✅ **Test thoroughly**: Cover all states, interactions, and edge cases * ✅ **Document clearly**: Provide examples and usage guidelines * ✅ **Optimize performance**: Use memoization appropriately ## Component Architecture Foundation components follow a consistent architectural pattern that aligns with our [Three-Layer Architecture](/docs/ui-development/architecture#three-layer-architecture): * **Props Layer**: Well-defined TypeScript interfaces * **Implementation Layer**: React Native components with hooks * **Style Layer**: Design token integration * **Test Layer**: Comprehensive test coverage This architecture ensures components are predictable, maintainable, and easy to understand. ## Getting Started #### Review Core Principles Understand the characteristics that define effective foundation components #### Explore Component Categories Browse the different types of foundation components and their patterns #### Learn Token Integration Master the use of design tokens for consistent styling #### Implement Accessibility Ensure your components work for all users #### Test Comprehensively Write tests that ensure reliability and catch regressions ## Next Steps * Start with [Interactive Elements](/docs/ui-development/architecture/foundation-guide/interactive-elements) to learn about building user controls * Review our [Component Examples Library](/docs/reference-implementations/ui) for implementation references * Check the [Component Development Workflow](/docs/ui-development/architecture/development-workflow) for the complete development process ## Related Documents * [UI Architecture Overview](/docs/ui-development/architecture) - Understanding the three-layer architecture * [Component Development Workflow](/docs/ui-development/architecture/development-workflow) - Step-by-step development process * [Component Patterns](/docs/ui-development/development/component-patterns) - Higher-level composition patterns * [Design Token System](/docs/ui-development/theme-management) - Theme and token management * [Component Examples Library](/docs/reference-implementations/ui) - Implementation examples # UTA UI Development: Interactive Elements URL: https://dev.updatetheapp.com/docs/ui-development/architecture/foundation-guide/interactive-elements Documentation: https://dev.updatetheapp.com/docs/ui-development/architecture/foundation-guide/interactive-elements Foundation components that respond to user input - buttons, inputs, switches, and other interactive controls. *** title: Interactive Elements description: Foundation components that respond to user input - buttons, inputs, switches, and other interactive controls. -------------------------------------------------------------------------------------------------------------------------- import { Tab, Tabs } from 'fumadocs-ui/components/tabs'; import { Callout } from 'fumadocs-ui/components/callout'; # Interactive Elements ## Overview Interactive elements are foundation components that respond to user input such as taps, swipes, or keyboard actions. They form the basis of all user interactions in our applications and must be built with careful attention to accessibility, performance, and user experience. **Key Principles for Interactive Components** All interactive elements should: * Provide clear visual feedback for all states (default, hover, pressed, disabled) * Meet minimum touch target sizes (44×44 points) * Include comprehensive keyboard support * Communicate state changes to assistive technologies * Handle loading and error states gracefully ## Button Components The Button is perhaps the most fundamental interactive component in any UI system. It serves as the primary way users trigger actions in our applications. **📋 Complete Implementation Available** See our [Button Reference Implementation](/docs/reference-implementations/ui/foundation/Button) for a production-ready example with TypeScript types, accessibility features, testing patterns, and usage examples. Perfect for copy-pasting into your project! ### Component Interface ```typescript // ui/foundation/Button/types.ts import type { TouchableOpacityProps } from 'react-native'; export interface ButtonProps extends Omit { /** * Visual style variant of the button * @default 'primary' */ variant?: 'primary' | 'secondary' | 'ghost' | 'danger'; /** * Size of the button * @default 'medium' */ size?: 'small' | 'medium' | 'large'; /** * When true, shows a loading spinner and disables interaction * @default false */ loading?: boolean; /** * Optional icon to display before text */ icon?: React.ReactNode; /** * Optional icon to display after text */ iconRight?: React.ReactNode; /** * When true, button expands to fill its container width * @default false */ fullWidth?: boolean; /** * Button content (typically text) */ children: React.ReactNode; /** * Optional custom styles (use sparingly, prefer variants) */ style?: ViewStyle; } ``` **Button Props Reference** | Prop | Type | Default | Description | | ----------- | ------------------------------------------------- | ----------- | --------------------- | | `variant` | `'primary' \| 'secondary' \| 'ghost' \| 'danger'` | `'primary'` | Visual style variant | | `size` | `'small' \| 'medium' \| 'large'` | `'medium'` | Button size | | `loading` | `boolean` | `false` | Shows loading spinner | | `disabled` | `boolean` | `false` | Disables interaction | | `icon` | `ReactNode` | - | Icon before text | | `iconRight` | `ReactNode` | - | Icon after text | | `fullWidth` | `boolean` | `false` | Full container width | | `onPress` | `() => void` | - | Press handler | | `children` | `ReactNode` | - | Button content | **Inherited Props** Button also accepts all standard `TouchableOpacity` props except `style`. ```tsx // Basic usage // With variant and size // With loading state // With icons // Danger variant for destructive actions // Full width button ``` ### Implementation Guidelines **Implementation Considerations** Buttons appear simple but require careful implementation to handle all scenarios effectively: * **Accessibility**: Ensure proper roles, states, and labels * **Loading states**: Prevent multiple submissions while actions are pending * **Touch feedback**: Provide appropriate visual feedback on press * **Variants**: Support different visual styles for different contexts * **Composition**: Allow composition with icons and other elements #### Design Token Integration Buttons should use design tokens for all visual properties: ```typescript // ui/foundation/Button/Button.tsx import { theme } from '@/core/shared/styles/theme'; import { spacing } from '@/core/shared/styles/spacing'; import { typography } from '@/core/shared/styles/typography'; // Style implementation using design tokens const getStyles = (variant: ButtonVariant, size: ButtonSize) => ({ container: { backgroundColor: theme.colors[variant].background, borderRadius: theme.radii.md, padding: spacing[size], alignItems: 'center', justifyContent: 'center', flexDirection: 'row', }, text: { color: theme.colors[variant].text, fontSize: typography.size[size], fontFamily: typography.families.medium, }, }); ``` #### Loading State Implementation Loading states should always: * Disable the button to prevent multiple submissions * Show a visual indicator (typically a spinner) * Maintain the button's size to prevent layout shifts * Provide appropriate accessibility updates ### Button Best Practices **(Do ✅) Follow these Button component best practices** * Use semantic colors based on button purpose (e.g., danger for destructive actions) * Include appropriate touch targets (minimum 44×44 points) * Maintain sufficient contrast ratios between text and background * Use consistent spacing between buttons when grouped * Always include a visible focus state for keyboard navigation * Provide clear, action-oriented button text **(Don't ❌) Make these common Button component mistakes** * Using ambiguous or generic text like "Click Here" or "Submit" * Placing too many buttons with equal visual weight in proximity * Using buttons when links would be more appropriate (navigation vs. action) * Creating custom one-off button styles instead of using variants * Omitting loading states for asynchronous actions * Making touch targets too small for comfortable interaction ### Complete Button Implementation For a complete implementation of the Button component with all variants, states, accessibility features, and testing patterns, see the [Button Reference Implementation](/docs/reference-implementations/ui/foundation/Button). The Button example demonstrates: * ✅ All visual variants (primary, secondary, ghost, danger) * ✅ Size variations (small, medium, large) * ✅ Loading and disabled states * ✅ Icon support (left and right) * ✅ Full accessibility implementation * ✅ Design token integration * ✅ TypeScript interfaces * ✅ Comprehensive testing patterns ## Input Components Input components handle text entry and form interactions. They must balance functionality with accessibility and user experience across different platforms and input methods. ### Input Interface For a complete implementation of the Input component with all variants, validation, accessibility features, and testing patterns, see the [Input Reference Implementation](/docs/reference-implementations/ui/foundation/Input). The Input example demonstrates: * ✅ Multiple variants (outlined, filled) * ✅ Size variations (small, medium, large) * ✅ Validation with error messages * ✅ Helper text support * ✅ Icon support (left and right) * ✅ Focus state management * ✅ Full accessibility implementation * ✅ Comprehensive testing patterns ### Input Best Practices **(Do ✅) Input component best practices** * Provide clear labels for all inputs * Show validation feedback inline and immediately * Support autofill and password managers * Include helper text for complex inputs * Make error messages specific and actionable * Support platform-specific input behaviors **(Don't ❌) Common input mistakes** * Validating only on form submission * Using placeholder text as the only label * Making error messages vague or technical * Ignoring platform keyboard differences * Forgetting to handle secure text entry * Using tiny touch targets for input fields ## Switch Components Switches provide binary on/off controls for settings and preferences. ### Switch Interface ```typescript export interface SwitchProps { /** * Current value of the switch */ value: boolean; /** * Called when switch value changes */ onValueChange: (value: boolean) => void; /** * Disabled state * @default false */ disabled?: boolean; /** * Size variant * @default 'medium' */ size?: 'small' | 'medium' | 'large'; /** * Color when switch is on * @default theme.colors.primary[500] */ activeColor?: string; /** * Accessibility label */ accessibilityLabel: string; } ``` ### Switch Implementation Considerations * **Visual feedback**: Smooth animations for state changes * **Touch targets**: Ensure adequate size for easy interaction * **Color contrast**: Maintain visibility in both states * **Platform consistency**: Match iOS and Android patterns * **Accessibility**: Clear state announcements ## Checkbox Components Checkboxes allow users to select multiple options from a set. ### Checkbox Interface ```typescript export interface CheckboxProps { /** * Whether checkbox is checked */ checked: boolean; /** * Called when checkbox is pressed */ onChange: (checked: boolean) => void; /** * Indeterminate state (partial selection) * @default false */ indeterminate?: boolean; /** * Disabled state * @default false */ disabled?: boolean; /** * Label text */ label?: string; /** * Size variant * @default 'medium' */ size?: 'small' | 'medium' | 'large'; /** * Error state * @default false */ error?: boolean; } ``` ### Checkbox Best Practices * Support indeterminate state for parent-child relationships * Include clear touch targets beyond the checkbox itself * Provide visual feedback for all states * Support keyboard navigation (Space to toggle) * Group related checkboxes appropriately ## Radio Button Components Radio buttons allow single selection from a group of options. ### RadioButton Interface ```typescript export interface RadioButtonProps { /** * Whether radio is selected */ selected: boolean; /** * Called when radio is pressed */ onPress: () => void; /** * Value of this radio option */ value: string; /** * Display label */ label: string; /** * Disabled state * @default false */ disabled?: boolean; /** * Size variant * @default 'medium' */ size?: 'small' | 'medium' | 'large'; } export interface RadioGroupProps { /** * Currently selected value */ value: string; /** * Called when selection changes */ onChange: (value: string) => void; /** * Group name for accessibility */ name: string; /** * Radio options */ children: React.ReactNode; } ``` ## Slider Components Sliders allow users to select values from a continuous range. ### Slider Interface ```typescript export interface SliderProps { /** * Current value */ value: number; /** * Value change handler */ onValueChange: (value: number) => void; /** * Minimum value * @default 0 */ minimumValue?: number; /** * Maximum value * @default 100 */ maximumValue?: number; /** * Step interval * @default 1 */ step?: number; /** * Show value label * @default false */ showValueLabel?: boolean; /** * Disabled state * @default false */ disabled?: boolean; } ``` ## General Best Practices for Interactive Elements ### Accessibility Requirements All interactive elements must: 1. **Provide proper roles** ```tsx accessibilityRole="button" accessibilityRole="switch" accessibilityRole="checkbox" ``` 2. **Communicate states** ```tsx accessibilityState={{ disabled: isDisabled, checked: isChecked, selected: isSelected, busy: isLoading, }} ``` 3. **Include descriptive labels** ```tsx accessibilityLabel="Save changes" accessibilityHint="Double tap to save your profile changes" ``` ### Performance Considerations * Use `React.memo` for components that receive stable props * Memoize style calculations with `useMemo` * Debounce rapid interactions when appropriate * Optimize animation performance with `useNativeDriver` ### Testing Interactive Elements ```typescript // Example test structure describe('Interactive Component', () => { it('responds to user interaction', () => { const onPress = jest.fn(); const { getByRole } = render( ); fireEvent.press(getByRole('button')); expect(onPress).toHaveBeenCalledTimes(1); }); it('respects disabled state', () => { const onPress = jest.fn(); const { getByRole } = render( ); fireEvent.press(getByRole('button')); expect(onPress).not.toHaveBeenCalled(); }); }); ``` ## Summary Interactive elements form the foundation of user interaction in your application. By following these patterns and best practices, you can create components that are: * **Accessible**: Work for all users regardless of abilities * **Performant**: Respond quickly to user input * **Consistent**: Behave predictably across the application * **Maintainable**: Easy to update and extend ## Next Steps * Explore [Display Components](/docs/ui-development/architecture/foundation-guide/display-components) for presenting information * Learn about [Design Token Integration](/docs/ui-development/architecture/foundation-guide/design-tokens) for consistent styling * Review [Accessibility Patterns](/docs/ui-development/architecture/foundation-guide/accessibility-patterns) for inclusive design ## Related Documents * [Button Reference Implementation](/docs/reference-implementations/ui/foundation/Button) * [Input Reference Implementation](/docs/reference-implementations/ui/foundation/Input) * [Component Development Workflow](/docs/ui-development/architecture/development-workflow) * [Accessibility Guidelines](/docs/ui-development/architecture/foundation-guide/accessibility-patterns) # UTA UI Development: Display Components URL: https://dev.updatetheapp.com/docs/ui-development/architecture/foundation-guide/display-components Documentation: https://dev.updatetheapp.com/docs/ui-development/architecture/foundation-guide/display-components Visual elements that present information - text, cards, images, and containers for content presentation. *** title: Display Components description: Visual elements that present information - text, cards, images, and containers for content presentation. --------------------------------------------------------------------------------------------------------------------- import { Tab, Tabs } from 'fumadocs-ui/components/tabs'; import { Callout } from 'fumadocs-ui/components/callout'; # Display Components ## Overview Display components are foundation elements that present information to users without direct interaction. They focus on visual presentation, typography, layout, and content organization while maintaining consistency through design tokens. **Key Principles for Display Components** All display components should: * Use design tokens for all visual properties * Support responsive sizing and scaling * Maintain proper visual hierarchy * Ensure content accessibility * Handle loading and error states gracefully ## Text Component The Text component is the foundation for all typography in your application. It ensures consistent text rendering across platforms while supporting various typographic styles. ### Text Interface ```typescript // ui/foundation/Text/types.ts import type { TextProps as RNTextProps } from 'react-native'; export interface TextProps extends RNTextProps { /** * Typography variant * @default 'body' */ variant?: 'heading' | 'subheading' | 'body' | 'caption' | 'label'; /** * Size variant * @default 'medium' */ size?: 'small' | 'medium' | 'large' | 'xlarge'; /** * Text color * @default 'primary' */ color?: 'primary' | 'secondary' | 'disabled' | 'error' | 'success' | 'warning' | string; /** * Font weight * @default 'normal' */ weight?: 'normal' | 'medium' | 'semibold' | 'bold'; /** * Text alignment * @default 'left' */ align?: 'left' | 'center' | 'right' | 'justify'; /** * Text content */ children: React.ReactNode; } ``` ```tsx // Basic text usage Default body text // Heading variants Main Heading Section Title // Colored text Error message text ✓ Operation successful // Caption text Last updated 2 hours ago // Custom styling while maintaining tokens Text with custom spacing ``` **(Do ✅) Text component best practices** * Use semantic variants (heading, body, caption) for proper hierarchy * Apply color tokens for consistency * Use weight variations sparingly for emphasis * Ensure sufficient contrast ratios for readability * Test text truncation and wrapping behavior * Include proper accessibility roles for semantic text **(Don't ❌) Common text mistakes** * Hardcoding font sizes or colors * Using too many font weights (stick to 2-3 max) * Ignoring platform-specific rendering differences * Forgetting numberOfLines for potentially long text * Using Text for non-text content (icons, images) * Missing language/locale considerations ### Text Implementation ```typescript // ui/foundation/Text/Text.tsx import React from 'react'; import { Text as RNText } from 'react-native'; import { theme } from '@/core/shared/styles/theme'; import { typography } from '@/core/shared/styles/typography'; import type { TextProps } from './types'; export const Text: React.FC = ({ variant = 'body', size = 'medium', color = 'primary', weight = 'normal', align = 'left', children, style, ...props }) => { const getVariantStyles = () => { switch (variant) { case 'heading': return { fontWeight: typography.weights.bold, lineHeight: typography.lineHeights.tight, }; case 'subheading': return { fontWeight: typography.weights.semibold, lineHeight: typography.lineHeights.normal, }; case 'body': return { fontWeight: typography.weights.normal, lineHeight: typography.lineHeights.normal, }; case 'caption': return { fontWeight: typography.weights.normal, lineHeight: typography.lineHeights.normal, fontSize: typography.sizes.small, }; } }; const getColorValue = () => { switch (color) { case 'primary': return theme.colors.text.primary; case 'secondary': return theme.colors.text.secondary; case 'disabled': return theme.colors.text.disabled; case 'error': return theme.colors.error[500]; case 'success': return theme.colors.success[500]; case 'warning': return theme.colors.warning[500]; default: return typeof color === 'string' ? color : theme.colors.text.primary; } }; const textStyles = { ...getVariantStyles(), fontSize: variant === 'caption' ? typography.sizes.small : typography.sizes[size], fontWeight: typography.weights[weight], color: getColorValue(), textAlign: align, }; return ( {children} ); }; ``` ## Card Component Cards are containers that group related content and actions, providing visual boundaries and consistent spacing. ### Card Interface ```typescript // ui/foundation/Card/types.ts import type { ViewProps } from 'react-native'; export interface CardProps extends ViewProps { /** * Visual variant * @default 'elevated' */ variant?: 'elevated' | 'outlined' | 'filled'; /** * Padding size * @default 'medium' */ padding?: 'none' | 'small' | 'medium' | 'large' | number; /** * Card content */ children: React.ReactNode; } ``` ```tsx // Basic card Card Title Card content goes here // Outlined variant // Filled variant for emphasis // Card with custom padding // Nested cards (use sparingly) Parent Card Nested content ``` **Common Card Patterns** ```tsx // Product card pattern {product.name} {product.description} ${product.price} // List item card {user.name} {user.role} // Stats card Total Revenue $24,500 ↑ 12% from last month ``` ### Card Implementation ```typescript // ui/foundation/Card/Card.tsx import React from 'react'; import { View } from 'react-native'; import { theme } from '@/core/shared/styles/theme'; import { shadows } from '@/core/shared/styles/shadows'; import type { CardProps } from './types'; export const Card: React.FC = ({ variant = 'elevated', padding = 'medium', children, style, ...props }) => { const getVariantStyles = () => { switch (variant) { case 'elevated': return { backgroundColor: theme.colors.surface, ...shadows.medium, }; case 'outlined': return { backgroundColor: theme.colors.surface, borderWidth: 1, borderColor: theme.colors.border.default, }; case 'filled': return { backgroundColor: theme.colors.surface.secondary, }; } }; const getPaddingValue = () => { switch (padding) { case 'none': return 0; case 'small': return theme.spacing.sm; case 'medium': return theme.spacing.md; case 'large': return theme.spacing.lg; default: return typeof padding === 'number' ? padding : theme.spacing.md; } }; const cardStyles = { ...getVariantStyles(), borderRadius: theme.radii.lg, padding: getPaddingValue(), }; return ( {children} ); }; ``` ## Image Component The Image component extends React Native's Image with consistent sizing, loading states, and accessibility features. ### Image Interface ```typescript export interface ImageProps extends RNImageProps { /** * Image dimensions preset * @default 'medium' */ size?: 'small' | 'medium' | 'large' | 'full' | { width: number; height: number }; /** * Border radius variant * @default 'none' */ rounded?: 'none' | 'small' | 'medium' | 'large' | 'full'; /** * Aspect ratio */ aspectRatio?: number; /** * Fallback component for loading/error */ fallback?: React.ReactNode; /** * Alt text for accessibility */ alt: string; } ``` ### Image Best Practices * Always provide meaningful alt text * Use consistent sizing presets * Handle loading and error states * Optimize image sizes for performance * Consider lazy loading for lists * Test on various screen densities ## Icon Component Icons are essential for visual communication and UI enhancement. ### Icon Interface ```typescript export interface IconProps { /** * Icon name from the icon set */ name: string; /** * Icon size * @default 24 */ size?: number | 'small' | 'medium' | 'large'; /** * Icon color * @default theme.colors.text.primary */ color?: string; /** * Accessibility label */ accessibilityLabel?: string; } ``` ### Icon Usage Guidelines * Use semantic names for icons * Maintain consistent sizing * Ensure sufficient contrast * Provide labels for standalone icons * Group related icons visually * Consider platform-specific icons ## Badge Component Badges display small amounts of information, typically numbers or status indicators. ### Badge Interface ```typescript export interface BadgeProps { /** * Badge content */ children: React.ReactNode; /** * Visual variant * @default 'default' */ variant?: 'default' | 'primary' | 'success' | 'warning' | 'error'; /** * Size variant * @default 'medium' */ size?: 'small' | 'medium' | 'large'; /** * Dot indicator only * @default false */ dot?: boolean; } ``` ## Avatar Component Avatars represent users or entities with images or initials. ### Avatar Interface ```typescript export interface AvatarProps { /** * Image source */ source?: ImageSourcePropType; /** * Fallback text (initials) */ name?: string; /** * Size variant * @default 'medium' */ size?: 'small' | 'medium' | 'large' | 'xlarge' | number; /** * Shape variant * @default 'circle' */ shape?: 'circle' | 'square'; /** * Online status indicator */ status?: 'online' | 'offline' | 'away' | 'busy'; } ``` ## General Best Practices for Display Components ### Performance Optimization * Use `React.memo` for components with stable props * Implement lazy loading for images in lists * Optimize image assets before bundling * Cache computed styles with `StyleSheet.create` * Minimize re-renders with proper prop design ### Accessibility Considerations * Provide alternative text for all images * Ensure text has sufficient contrast * Support Dynamic Type scaling * Include proper semantic markup * Test with screen readers ### Testing Display Components ```typescript describe('Display Component Tests', () => { it('renders with correct styles', () => { const { getByText } = render( Test Heading ); const text = getByText('Test Heading'); expect(text.props.style).toMatchObject({ fontSize: typography.sizes.large, color: theme.colors.text.primary, }); }); it('applies design tokens correctly', () => { const { getByTestId } = render( Content ); const card = getByTestId('test-card'); expect(card.props.style).toMatchObject({ backgroundColor: theme.colors.surface, borderRadius: theme.radii.lg, }); }); }); ``` ## Summary Display components are the visual building blocks of your application. They should: * **Be Consistent**: Use design tokens exclusively * **Be Flexible**: Support common variations and patterns * **Be Accessible**: Include proper semantics and alternatives * **Be Performant**: Optimize for smooth rendering * **Be Testable**: Enable easy visual regression testing ## Next Steps * Learn about [Design Token Integration](/docs/ui-development/architecture/foundation-guide/design-tokens) for consistent styling * Explore [Accessibility Patterns](/docs/ui-development/architecture/foundation-guide/accessibility-patterns) for inclusive design * Review [Testing & Performance](/docs/ui-development/architecture/foundation-guide/testing-performance) optimization strategies ## Related Documents * [Typography System](/docs/ui-development/styling/typography) * [Color System](/docs/ui-development/styling/colors) * [Layout Components](/docs/ui-development/architecture/foundation-guide/layout-components) * [Component Examples Library](/docs/reference-implementations/ui) # UTA UI Development: Design Token Integration URL: https://dev.updatetheapp.com/docs/ui-development/architecture/foundation-guide/design-tokens Documentation: https://dev.updatetheapp.com/docs/ui-development/architecture/foundation-guide/design-tokens Deep dive into using design tokens for colors, typography, spacing, and shadows to ensure consistency across your component library. *** title: Design Token Integration description: Deep dive into using design tokens for colors, typography, spacing, and shadows to ensure consistency across your component library. ------------------------------------------------------------------------------------------------------------------------------------------------- import { Tab, Tabs } from 'fumadocs-ui/components/tabs'; import { Callout } from 'fumadocs-ui/components/callout'; # Design Token Integration ## Overview Design tokens are the visual atoms of your design system - the smallest pieces of your visual language. They ensure consistency across your entire component library by centralizing design decisions into reusable values. **Why Design Tokens Matter** Design tokens provide: * **Consistency**: Single source of truth for design values * **Maintainability**: Update once, changes propagate everywhere * **Theming**: Easy switching between light/dark modes * **Platform Unity**: Same visual language across iOS and Android * **Design-Dev Sync**: Shared vocabulary between designers and developers ## Token Categories Our design system uses these primary token categories: 1. **Color Tokens**: Brand colors, semantic colors, and surfaces 2. **Typography Tokens**: Font families, sizes, weights, and line heights 3. **Spacing Tokens**: Consistent spacing scale 4. **Radius Tokens**: Border radius values 5. **Shadow Tokens**: Elevation and depth 6. **Motion Tokens**: Animation durations and easings ## Color Token Usage ### Color Token Structure ```typescript // core/shared/styles/theme/colors.ts export const colors = { // Brand colors primary: { 50: '#e3f2fd', 100: '#bbdefb', 200: '#90caf9', 300: '#64b5f6', 400: '#42a5f5', 500: '#2196f3', // Main brand color 600: '#1e88e5', 700: '#1976d2', 800: '#1565c0', 900: '#0d47a1', }, // Semantic colors success: { light: '#4caf50', main: '#2e7d32', dark: '#1b5e20', }, error: { light: '#ef5350', main: '#d32f2f', dark: '#c62828', }, // Text colors text: { primary: 'rgba(0, 0, 0, 0.87)', secondary: 'rgba(0, 0, 0, 0.60)', disabled: 'rgba(0, 0, 0, 0.38)', inverse: '#ffffff', }, // Surface colors surface: { background: '#f5f5f5', paper: '#ffffff', elevated: '#ffffff', }, // Border colors border: { default: 'rgba(0, 0, 0, 0.12)', focus: '#2196f3', error: '#d32f2f', }, }; ``` ```typescript // ✅ Correct: Use semantic color tokens const buttonStyles = { backgroundColor: theme.colors.primary[500], // Primary action color borderColor: theme.colors.border.default, // Default border color: theme.colors.text.primary, // Primary text }; // ✅ Correct: Use state-based colors const getButtonColor = (state: 'default' | 'hover' | 'pressed') => { switch (state) { case 'hover': return theme.colors.primary[600]; case 'pressed': return theme.colors.primary[700]; default: return theme.colors.primary[500]; } }; // ❌ Wrong: Hardcoded colors const buttonStyles = { backgroundColor: '#3b82f6', // Don't do this color: '#000000', // Use tokens instead }; // Component implementation export const Button = ({ variant, ...props }) => { const styles = { backgroundColor: theme.colors[variant][500], color: theme.colors[variant].contrast || theme.colors.text.inverse, }; return ; }; ``` **(Do ✅) Color token best practices** * Use semantic tokens (primary, error, success) over raw colors * Apply opacity through RGBA values, not opacity styles * Test color contrast for accessibility (4.5:1 minimum) * Use consistent color scales (50-900) * Define inverse colors for dark backgrounds * Create aliases for common use cases **(Don't ❌) Color token mistakes** * Never hardcode hex/rgb values in components * Don't create one-off color variations * Avoid using colors outside the token system * Don't mix opacity methods (RGBA vs opacity prop) * Never assume color meanings across cultures * Don't forget platform-specific color behaviors ### Theme-Aware Colors ```typescript // Support for light/dark themes const useThemeColors = () => { const isDark = useColorScheme() === 'dark'; return { background: isDark ? colors.dark.background : colors.light.background, text: isDark ? colors.dark.text : colors.light.text, surface: isDark ? colors.dark.surface : colors.light.surface, }; }; // Component using theme-aware colors export const ThemedCard = ({ children }) => { const colors = useThemeColors(); return ( {children} ); }; ``` ## Typography Token Usage ### Typography Token Structure ```typescript // core/shared/styles/theme/typography.ts export const typography = { // Font families families: { regular: 'Inter-Regular', medium: 'Inter-Medium', semibold: 'Inter-SemiBold', bold: 'Inter-Bold', mono: 'JetBrainsMono-Regular', }, // Font sizes sizes: { xs: 12, sm: 14, md: 16, lg: 18, xl: 20, '2xl': 24, '3xl': 30, '4xl': 36, }, // Font weights weights: { normal: '400', medium: '500', semibold: '600', bold: '700', }, // Line heights lineHeights: { tight: 1.25, normal: 1.5, relaxed: 1.75, loose: 2, }, // Letter spacing letterSpacing: { tight: -0.5, normal: 0, wide: 0.5, }, }; ``` ```typescript // ✅ Correct: Use typography tokens const textStyles = { fontSize: typography.sizes.large, fontWeight: typography.weights.semibold, lineHeight: typography.lineHeights.normal, fontFamily: typography.families.regular, }; // ✅ Correct: Create text variants const textVariants = { heading: { fontSize: typography.sizes['2xl'], fontWeight: typography.weights.bold, lineHeight: typography.lineHeights.tight, fontFamily: typography.families.bold, }, body: { fontSize: typography.sizes.md, fontWeight: typography.weights.normal, lineHeight: typography.lineHeights.normal, fontFamily: typography.families.regular, }, caption: { fontSize: typography.sizes.sm, fontWeight: typography.weights.normal, lineHeight: typography.lineHeights.normal, fontFamily: typography.families.regular, }, }; // Component implementation export const Text = ({ variant = 'body', ...props }) => { const styles = textVariants[variant]; return ; }; ``` ```typescript // Responsive typography with scaling import { PixelRatio } from 'react-native'; const scaleFont = (size: number) => { const scale = PixelRatio.getFontScale(); return size * scale; }; // Create responsive variants const responsiveTypography = { sizes: Object.entries(typography.sizes).reduce( (acc, [key, value]) => ({ ...acc, [key]: scaleFont(value), }), {} ), }; // Dynamic type support const useDynamicType = () => { const [fontScale, setFontScale] = useState(1); useEffect(() => { const updateScale = () => { setFontScale(PixelRatio.getFontScale()); }; const subscription = AccessibilityInfo.addEventListener( 'change', updateScale ); return () => subscription.remove(); }, []); return fontScale; }; ``` ## Spacing Token Usage ### Spacing Scale ```typescript // core/shared/styles/theme/spacing.ts export const spacing = { // Base unit: 4px xxs: 2, // 2px xs: 4, // 4px - base unit sm: 8, // 8px md: 16, // 16px - default lg: 24, // 24px xl: 32, // 32px '2xl': 48, // 48px '3xl': 64, // 64px '4xl': 80, // 80px }; // Semantic spacing export const semanticSpacing = { // Component spacing componentPadding: { small: spacing.sm, medium: spacing.md, large: spacing.lg, }, // Layout spacing layoutMargin: { small: spacing.sm, medium: spacing.md, large: spacing.xl, }, // Grid spacing gridGap: { small: spacing.xs, medium: spacing.sm, large: spacing.md, }, }; ``` ```typescript // ✅ Correct: Use spacing tokens const layoutStyles = { padding: theme.spacing.md, marginVertical: theme.spacing.lg, gap: theme.spacing.sm, }; // ✅ Correct: Responsive spacing const getSpacing = (size: 'compact' | 'comfortable' | 'spacious') => { switch (size) { case 'compact': return theme.spacing.sm; case 'spacious': return theme.spacing.xl; default: return theme.spacing.md; } }; // ✅ Correct: Consistent spacing in lists const ListItem = ({ children, last }) => ( {children} ); // ❌ Wrong: Hardcoded spacing const badStyles = { padding: 16, // Use tokens margin: '12px', // No strings gap: 8, // Use consistent scale }; ``` ```typescript // Common layout patterns with spacing // Card layout const CardLayout = ({ children }) => ( {children} ); // Form layout const FormLayout = ({ children }) => ( {children} ); // Section layout const SectionLayout = ({ title, children }) => ( {title} {children} ); // Grid layout helper const createGrid = (columns: number, gap: number = theme.spacing.sm) => ({ flexDirection: 'row', flexWrap: 'wrap', marginHorizontal: -gap / 2, marginVertical: -gap / 2, }); ``` ## Radius Token Usage ```typescript // Border radius tokens export const radii = { none: 0, sm: 4, md: 8, lg: 12, xl: 16, full: 9999, // For circular elements }; // Usage in components const cardStyles = { borderRadius: theme.radii.lg, }; const avatarStyles = { borderRadius: theme.radii.full, }; const chipStyles = { borderRadius: theme.radii.full, }; ``` ## Shadow Token Usage ```typescript // core/shared/styles/theme/shadows.ts export const shadows = { // Elevation levels none: { shadowColor: 'transparent', shadowOffset: { width: 0, height: 0 }, shadowOpacity: 0, shadowRadius: 0, elevation: 0, }, sm: { shadowColor: '#000', shadowOffset: { width: 0, height: 1 }, shadowOpacity: 0.05, shadowRadius: 2, elevation: 2, }, md: { shadowColor: '#000', shadowOffset: { width: 0, height: 2 }, shadowOpacity: 0.1, shadowRadius: 4, elevation: 4, }, lg: { shadowColor: '#000', shadowOffset: { width: 0, height: 4 }, shadowOpacity: 0.15, shadowRadius: 8, elevation: 8, }, xl: { shadowColor: '#000', shadowOffset: { width: 0, height: 8 }, shadowOpacity: 0.2, shadowRadius: 16, elevation: 16, }, }; ``` ```typescript import { Platform } from 'react-native'; // Platform-aware shadow implementation const getShadow = (elevation: keyof typeof shadows) => { const shadow = shadows[elevation]; if (Platform.OS === 'android') { return { elevation: shadow.elevation, }; } return { shadowColor: shadow.shadowColor, shadowOffset: shadow.shadowOffset, shadowOpacity: shadow.shadowOpacity, shadowRadius: shadow.shadowRadius, }; }; // Component with shadows export const ElevatedCard = ({ elevation = 'md', children }) => ( {children} ); // Dynamic elevation export const InteractiveCard = ({ children }) => { const [pressed, setPressed] = useState(false); return ( setPressed(true)} onPressOut={() => setPressed(false)} style={[ styles.card, getShadow(pressed ? 'sm' : 'md'), ]} > {children} ); }; ``` ```typescript // Elevation system for depth hierarchy const elevationSystem = { // Ground level - no elevation ground: shadows.none, // Raised elements - slight elevation raised: shadows.sm, // Floating elements - medium elevation floating: shadows.md, // Modal/overlay - high elevation modal: shadows.lg, // Popover/tooltip - highest elevation popover: shadows.xl, }; // Usage examples const ComponentElevation = { Button: elevationSystem.raised, Card: elevationSystem.floating, Modal: elevationSystem.modal, Dropdown: elevationSystem.popover, BottomSheet: elevationSystem.modal, FAB: elevationSystem.floating, }; // Elevation context const ElevationContext = createContext('ground'); export const ElevationProvider = ({ level, children }) => ( {children} ); ``` ## Token Organization Best Practices ### File Structure ``` core/shared/styles/ ├── theme/ │ ├── index.ts # Main theme export │ ├── colors.ts # Color tokens │ ├── typography.ts # Typography tokens │ ├── spacing.ts # Spacing scale │ ├── shadows.ts # Shadow/elevation tokens │ ├── radii.ts # Border radius tokens │ └── motion.ts # Animation tokens ├── tokens.ts # Token type definitions └── utils.ts # Token utilities ``` ### Creating Custom Tokens ```typescript // Extend existing tokens const customColors = { ...theme.colors, brand: { primary: '#FF6B6B', secondary: '#4ECDC4', }, }; // Create semantic aliases const semanticColors = { interactive: { default: theme.colors.primary[500], hover: theme.colors.primary[600], pressed: theme.colors.primary[700], disabled: theme.colors.gray[300], }, }; // Component-specific tokens const buttonTokens = { sizes: { small: { padding: theme.spacing.sm, fontSize: theme.typography.sizes.sm, }, medium: { padding: theme.spacing.md, fontSize: theme.typography.sizes.md, }, large: { padding: theme.spacing.lg, fontSize: theme.typography.sizes.lg, }, }, }; ``` ## Token Validation ```typescript // Validate token usage in development const validateToken = (value: any, tokenType: string) => { if (__DEV__) { const validTokens = Object.values(theme[tokenType]); if (!validTokens.includes(value)) { console.warn( `Invalid ${tokenType} token: ${value}. Use one of:`, validTokens ); } } return value; }; // Type-safe token access const getToken = ( tokenType: T, tokenName: keyof typeof theme[T] ): typeof theme[T][keyof typeof theme[T]] => { return theme[tokenType][tokenName]; }; ``` ## Summary Design tokens are the foundation of a consistent design system. By using them properly: * **Maintain Consistency**: Every component uses the same visual language * **Enable Theming**: Switch themes by changing token values * **Improve Maintainability**: Update design decisions in one place * **Enhance Developer Experience**: Clear, predictable values * **Support Accessibility**: Built-in support for dynamic type and high contrast Remember: If you find yourself hardcoding a value, there should be a token for it! ## Next Steps * Review [Accessibility Patterns](/docs/ui-development/architecture/foundation-guide/accessibility-patterns) for inclusive design * Explore [Testing & Performance](/docs/ui-development/architecture/foundation-guide/testing-performance) with tokens * Learn about [AI Collaboration](/docs/ui-development/architecture/foundation-guide/ai-collaboration-docs) for token usage ## Related Documents * [Theme Management](/docs/ui-development/theme-management) - Complete theming guide * [Color System](/docs/ui-development/styling/colors) - Color theory and usage * [Typography System](/docs/ui-development/styling/typography) - Type scale and usage * [Spacing System](/docs/ui-development/styling/spacing) - Layout and spacing guide # UTA UI Development: Accessibility Patterns URL: https://dev.updatetheapp.com/docs/ui-development/architecture/foundation-guide/accessibility-patterns Documentation: https://dev.updatetheapp.com/docs/ui-development/architecture/foundation-guide/accessibility-patterns Comprehensive accessibility implementation including roles, states, focus management, and testing strategies for inclusive component design. *** title: Accessibility Patterns description: Comprehensive accessibility implementation including roles, states, focus management, and testing strategies for inclusive component design. --------------------------------------------------------------------------------------------------------------------------------------------------------- import { Tab, Tabs } from 'fumadocs-ui/components/tabs'; import { Callout } from 'fumadocs-ui/components/callout'; import { Steps, Step } from 'fumadocs-ui/components/steps'; # Accessibility Patterns ## Overview Accessibility ensures that all users, regardless of their abilities, can effectively use your application. React Native provides built-in accessibility features that, when properly implemented, create inclusive experiences for users with visual, motor, or cognitive disabilities. **Accessibility is Not Optional** Making your components accessible: * **Expands your user base** to include users with disabilities * **Improves usability** for all users in various contexts * **Ensures legal compliance** with accessibility regulations * **Demonstrates social responsibility** and inclusive design * **Often improves** overall app quality and structure ## Core Accessibility Concepts ### The Accessibility Tree React Native creates an accessibility tree parallel to the view hierarchy. Screen readers and other assistive technologies use this tree to understand and interact with your app. ```typescript // Regular view tree Hello // Accessibility tree representation Hello ``` ### Platform Differences **iOS VoiceOver Specifics** * Gestures: Swipe to navigate, double-tap to activate * Rotor: Two-finger rotation to change navigation mode * Magic Tap: Two-finger double-tap for primary action * Escape: Two-finger Z gesture to go back ```typescript // iOS-specific accessibility { if (event.actionName === 'magicTap') { quickSave(); } }} /> ``` **Android TalkBack Specifics** * Gestures: Swipe to navigate, double-tap to activate * Reading controls: Swipe up/down to change reading granularity * Local gestures: Draw L-shape for local menu * Global gestures: Draw specific shapes for global actions ```typescript // Android-specific accessibility ``` **Cross-Platform Best Practices** ```typescript // Works well on both platforms Save ``` ## Required Accessibility Features Every foundation component must implement these accessibility features: ### 1. Semantic Roles ```typescript // Button component // Text Input component // Image component Profile photo of John Smith // Link component ``` ```typescript // Header Settings // Navigation // List ( {item.content} )} /> // Progress indicator ``` ```typescript // Custom component roles export const CustomSwitch = ({ value, onValueChange }) => ( onValueChange(!value)} > {/* Custom switch implementation */} ); // Composite components export const SearchBar = ({ value, onChangeText }) => ( ); ``` ### 2. State Communication ```typescript // Button states // Input states // Expandable states // Multi-state components ``` ### 3. Dynamic Content ```typescript // Live region for dynamic updates {searchResultCount} results found // Assertive announcements for urgent updates Error: Invalid email address // None for decorative content ``` ```typescript import { AccessibilityInfo } from 'react-native'; // Announce messages to screen readers const announceMessage = (message: string) => { AccessibilityInfo.announceForAccessibility(message); }; // Example usage const handleSubmit = async () => { try { await submitForm(); announceMessage('Form submitted successfully'); } catch (error) { announceMessage(`Error: ${error.message}`); } }; // Delayed announcements const handleDelete = () => { deleteItem(); setTimeout(() => { announceMessage('Item deleted'); }, 300); }; ``` ```typescript // Loading states {isLoading && ( )} // Error states {error && ( {error} )} // Success states {success && ( Operation completed )} ``` ## Focus Management ### Focus Control #### Programmatic Focus ```typescript import { findNodeHandle, AccessibilityInfo } from 'react-native'; const MyComponent = () => { const inputRef = useRef(null); const focusInput = () => { const handle = findNodeHandle(inputRef.current); if (handle) { AccessibilityInfo.setAccessibilityFocus(handle); } }; return ( ); expect(getByRole('button')).toBeTruthy(); }); it('communicates disabled state', () => { const { getByRole } = render(); const button = getByRole('button'); expect(button.props.accessibilityState.disabled).toBe(true); }); it('communicates loading state', () => { const { getByRole } = render(); const button = getByRole('button'); expect(button.props.accessibilityState.busy).toBe(true); }); it('has accessible label', () => { const { getByLabelText } = render( ); expect(getByLabelText('Save changes')).toBeTruthy(); }); it('provides hint for complex actions', () => { const { getByRole } = render( ); const button = getByRole('button'); expect(button.props.accessibilityHint).toBe( 'Double tap to save all changes' ); }); }); ```
```typescript // Test accessibility in context describe('Form Accessibility', () => { it('announces validation errors', async () => { const announceForAccessibility = jest.spyOn( AccessibilityInfo, 'announceForAccessibility' ); const { getByLabelText, getByRole } = render(); // Submit with invalid data fireEvent.press(getByRole('button', { name: 'Login' })); await waitFor(() => { expect(announceForAccessibility).toHaveBeenCalledWith( 'Error: Please enter a valid email address' ); }); }); it('maintains focus after error', async () => { const setAccessibilityFocus = jest.spyOn( AccessibilityInfo, 'setAccessibilityFocus' ); const { getByLabelText } = render(); const emailInput = getByLabelText('Email'); // Trigger validation error fireEvent.changeText(emailInput, 'invalid'); fireEvent.blur(emailInput); expect(setAccessibilityFocus).toHaveBeenCalled(); }); }); ``` **Manual Testing Checklist** **Screen Reader Testing** * [ ] Enable VoiceOver (iOS) or TalkBack (Android) * [ ] Navigate using swipe gestures only * [ ] Verify all content is announced * [ ] Check announcement order is logical * [ ] Test all interactive elements * [ ] Verify state changes are announced **Keyboard Testing** (External keyboard) * [ ] Tab through all interactive elements * [ ] Verify focus indicators are visible * [ ] Test Enter/Space activation * [ ] Check Escape key handling * [ ] Verify no keyboard traps **Visual Testing** * [ ] Test with increased text size (200%) * [ ] Verify color contrast ratios * [ ] Check focus indicators contrast * [ ] Test with color filters * [ ] Verify no color-only information **Motion Testing** * [ ] Test with reduced motion enabled * [ ] Verify no essential information in animations * [ ] Check for motion alternatives
## Component-Specific Patterns ### Form Components ```typescript // Accessible form field export const FormField = ({ label, error, ...props }) => { const inputId = useId(); const errorId = useId(); return ( {label} {error && ( {error} )} ); }; ``` ### List Components ```typescript // Accessible list with actions export const AccessibleList = ({ items, onDelete }) => { return ( ( { if (event.actionName === 'delete') { onDelete(item.id); } }} > {item.name} )} /> ); }; ``` ### Modal Components ```typescript // Accessible modal export const AccessibleModal = ({ visible, onClose, children }) => { const previousFocusRef = useRef(null); useEffect(() => { if (visible) { // Store current focus previousFocusRef.current = findNodeHandle( AccessibilityInfo.currentlyFocusedField() ); } else if (previousFocusRef.current) { // Restore focus when closing AccessibilityInfo.setAccessibilityFocus(previousFocusRef.current); } }, [visible]); return ( Modal Title {children} ); }; ``` ## Best Practices Summary **Accessibility Excellence** Remember these key principles: * **Test with real assistive technologies** - Simulators aren't enough * **Provide context** - Labels should make sense out of context * **Be consistent** - Similar elements should behave similarly * **Avoid redundancy** - Don't repeat visual information * **Design for everyone** - Accessibility benefits all users **(Do ✅) Accessibility best practices** * Always provide `accessibilityLabel` for interactive elements * Use semantic `accessibilityRole` values * Communicate state changes with `accessibilityState` * Test with actual screen readers on devices * Provide hints for complex interactions * Group related content with `accessible={true}` * Use `accessibilityLiveRegion` for dynamic content * Maintain logical focus order * Provide text alternatives for images * Ensure sufficient color contrast (4.5:1 minimum) **(Don't ❌) Common accessibility mistakes** * Don't rely on placeholder text as labels * Don't use color as the only indicator * Don't create keyboard traps * Don't auto-focus without user action * Don't ignore platform differences * Don't skip accessibility testing * Don't make assumptions about user abilities * Don't use vague labels like "Click here" * Don't forget loading and error states * Don't break native accessibility features **Quick Accessibility Wins** 1. **Add labels to all buttons** ```tsx accessibilityLabel="Save document" ``` 2. **Group related content** ```tsx accessible={true} accessibilityLabel="User: John Doe, Admin" ``` 3. **Announce dynamic changes** ```tsx AccessibilityInfo.announceForAccessibility('Saved') ``` 4. **Hide decorative elements** ```tsx accessible={false} importantForAccessibility="no" ``` 5. **Use semantic HTML roles** ```tsx accessibilityRole="header" accessibilityRole="button" ``` ## Next Steps * Implement [Testing & Performance](/docs/ui-development/architecture/foundation-guide/testing-performance) strategies * Review [AI Collaboration](/docs/ui-development/architecture/foundation-guide/ai-collaboration-docs) for accessibility * Explore platform-specific [Accessibility APIs](https://reactnative.dev/docs/accessibility) ## Related Documents * [WCAG 2.1 Guidelines](https://www.w3.org/WAI/WCAG21/quickref/) * [React Native Accessibility Docs](https://reactnative.dev/docs/accessibility) * [iOS Accessibility Guidelines](https://developer.apple.com/accessibility/) * [Android Accessibility Guidelines](https://developer.android.com/guide/topics/ui/accessibility) # UTA UI Development: Testing & Performance URL: https://dev.updatetheapp.com/docs/ui-development/architecture/foundation-guide/testing-performance Documentation: https://dev.updatetheapp.com/docs/ui-development/architecture/foundation-guide/testing-performance Testing strategies, visual regression, performance optimization, and memoization patterns for reliable foundation components. *** title: Testing & Performance description: Testing strategies, visual regression, performance optimization, and memoization patterns for reliable foundation components. ------------------------------------------------------------------------------------------------------------------------------------------ import { Tab, Tabs } from 'fumadocs-ui/components/tabs'; import { Callout } from 'fumadocs-ui/components/callout'; import { Steps, Step } from 'fumadocs-ui/components/steps'; # Testing & Performance ## Overview Robust testing and performance optimization are critical for building reliable, fast foundation components. This guide covers comprehensive testing strategies and performance patterns that ensure your components meet quality standards and provide excellent user experiences. **Quality and Speed Matter** Well-tested and performant components: * **Reduce bugs** through comprehensive test coverage * **Improve user experience** with fast, responsive interfaces * **Enable confident refactoring** with regression protection * **Lower maintenance costs** through early issue detection * **Scale better** as your application grows ## Testing Strategies ### Component Testing Structure Foundation components require comprehensive testing to ensure reliability and accessibility. Here's a complete testing structure: ``` ui/foundation/Button/ ├── Button.tsx # Component implementation ├── Button.test.tsx # Unit tests ├── Button.visual.test.tsx # Visual regression tests ├── Button.perf.test.tsx # Performance tests └── __snapshots__/ # Snapshot files ``` **Test File Organization** ```typescript // Button.test.tsx structure describe('Button Component', () => { // Rendering tests describe('Rendering', () => { it('renders with default props', () => {}); it('renders all variants correctly', () => {}); }); // Interaction tests describe('Interactions', () => { it('handles press events', () => {}); it('prevents interaction when disabled', () => {}); }); // State tests describe('States', () => { it('shows loading state', () => {}); it('communicates disabled state', () => {}); }); // Accessibility tests describe('Accessibility', () => { it('has proper role and labels', () => {}); it('announces state changes', () => {}); }); }); ``` ```typescript // test-utils/setup.ts import '@testing-library/jest-native/extend-expect'; import { ReactTestInstance } from 'react-test-renderer'; // Mock native modules jest.mock('react-native/Libraries/Animated/NativeAnimatedHelper'); // Custom render with providers export const renderWithProviders = ( ui: React.ReactElement, options?: RenderOptions ) => { const Wrapper = ({ children }) => ( {children} ); return render(ui, { wrapper: Wrapper, ...options }); }; // Custom matchers expect.extend({ toHaveDesignToken(received: ReactTestInstance, token: string) { const hasToken = received.props.style?.includes(theme[token]); return { pass: hasToken, message: () => `Expected component to ${hasToken ? 'not ' : ''}use design token ${token}`, }; }, }); ``` **Coverage Targets by Component Type** | Component Type | Statement | Branch | Function | Line | | -------------- | --------- | ------ | -------- | ---- | | Foundation | 90%+ | 85%+ | 90%+ | 90%+ | | Pattern | 85%+ | 80%+ | 85%+ | 85%+ | | Business | 80%+ | 75%+ | 80%+ | 80%+ | **Critical Areas Requiring 100% Coverage** * Accessibility features * Error boundaries * State management logic * Event handlers * Conditional rendering ```json // jest.config.js coverage configuration { "coverageThreshold": { "global": { "branches": 80, "functions": 80, "lines": 80, "statements": 80 }, "ui/foundation/**": { "branches": 85, "functions": 90, "lines": 90, "statements": 90 } } } ``` ### Unit Testing Patterns ```typescript import { render } from '@testing-library/react-native'; import { Button } from './Button'; import { theme } from '@/core/shared/styles/theme'; describe('Button Rendering', () => { it('renders with required props', () => { const { getByText } = render( ); expect(getByText('Click me')).toBeTruthy(); }); it('applies correct variant styles', () => { const variants = ['primary', 'secondary', 'ghost', 'danger'] as const; variants.forEach(variant => { const { getByTestId, unmount } = render( ); const button = getByTestId(`button-${variant}`); expect(button.props.style).toMatchObject({ backgroundColor: theme.colors[variant][500], }); unmount(); }); }); it('renders with icons', () => { const Icon = () => Icon; const { getByText } = render( ); expect(getByText('Content')).toBeTruthy(); expect(getAllByText('Icon')).toHaveLength(2); }); it('applies size variations correctly', () => { const sizes = { small: theme.spacing.sm, medium: theme.spacing.md, large: theme.spacing.lg, }; Object.entries(sizes).forEach(([size, padding]) => { const { getByTestId } = render( ); expect(getByTestId(`button-${size}`).props.style).toMatchObject({ padding, }); }); }); }); ``` ```typescript import { render, fireEvent, waitFor } from '@testing-library/react-native'; import { Button } from './Button'; describe('Button Interactions', () => { it('calls onPress when pressed', () => { const onPress = jest.fn(); const { getByText } = render( ); fireEvent.press(getByText('Press me')); expect(onPress).toHaveBeenCalledTimes(1); }); it('prevents interaction when disabled', () => { const onPress = jest.fn(); const { getByText } = render( ); fireEvent.press(getByText('Disabled')); expect(onPress).not.toHaveBeenCalled(); }); it('prevents interaction when loading', () => { const onPress = jest.fn(); const { getByTestId } = render( ); fireEvent.press(getByTestId('loading-button')); expect(onPress).not.toHaveBeenCalled(); }); it('handles long press events', () => { const onLongPress = jest.fn(); const { getByText } = render( ); fireEvent(getByText('Long press me'), 'longPress'); expect(onLongPress).toHaveBeenCalledTimes(1); }); it('provides haptic feedback on press', () => { const hapticSpy = jest.spyOn(Haptics, 'impact'); const { getByText } = render( ); fireEvent.press(getByText('Press me')); expect(hapticSpy).toHaveBeenCalledWith( Haptics.ImpactFeedbackStyle.Light ); }); }); ``` ```typescript import { render, act } from '@testing-library/react-native'; import { Button } from './Button'; describe('Button States', () => { it('shows loading spinner when loading', () => { const { getByTestId, queryByTestId, rerender } = render( ); expect(queryByTestId('button-spinner')).toBeNull(); rerender(); expect(getByTestId('button-spinner')).toBeTruthy(); }); it('updates opacity when disabled', () => { const { getByTestId, rerender } = render( ); expect(getByTestId('button').props.style.opacity).toBe(1); rerender( ); expect(getByTestId('button').props.style.opacity).toBe(0.6); }); it('maintains size during loading state', async () => { const { getByTestId, rerender } = render( ); const initialHeight = getByTestId('button').props.style.height; rerender( ); await waitFor(() => { expect(getByTestId('button').props.style.height).toBe(initialHeight); }); }); }); ``` ### Accessibility Testing ```typescript describe('Button Accessibility', () => { it('has correct accessibility role', () => { const { getByRole } = render(); expect(getByRole('button')).toBeTruthy(); }); it('communicates disabled state', () => { const { getByRole } = render(); const button = getByRole('button'); expect(button.props.accessibilityState.disabled).toBe(true); }); it('communicates loading state', () => { const { getByRole } = render(); const button = getByRole('button'); expect(button.props.accessibilityState.busy).toBe(true); }); it('has accessible label', () => { const { getByLabelText } = render( ); expect(getByLabelText('Save changes')).toBeTruthy(); }); it('announces press action', () => { const announceForAccessibility = jest.spyOn( AccessibilityInfo, 'announceForAccessibility' ); const { getByRole } = render( ); fireEvent.press(getByRole('button')); expect(announceForAccessibility).toHaveBeenCalledWith('Saved!'); }); }); ``` ### Visual Regression Testing ```typescript // Button.visual.test.tsx import React from 'react'; import renderer from 'react-test-renderer'; import { Button } from './Button'; describe('Button Visual Tests', () => { it('matches snapshot for all variants', () => { const variants = ['primary', 'secondary', 'ghost', 'danger'] as const; variants.forEach(variant => { const tree = renderer.create( ).toJSON(); expect(tree).toMatchSnapshot(`button-${variant}`); }); }); it('matches snapshot for all sizes', () => { const sizes = ['small', 'medium', 'large'] as const; sizes.forEach(size => { const tree = renderer.create( ).toJSON(); expect(tree).toMatchSnapshot(`button-${size}`); }); }); it('matches snapshot for different states', () => { const states = [ { loading: true }, { disabled: true }, { fullWidth: true }, ]; states.forEach((state, index) => { const tree = renderer.create( ).toJSON(); expect(tree).toMatchSnapshot(`button-state-${index}`); }); }); }); ``` ```typescript // Using Storybook for visual testing import type { Meta, StoryObj } from '@storybook/react-native'; import { Button } from './Button'; const meta: Meta = { title: 'Foundation/Button', component: Button, argTypes: { variant: { control: { type: 'select' }, options: ['primary', 'secondary', 'ghost', 'danger'], }, size: { control: { type: 'select' }, options: ['small', 'medium', 'large'], }, }, }; export default meta; type Story = StoryObj; export const Default: Story = { args: { children: 'Button', }, }; export const AllVariants: Story = { render: () => ( ), }; export const States: Story = { render: () => ( ), }; ``` ```typescript import { Platform } from 'react-native'; describe('Platform-specific visual tests', () => { it('renders iOS-specific styles', () => { Platform.OS = 'ios'; const tree = renderer.create( ).toJSON(); expect(tree).toMatchSnapshot('button-ios'); }); it('renders Android-specific styles', () => { Platform.OS = 'android'; const tree = renderer.create( ).toJSON(); expect(tree).toMatchSnapshot('button-android'); }); it('handles platform-specific shadows', () => { const platforms = ['ios', 'android'] as const; platforms.forEach(platform => { Platform.OS = platform; const { getByTestId } = render( ); const button = getByTestId('elevated-button'); if (platform === 'ios') { expect(button.props.style).toHaveProperty('shadowColor'); expect(button.props.style).toHaveProperty('shadowOffset'); } else { expect(button.props.style).toHaveProperty('elevation'); } }); }); }); ``` ## Performance Optimization ### Memoization Patterns ```typescript import React from 'react'; // Basic memoization export const Button = React.memo(({ variant = 'primary', size = 'medium', loading = false, disabled = false, children, onPress, ...props }) => { // Component implementation }); // Custom comparison function export const Button = React.memo( (props) => { // Component implementation }, (prevProps, nextProps) => { // Return true if props are equal (skip re-render) return ( prevProps.variant === nextProps.variant && prevProps.size === nextProps.size && prevProps.loading === nextProps.loading && prevProps.disabled === nextProps.disabled && prevProps.children === nextProps.children // Note: Exclude onPress as it might be recreated ); } ); // Memoizing compound components const ButtonIcon = React.memo(({ icon, position }) => ( {icon} )); const ButtonContent = React.memo(({ children, loading }) => { if (loading) return ; return {children}; }); ``` ```typescript // Memoize expensive style calculations const useButtonStyles = (variant: string, size: string, disabled: boolean) => { return useMemo(() => { const baseStyles = { paddingHorizontal: theme.spacing[size], paddingVertical: theme.spacing[size] / 2, borderRadius: theme.radii.md, flexDirection: 'row', alignItems: 'center', justifyContent: 'center', }; const variantStyles = { backgroundColor: theme.colors[variant][500], borderColor: theme.colors[variant][600], }; const stateStyles = disabled ? { opacity: 0.6, backgroundColor: theme.colors.gray[300], } : {}; return StyleSheet.create({ container: { ...baseStyles, ...variantStyles, ...stateStyles, }, }); }, [variant, size, disabled]); }; // Memoize complex computations const useIconSize = (buttonSize: string) => { return useMemo(() => { switch (buttonSize) { case 'small': return 16; case 'large': return 24; default: return 20; } }, [buttonSize]); }; // Memoize transformed data const useFormattedLabel = (label: string, loading: boolean) => { return useMemo(() => { if (loading) return 'Loading...'; return label.trim().toUpperCase(); }, [label, loading]); }; ``` ```typescript // Memoize event handlers const Button = ({ onPress, onLongPress, id, ...props }) => { const handlePress = useCallback(() => { Haptics.impact(Haptics.ImpactFeedbackStyle.Light); onPress?.(id); }, [onPress, id]); const handleLongPress = useCallback(() => { Haptics.impact(Haptics.ImpactFeedbackStyle.Medium); onLongPress?.(id); }, [onLongPress, id]); return ( ); }; // Memoize animation callbacks const AnimatedButton = ({ onPress }) => { const scale = useSharedValue(1); const handlePressIn = useCallback(() => { 'worklet'; scale.value = withSpring(0.95); }, []); const handlePressOut = useCallback(() => { 'worklet'; scale.value = withSpring(1); runOnJS(onPress)(); }, [onPress]); const animatedStyle = useAnimatedStyle(() => ({ transform: [{ scale: scale.value }], })); return ( ); }; ``` ### StyleSheet Optimization ```typescript // Static styles - created once const staticStyles = StyleSheet.create({ container: { flexDirection: 'row', alignItems: 'center', justifyContent: 'center', }, text: { textAlign: 'center', }, icon: { marginHorizontal: theme.spacing.xs, }, }); // Dynamic styles - use factory pattern const createButtonStyles = memoize((variant: string, size: string) => StyleSheet.create({ button: { backgroundColor: theme.colors[variant][500], padding: theme.spacing[size], borderRadius: theme.radii[size], }, }) ); // Flatten styles for performance const Button = ({ variant, size, style, ...props }) => { const dynamicStyles = createButtonStyles(variant, size); // Flatten only what's needed const flattenedStyle = useMemo( () => StyleSheet.flatten([ staticStyles.container, dynamicStyles.button, style, ]), [dynamicStyles, style] ); return ; }; ``` ### List Performance #### Use FlatList Optimizations ```typescript const OptimizedList = ({ data }) => { const renderItem = useCallback(({ item }) => ( ), []); const keyExtractor = useCallback((item) => item.id, []); const getItemLayout = useCallback((data, index) => ({ length: ITEM_HEIGHT, offset: ITEM_HEIGHT * index, index, }), []); return ( ); }; ``` #### Implement Virtualization ```typescript import { VirtualizedList } from 'react-native'; const VirtualizedComponent = ({ items }) => { const getItem = useCallback((data, index) => data[index], []); const getItemCount = useCallback((data) => data.length, []); return ( ); }; ``` #### Use FlashList for Better Performance ```typescript import { FlashList } from '@shopify/flash-list'; const PerformantList = ({ data }) => { return ( { if (item.type === 'header') { layout.size = 60; layout.span = 2; } }} /> ); }; ``` ### Animation Performance ```typescript // Use native driver for better performance const fadeIn = useCallback(() => { Animated.timing(opacity, { toValue: 1, duration: 300, useNativeDriver: true, // Critical for performance }).start(); }, [opacity]); // Use Reanimated for complex animations const AnimatedComponent = () => { const translateY = useSharedValue(0); const animatedStyle = useAnimatedStyle(() => ({ transform: [{ translateY: translateY.value }], })); const startAnimation = useCallback(() => { 'worklet'; translateY.value = withSpring(100, { damping: 15, stiffness: 100, }); }, []); return ( {/* Content */} ); }; // Batch animations const batchedAnimation = useCallback(() => { Animated.parallel([ Animated.timing(opacity, { toValue: 1, useNativeDriver: true }), Animated.timing(scale, { toValue: 1, useNativeDriver: true }), ]).start(); }, [opacity, scale]); ``` ## Performance Testing ```typescript // Button.perf.test.tsx import { measurePerformance } from '@testing-library/react-native-performance'; describe('Button Performance', () => { it('renders within performance budget', async () => { const { renderTime } = await measurePerformance( ); expect(renderTime).toBeLessThan(16); // 60fps threshold }); it('re-renders efficiently', async () => { const scenario = async (screen) => { const button = screen.getByRole('button'); await screen.rerender( ); await screen.rerender( ); }; const { renderCount, renderTime } = await measurePerformance( , { scenario } ); expect(renderCount).toBe(3); // Initial + 2 re-renders expect(renderTime.median).toBeLessThan(8); }); it('handles rapid interactions efficiently', async () => { const onPress = jest.fn(); const scenario = async (screen) => { const button = screen.getByRole('button'); // Simulate rapid taps for (let i = 0; i < 10; i++) { fireEvent.press(button); await wait(50); } }; const { renderTime } = await measurePerformance( , { scenario } ); expect(renderTime.max).toBeLessThan(16); }); }); ``` ```typescript // Memory leak testing describe('Memory Management', () => { it('cleans up resources on unmount', async () => { const { unmount } = render( ); const initialMemory = await getMemoryUsage(); // Trigger animations and interactions fireEvent.press(screen.getByRole('button')); await wait(500); unmount(); // Force garbage collection if (global.gc) global.gc(); await wait(100); const finalMemory = await getMemoryUsage(); expect(finalMemory).toBeLessThanOrEqual(initialMemory * 1.1); }); it('handles large lists without memory leaks', async () => { const largeData = Array.from({ length: 1000 }, (_, i) => ({ id: i, title: `Item ${i}`, })); const { unmount } = render( } keyExtractor={(item) => item.id.toString()} /> ); const memoryBefore = await getMemoryUsage(); // Scroll through list await scrollToEnd(); await scrollToTop(); unmount(); if (global.gc) global.gc(); const memoryAfter = await getMemoryUsage(); expect(memoryAfter).toBeLessThan(memoryBefore * 1.2); }); }); ``` ```typescript // Bundle size analysis import { analyzeBundle } from 'react-native-bundle-analyzer'; describe('Bundle Size', () => { it('keeps component bundle size small', async () => { const analysis = await analyzeBundle({ entry: './ui/foundation/Button/index.ts', }); expect(analysis.size).toBeLessThan(5 * 1024); // 5KB max expect(analysis.gzipSize).toBeLessThan(2 * 1024); // 2KB gzipped }); it('tree shakes unused code', async () => { const fullBundle = await analyzeBundle({ entry: './ui/index.ts', }); const usedBundle = await analyzeBundle({ entry: './test-app.ts', // imports only Button }); // Should only include Button code, not all components expect(usedBundle.size).toBeLessThan(fullBundle.size * 0.1); }); it('identifies heavy dependencies', async () => { const analysis = await analyzeBundle({ entry: './ui/foundation/Button/index.ts', detailed: true, }); // Check for unexpected large dependencies analysis.modules.forEach(module => { if (module.size > 1024) { // 1KB console.warn(`Large dependency: ${module.name} (${module.size}B)`); } }); expect(analysis.modules.every(m => m.size < 2048)).toBe(true); }); }); ``` ## Testing Best Practices **Testing Excellence** Great tests are: * **Fast**: Run quickly to encourage frequent execution * **Reliable**: No flaky tests that fail intermittently * **Maintainable**: Easy to understand and update * **Comprehensive**: Cover all critical paths * **Isolated**: Test one thing at a time **(Do ✅) Testing best practices** * Write tests before or alongside implementation * Test behavior, not implementation details * Use descriptive test names that explain the scenario * Mock external dependencies consistently * Test edge cases and error conditions * Keep tests DRY with shared utilities * Run tests in CI/CD pipeline * Monitor and maintain test coverage * Test accessibility features * Profile performance regularly **(Don't ❌) Testing anti-patterns** * Don't test React Native internals * Don't write brittle tests tied to implementation * Don't ignore flaky tests - fix them * Don't skip tests for "simple" components * Don't mock everything - test integration * Don't forget cleanup in tests * Don't test styling details in unit tests * Don't write tests after bugs are found * Don't ignore performance budgets * Don't test private methods directly **Testing Tips & Tricks** 1. **Use data-testid sparingly** ```tsx // Only when semantic queries don't work ``` 2. **Create test utilities** ```tsx const renderButton = (props = {}) => render(); expect(getByText('Press me')).toBeTruthy(); }); it('calls onPress when pressed', () => { const onPressMock = jest.fn(); const { getByText } = render( ); fireEvent.press(getByText('Press me')); expect(onPressMock).toHaveBeenCalledTimes(1); }); it('does not call onPress when disabled', () => { const onPressMock = jest.fn(); const { getByText } = render( ); fireEvent.press(getByText('Press me')); expect(onPressMock).not.toHaveBeenCalled(); }); }); ``` ## Step 5: Implement Component Component implementation is where planning becomes reality. Following consistent patterns makes components predictable for other developers to use and extend. Implementation should focus on accessibility, performance, and maintainability from the start rather than as an afterthought. ### Component Implementation Principles When implementing components, follow these key principles: **(Do ✅) Core Implementation Practices** * **Design Token Usage**: Always use design tokens from our central theme for colors, spacing, typography, etc. * **Prop Defaults**: Provide sensible defaults for optional props * **Component Composition**: Build components that compose well with others * **Accessibility First**: Implement accessibility features from the beginning * **Performance Awareness**: Be mindful of re-renders and optimization opportunities * **Test Coverage**: Write tests for all key functionality and edge cases **(Don't ❌) Implementation Anti-patterns** * **Hardcoded Values**: Avoid hardcoded colors, sizes, or spacing values * **Overly Complex Components**: Keep components focused on a single responsibility * **Direct DOM Manipulation**: Use React's declarative approach instead * **Poor Type Safety**: Ensure props have proper TypeScript types * **Missing Accessibility**: Don't skip accessibility attributes **Implementation Flow** 1. Set up component props and default values 2. Implement core rendering logic 3. Apply styling using design tokens 4. Add accessibility features 5. Implement interactions and state management 6. Optimize for performance 7. Add comprehensive tests Here are examples of recommended code patterns for different component types: **Foundation Component Pattern** ```typescript // ui/foundation/Button/Button.tsx import React from 'react'; import { TouchableOpacity, Text, View } from 'react-native'; import { ButtonProps } from './types'; import { getStyles } from './styles'; export const Button = React.forwardRef(( { children, variant = 'primary', size = 'medium', disabled = false, loading = false, icon, onPress, accessibilityLabel, testID, ...rest }, ref ) => { const styles = getStyles({ variant, size, disabled }); return ( {loading && } {!loading && icon && {icon}} {typeof children === 'string' ? ( {children} ) : ( children )} ); }); Button.displayName = 'Button'; ``` **Pattern Component Example** ```typescript // ui/patterns/Form/Form.tsx import React from 'react'; import { View } from 'react-native'; import { FormProps, FormContextValue } from './types'; import { FormContext } from './context'; import { getStyles } from './styles'; export const Form = ({ children, onSubmit, initialValues = {}, validate, ...rest }: FormProps) => { const [values, setValues] = React.useState(initialValues); const [errors, setErrors] = React.useState({}); const styles = getStyles(); // Form implementation with context for state management const contextValue: FormContextValue = { values, errors, setFieldValue: (name, value) => { setValues(prev => ({ ...prev, [name]: value })); }, // Additional context methods would go here }; return ( {children} ); }; ``` These are common implementation issues to avoid: **1. Style Inconsistency** ```typescript // ❌ Bad: Hardcoded styles and values {children} // ✅ Good: Using design tokens {children} ``` **2. Missing Accessibility** ```typescript // ❌ Bad: No accessibility support {children} // ✅ Good: Proper accessibility support {children} ``` **3. Prop Handling** ```typescript // ❌ Bad: Poor props handling function Button(props) { return ( {props.children} ); } // ✅ Good: Proper props destructuring with defaults function Button({ children, onPress, variant = 'primary', disabled = false, ...rest }) { // Implementation } ``` ## Step 6: Apply Design Tokens Design tokens are the visual building blocks of our UI system. Using tokens consistently creates a cohesive user experience and makes it easier to update the design system globally. Design tokens are centralized design variables that represent the visual properties of our UI. They ensure consistency across the application and enable global style updates with minimal code changes. **Key Token Categories** * **Colors**: Brand colors, UI states, text colors, backgrounds * **Typography**: Font families, sizes, weights, line heights * **Spacing**: Margins, padding, gaps * **Sizing**: Component dimensions, icon sizes * **Borders**: Border widths, radii * **Shadows**: Elevation levels, drop shadows * **Animation**: Durations, easing functions **Token Structure** Our tokens are organized hierarchically, from primitive values to semantic tokens: 1. **Base Tokens**: Raw values (hex colors, pixel values) 2. **Semantic Tokens**: Purpose-based tokens (primary, danger, success) 3. **Component Tokens**: Component-specific tokens **Token Import Path** ```typescript // Import all token categories import { theme } from '@/core/shared/styles/theme'; // Or import specific token categories import { colors } from '@/core/shared/styles/colors'; import { spacing } from '@/core/shared/styles/spacing'; import { typography } from '@/core/shared/styles/typography'; ``` **Example: Button Component with Design Tokens** ```typescript // ui/foundation/Button/styles.ts import { StyleSheet } from 'react-native'; import { theme } from '@/core/shared/styles/theme'; export const getStyles = ({ variant, size, disabled }) => { return StyleSheet.create({ container: { backgroundColor: disabled ? theme.colors.disabled.background : theme.colors[variant].background, borderRadius: theme.radii.md, paddingHorizontal: theme.spacing[size], paddingVertical: theme.spacing.xs, flexDirection: 'row', alignItems: 'center', justifyContent: 'center', opacity: disabled ? 0.7 : 1, }, text: { color: disabled ? theme.colors.disabled.text : theme.colors[variant].text, fontSize: theme.typography.size[size], fontFamily: theme.typography.families.medium, textAlign: 'center', }, iconContainer: { marginRight: theme.spacing.xs, }, spinner: { color: theme.colors[variant].text, }, }); }; ``` **Example: Card Component with Design Tokens** ```typescript // ui/foundation/Card/styles.ts import { StyleSheet } from 'react-native'; import { theme } from '@/core/shared/styles/theme'; export const getStyles = ({ variant }) => { return StyleSheet.create({ container: { backgroundColor: theme.colors.surface, borderRadius: theme.radii.lg, padding: theme.spacing.md, ...theme.shadows[variant], }, title: { fontFamily: theme.typography.families.bold, fontSize: theme.typography.size.lg, color: theme.colors.text.primary, marginBottom: theme.spacing.sm, }, content: { fontFamily: theme.typography.families.regular, fontSize: theme.typography.size.md, color: theme.colors.text.secondary, }, }); }; ``` **(Do ✅) Design Token Best Practices** * **Use Semantic Tokens**: Prefer `theme.colors.primary` over raw values like `#0066CC` * **Extract Common Styles**: Create style functions for reused patterns * **Dynamic Token Selection**: Use props to determine which tokens to apply * **Consistent Usage Patterns**: Use the same token patterns across similar components * **Platform Adaptations**: Use platform-specific tokens when necessary **(Don't ❌) Common Token Mistakes** * **Hardcoded Values**: Never use raw color codes, pixel values, or font names * **Bypassing the System**: Don't create one-off values outside the token system * **Mixing Token Sources**: Don't mix design tokens with hardcoded values * **Inconsistent Token Usage**: Don't use different token patterns for similar components * **Inline Styles**: Avoid using tokens directly in inline styles; use StyleSheet instead **Performance Considerations** * Extract style generators outside component functions to prevent recreation on each render * Memoize complex style calculations for components that render frequently * Consider using `StyleSheet.create()` for style optimization ### AI Collaboration: Design Token Implementation ```markdown # Design Token Implementation Please help me implement design token-based styling for a [ComponentName] component: ## Component Information - Component: [ComponentName] - Category: [foundation/pattern/business] - Visual States: [list states: default, hover, active, disabled, etc.] ## Design Token Requirements - Use our theme tokens from @/core/shared/styles/theme - Create a separate styles.ts file with a getStyles function - Support the following variants: [list variants] - Support the following sizes: [list sizes] - Handle states properly (disabled, focused, etc.) ## Implementation Needs - Generate the styles.ts file with proper TypeScript types - Show how to apply these styles in the component - Demonstrate dynamic style selection based on props - Add responsive considerations if applicable ``` ## Step 7: Implement Accessibility Accessibility is a core requirement for all our components, not an optional feature. Implementing accessibility features from the start ensures our components are usable by everyone, including people with disabilities. All components must meet these accessibility requirements: 1. **Screen Reader Support** * Appropriate accessibility roles * Meaningful accessibility labels * Proper state announcements 2. **Keyboard Navigation** * Focusable interactive elements * Visible focus indicators * Logical tab order 3. **Touch Targets** * Minimum size of 44×44 points for touch targets * Adequate spacing between interactive elements 4. **Visual Design** * Color contrast ratio of at least 4.5:1 for text * Not relying on color alone to convey information * Support for text scaling and zooming 5. **Motion and Animation** * Respecting reduced motion preferences * No flashing content that could trigger seizures **Example: Accessible Button Component** ```typescript import React from 'react'; import { TouchableOpacity, Text, AccessibilityState } from 'react-native'; import { ButtonProps } from './types'; export const Button = ({ children, onPress, disabled = false, loading = false, accessibilityLabel, accessibilityHint, ...rest }: ButtonProps) => { // Determine accessibility state const accessibilityState: AccessibilityState = { disabled: disabled || loading, busy: loading }; // Determine appropriate label const label = accessibilityLabel || (typeof children === 'string' ? children : undefined); // Add loading state to label if needed const fullAccessibilityLabel = loading ? `${label}, loading` : label; return ( {children} ); }; ``` **Example: Accessible Form Field** ```typescript import React from 'react'; import { View, TextInput, Text } from 'react-native'; import { InputProps } from './types'; export const Input = ({ label, value, onChangeText, error, required = false, ...rest }: InputProps) => { const inputId = React.useId(); return ( {label}{required && ' *'} {error && ( {error} )} ); }; ``` **Manual Testing Process** 1. **Screen Reader Testing** * Enable VoiceOver (iOS) or TalkBack (Android) * Navigate through the component * Verify all relevant information is announced * Test all interactive states (default, focused, disabled, loading) 2. **Keyboard Navigation Testing** * Connect a keyboard to your device * Verify the component can be focused using Tab * Verify the component can be activated using Enter/Space * Check that focus order is logical 3. **Touch Target Testing** * Verify interactive areas are large enough (44×44pts minimum) * Check spacing between interactive elements * Test edge cases like small screens **Automated Testing Approach** ```typescript import { render } from '@testing-library/react-native'; import { Button } from './Button'; describe('Button accessibility', () => { it('has correct accessibility role', () => { const { getByRole } = render(); const button = getByRole('button'); expect(button).toBeTruthy(); }); it('has correct accessibility label', () => { const { getByA11yLabel } = render(); expect(getByA11yLabel('Press me')).toBeTruthy(); }); it('communicates disabled state', () => { const { getByA11yState } = render( ); expect(getByA11yState({ disabled: true })).toBeTruthy(); }); it('communicates loading state', () => { const { getByA11yState } = render( ); expect(getByA11yState({ busy: true })).toBeTruthy(); }); }); ``` ### AI Collaboration: Accessibility Implementation ```markdown # Accessibility Implementation Please help me implement accessibility features for a [ComponentName] component: ## Component Information - Component: [ComponentName] - Category: [foundation/pattern/business] - Purpose: [brief description of component purpose] - Interactive Elements: [describe interactive parts] ## Accessibility Requirements - Screen reader support needs - Keyboard navigation requirements - Touch target considerations - State communication needs (loading, error, disabled, etc.) ## Implementation Needs - Add appropriate accessibilityRole - Implement accessibilityLabel and accessibilityHint - Configure accessibilityState for different component states - Add proper focus management if applicable - Ensure appropriate touch target sizes Please provide a complete implementation showing all necessary accessibility attributes and any special handling required. ``` ## Development Phase Summary The development phase transforms your planning into working code. By following this structured approach, you ensure: * Consistent component structure across the codebase * Proper implementation with design tokens and accessibility * Components that are maintainable and extensible * Code that follows established patterns and best practices ## Next Steps Once you've completed the development phase: 1. **Test Your Implementation**: Write comprehensive tests for your component 2. **Review Your Code**: Use the self-review checklist before requesting team review 3. **Document Your Component**: Create clear documentation for other developers 4. **Move to Testing Phase**: Proceed to the [Testing Strategies](/docs/ui-development/architecture/development-workflow/testing-strategies) phase ## Related Documents * [Component Patterns](/docs/ui-development/development/component-patterns) - Common patterns and best practices * [Foundation Components Guide](/docs/ui-development/architecture/foundation-guide) - Building atomic UI components * [Testing Patterns](/docs/ui-development/development/testing-patterns) - Comprehensive testing strategies * [AI Collaboration Guide](/docs/ui-development/architecture/ai-collaboration) - Working effectively with AI # UTA UI Development: Testing Strategies URL: https://dev.updatetheapp.com/docs/ui-development/architecture/development-workflow/testing-strategies Documentation: https://dev.updatetheapp.com/docs/ui-development/architecture/development-workflow/testing-strategies Write unit tests, visual tests, accessibility tests, and integration tests for your components. *** title: Testing Strategies description: Write unit tests, visual tests, accessibility tests, and integration tests for your components. ------------------------------------------------------------------------------------------------------------ import { Tab, Tabs } from 'fumadocs-ui/components/tabs'; import { Callout } from 'fumadocs-ui/components/callout'; import { Steps, Step } from 'fumadocs-ui/components/steps'; # Testing Strategies ## Overview Testing is an essential part of the component development process. A well-tested component is more reliable, easier to maintain, and less likely to cause issues when used in different contexts. This guide covers comprehensive testing strategies for React Native components. **Why Testing Matters** Testing is an essential part of the component development process. A well-tested component is more reliable, easier to maintain, and less likely to cause issues when used in different contexts. ## Step 8: Implement Component Tests Our testing approach ensures component quality through multiple levels of verification, from unit tests to accessibility compliance. Our testing approach includes multiple levels to ensure component quality: 1. **Unit Testing** * Test individual functions and methods * Verify prop handling and default values * Test component state management 2. **Component Testing** * Test component rendering * Verify component interactions * Test different prop combinations 3. **Integration Testing** * Test interaction with other components * Verify context and provider integration * Test real-world usage scenarios 4. **Accessibility Testing** * Verify screen reader compatibility * Test keyboard navigation * Check WCAG compliance 5. **Visual Testing** * Snapshot testing for UI regressions * Platform-specific rendering verification * Theme and style verification **Unit Test Example** ```typescript // ui/foundation/Button/Button.test.tsx import React from 'react'; import { render, fireEvent } from '@testing-library/react-native'; import { Button } from './Button'; describe('Button', () => { it('renders correctly with default props', () => { const { getByText } = render(); expect(getByText('Press Me')).toBeTruthy(); }); it('calls onPress when pressed', () => { const onPressMock = jest.fn(); const { getByText } = render( ); fireEvent.press(getByText('Press Me')); expect(onPressMock).toHaveBeenCalledTimes(1); }); it('applies correct styles based on variant', () => { const { getByTestId } = render( ); const button = getByTestId('test-button'); // Check style properties based on theme tokens expect(button.props.style).toMatchObject({ backgroundColor: expect.any(String), // theme.colors.secondary }); }); it('does not call onPress when disabled', () => { const onPressMock = jest.fn(); const { getByText } = render( ); fireEvent.press(getByText('Press Me')); expect(onPressMock).not.toHaveBeenCalled(); }); }); ``` **Snapshot Test Example** ```typescript // ui/foundation/Card/Card.test.tsx import React from 'react'; import { render } from '@testing-library/react-native'; import { Card } from './Card'; describe('Card', () => { it('matches snapshot with default props', () => { const { toJSON } = render( Card Content ); expect(toJSON()).toMatchSnapshot(); }); it('matches snapshot with custom props', () => { const { toJSON } = render( Card Content ); expect(toJSON()).toMatchSnapshot(); }); }); ``` **(Do ✅) Testing Best Practices** * **Test Component API**: Verify all props work as expected * **Test User Interactions**: Test all interactive elements * **Test Edge Cases**: Test empty states, loading states, error states * **Test Accessibility**: Verify screen reader support and keyboard navigation * **Keep Tests Focused**: Each test should verify a single aspect of the component * **Use Descriptive Test Names**: Make it clear what each test is verifying * **Mock External Dependencies**: Isolate component tests from external services **(Don't ❌) Testing Anti-patterns** * **Testing Implementation Details**: Focus on behavior, not implementation * **Brittle Tests**: Avoid tests that break with minor changes * **Overlapping Tests**: Don't test the same functionality multiple times * **Missing Key Scenarios**: Don't ignore important user flows * **Testing Library Internals**: Don't test third-party libraries **Test Coverage Goals** * **Foundation Components**: 90-100% coverage * **Pattern Components**: 80-100% coverage * **Business Components**: 70-90% coverage * **Core Functionality**: 100% coverage * **Edge Cases**: 90-100% coverage ## Types of Component Tests ### 1. Rendering Tests Verify that components render correctly with different prop combinations: ```typescript // ui/foundation/Badge/Badge.test.tsx import React from 'react'; import { render } from '@testing-library/react-native'; import { Badge } from './Badge'; describe('Badge Rendering', () => { it('renders with default props', () => { const { getByText } = render(New); expect(getByText('New')).toBeTruthy(); }); it('renders all variants correctly', () => { const variants = ['primary', 'success', 'warning', 'error'] as const; variants.forEach(variant => { const { getByTestId } = render( Test ); const badge = getByTestId(`badge-${variant}`); expect(badge).toBeTruthy(); // Verify variant-specific styles are applied }); }); it('renders with custom props', () => { const { getByTestId } = render( Custom ); const badge = getByTestId('custom-badge'); expect(badge).toBeTruthy(); }); }); ``` ### 2. Interaction Tests Test user interactions and event handling: ```typescript // ui/patterns/Accordion/Accordion.test.tsx import React from 'react'; import { render, fireEvent, waitFor } from '@testing-library/react-native'; import { Accordion } from './Accordion'; describe('Accordion Interactions', () => { const mockItems = [ { title: 'Section 1', content: 'Content 1' }, { title: 'Section 2', content: 'Content 2' }, ]; it('expands and collapses sections on press', async () => { const { getByText, queryByText } = render( ); // Initially collapsed expect(queryByText('Content 1')).toBeNull(); // Expand section fireEvent.press(getByText('Section 1')); await waitFor(() => { expect(getByText('Content 1')).toBeTruthy(); }); // Collapse section fireEvent.press(getByText('Section 1')); await waitFor(() => { expect(queryByText('Content 1')).toBeNull(); }); }); it('handles multiple sections correctly', () => { const { getByText } = render( ); // Expand both sections fireEvent.press(getByText('Section 1')); fireEvent.press(getByText('Section 2')); // Both should be visible expect(getByText('Content 1')).toBeTruthy(); expect(getByText('Content 2')).toBeTruthy(); }); }); ``` ### 3. State Management Tests Test component state and lifecycle: ```typescript // ui/patterns/Form/Form.test.tsx import React from 'react'; import { render, fireEvent, act } from '@testing-library/react-native'; import { Form, FormField } from './Form'; describe('Form State Management', () => { it('manages form values correctly', () => { const onSubmit = jest.fn(); const { getByPlaceholderText, getByText } = render(
); // Update form fields fireEvent.changeText( getByPlaceholderText('Email'), 'test@example.com' ); fireEvent.changeText( getByPlaceholderText('Password'), 'password123' ); // Submit form fireEvent.press(getByText('Submit')); expect(onSubmit).toHaveBeenCalledWith({ email: 'test@example.com', password: 'password123' }); }); it('validates form fields', async () => { const validate = (values: any) => { const errors: any = {}; if (!values.email) errors.email = 'Email is required'; return errors; }; const { getByText, getByTestId } = render(
); // Try to submit without filling required field fireEvent.press(getByText('Submit')); await waitFor(() => { expect(getByText('Email is required')).toBeTruthy(); }); }); }); ``` ### 4. Accessibility Tests Ensure components are accessible to all users: ```typescript // ui/foundation/Button/Button.accessibility.test.tsx import React from 'react'; import { render } from '@testing-library/react-native'; import { Button } from './Button'; describe('Button Accessibility', () => { it('has correct accessibility role', () => { const { getByRole } = render(); const button = getByRole('button'); expect(button).toBeTruthy(); }); it('has correct accessibility label', () => { const { getByLabelText } = render( ); expect(getByLabelText('Save changes')).toBeTruthy(); }); it('uses button text as label when no accessibilityLabel provided', () => { const { getByLabelText } = render(); expect(getByLabelText('Click here')).toBeTruthy(); }); it('communicates disabled state', () => { const { getByRole } = render( ); const button = getByRole('button'); expect(button.props.accessibilityState.disabled).toBe(true); }); it('communicates loading state', () => { const { getByRole } = render( ); const button = getByRole('button'); expect(button.props.accessibilityState.busy).toBe(true); }); it('has adequate touch target size', () => { const { getByRole } = render(); const button = getByRole('button'); const { height, width } = button.props.style; // Minimum 44x44 points for touch targets expect(height).toBeGreaterThanOrEqual(44); expect(width).toBeGreaterThanOrEqual(44); }); }); ``` ### 5. Visual Regression Tests Catch unintended visual changes: ```typescript // ui/foundation/Card/Card.visual.test.tsx import React from 'react'; import { render } from '@testing-library/react-native'; import { Card } from './Card'; describe('Card Visual Regression', () => { const variants = ['default', 'elevated', 'outlined'] as const; const sizes = ['small', 'medium', 'large'] as const; variants.forEach(variant => { sizes.forEach(size => { it(`matches snapshot for ${variant} variant with ${size} size`, () => { const { toJSON } = render( Card Content Additional content for testing ); expect(toJSON()).toMatchSnapshot(); }); }); }); it('matches snapshot with all props', () => { const { toJSON } = render( {}} > Interactive Card ); expect(toJSON()).toMatchSnapshot(); }); }); ``` ### 6. Performance Tests Ensure components perform well: ```typescript // ui/patterns/VirtualizedList/VirtualizedList.performance.test.tsx import React from 'react'; import { render, measure } from '@testing-library/react-native'; import { VirtualizedList } from './VirtualizedList'; describe('VirtualizedList Performance', () => { const generateItems = (count: number) => Array.from({ length: count }, (_, i) => ({ id: `item-${i}`, title: `Item ${i}`, })); it('renders large lists efficiently', async () => { const items = generateItems(1000); const renderTime = await measure(() => { render( {item.title}} keyExtractor={item => item.id} /> ); }); // Should render in under 100ms expect(renderTime).toBeLessThan(100); }); it('handles rapid scrolling without lag', async () => { const items = generateItems(5000); const { getByTestId } = render( {item.title}} keyExtractor={item => item.id} /> ); const list = getByTestId('list'); // Simulate rapid scrolling const scrollPerformance = await measure(() => { for (let i = 0; i < 10; i++) { fireEvent.scroll(list, { nativeEvent: { contentOffset: { y: i * 1000 }, }, }); } }); // Should handle scrolling smoothly expect(scrollPerformance).toBeLessThan(50); }); }); ``` ## Test Organization ### Test File Structure Organize tests alongside components for easy maintenance: ``` ui/foundation/Button/ ├── Button.tsx ├── Button.test.tsx # Main component tests ├── Button.accessibility.test.tsx # Accessibility-specific tests ├── Button.visual.test.tsx # Visual regression tests ├── Button.performance.test.tsx # Performance tests (if needed) └── __snapshots__/ # Jest snapshots ``` ### Test Utilities Create shared test utilities for common scenarios: ```typescript // ui/test-utils/render.tsx import React from 'react'; import { render as rtlRender } from '@testing-library/react-native'; import { ThemeProvider } from '@/core/shared/contexts/ThemeContext'; export function render( ui: React.ReactElement, options?: any ) { const Wrapper = ({ children }: { children: React.ReactNode }) => ( {children} ); return rtlRender(ui, { wrapper: Wrapper, ...options }); } // Re-export everything export * from '@testing-library/react-native'; ``` ### Mock Utilities Create consistent mocks for common dependencies: ```typescript // ui/test-utils/mocks.ts export const mockNavigation = { navigate: jest.fn(), goBack: jest.fn(), setOptions: jest.fn(), }; export const mockTheme = { colors: { primary: { 500: '#3b82f6', 600: '#2563eb', }, // ... other colors }, spacing: { xs: 4, sm: 8, md: 16, lg: 24, }, // ... other theme properties }; ``` ## AI Collaboration: Test Implementation Use AI to help generate comprehensive test suites: ```markdown # Component Test Implementation Please help me create tests for a [ComponentName] component: ## Component Information - Component: [ComponentName] - Category: [foundation/pattern/business] - Key Props: [list main props] - User Interactions: [describe key interactions] ## Testing Requirements - Unit tests for props and rendering - Interaction tests for user actions - Snapshot tests for UI verification - Accessibility tests - Edge case tests ## Implementation Needs - Set up test boilerplate with proper imports - Create comprehensive test cases - Implement proper mocks for dependencies - Show how to test each key component feature - Include examples for async behavior testing if needed Please provide a complete test implementation for this component. ``` ## Testing Best Practices Summary **(Do ✅) Testing Best Practices** * Write tests alongside implementation * Test from the user's perspective * Cover all component states and variations * Include accessibility in your test suite * Use descriptive test names that explain the scenario * Mock external dependencies appropriately * Keep tests focused and isolated * Test error scenarios and edge cases **(Don't ❌) Testing Anti-patterns** * Test implementation details that may change * Write tests that depend on component internals * Create overly complex test setups * Skip testing "simple" components * Ignore accessibility testing * Write tests after bugs are found * Test third-party library functionality * Rely solely on snapshot tests **(Consider 🤔) Testing Pro Tips** * Use test.each for testing multiple scenarios * Create custom matchers for common assertions * Set up continuous integration for test runs * Monitor test coverage trends over time * Review tests during code reviews * Refactor tests when they become hard to maintain * Use visual regression testing for UI-heavy components * Consider property-based testing for complex logic ## Next Steps After completing your component tests: 1. **Run the Full Test Suite**: Ensure all tests pass consistently 2. **Check Coverage**: Verify you meet coverage requirements 3. **Review Test Quality**: Ensure tests are meaningful and maintainable 4. **Move to Review Phase**: Proceed to the [Review Process](/docs/ui-development/architecture/development-workflow/review-process) phase ## Related Documents * [Testing Patterns](/docs/ui-development/development/testing-patterns) - Comprehensive testing strategies * [Component Patterns](/docs/ui-development/development/component-patterns) - Component implementation patterns * [Accessibility Guide](/docs/ui-development/accessibility) - Accessibility testing requirements * [AI Collaboration Guide](/docs/ui-development/architecture/ai-collaboration) - AI-assisted test generation # UTA UI Development: Component Review Process URL: https://dev.updatetheapp.com/docs/ui-development/architecture/development-workflow/review-process Documentation: https://dev.updatetheapp.com/docs/ui-development/architecture/development-workflow/review-process Self-review, peer review, and QA testing procedures to ensure UI component quality. *** title: Component Review Process description: Self-review, peer review, and QA testing procedures to ensure UI component quality. ------------------------------------------------------------------------------------------------ import { Tab, Tabs } from 'fumadocs-ui/components/tabs'; import { Callout } from 'fumadocs-ui/components/callout'; import { Steps, Step } from 'fumadocs-ui/components/steps'; # Component Review Process ## Overview The review process ensures components meet quality standards before deployment. Through systematic reviews at multiple levels, we maintain high code quality and catch issues early. **Quality Gates** The review process acts as quality gates, ensuring components are production-ready before deployment. Each review level catches different types of issues, from code quality to user experience problems. ## Purpose & Scope * Comprehensive self-review techniques * Peer review best practices * QA testing procedures * Review documentation templates * UI component developers * Code reviewers * QA engineers * Team leads managing code quality This process should be followed: * Before submitting any new component * When making significant changes to existing components * As part of standard development workflow ## Step 1: Self Review Before submitting for review, complete a thorough self-review: **Self Review Checklist** A comprehensive self-review catches issues early and saves time during peer review. Use this checklist to ensure your component is ready: **Code Quality** * [ ] Component follows our architecture rules * [ ] All props have proper TypeScript types * [ ] JSDoc comments are comprehensive and accurate * [ ] No hardcoded values - all styling uses design tokens * [ ] Performance optimizations applied (React.memo, useMemo) * [ ] Error boundaries and error handling implemented **Functionality** * [ ] All required functionality implemented * [ ] Edge cases handled appropriately * [ ] Loading and error states work correctly * [ ] Component handles all prop combinations * [ ] Default values work as expected **Testing** * [ ] All tests pass * [ ] Test coverage meets requirements (80%+) * [ ] Accessibility tests included * [ ] Visual regression tests added * [ ] Integration tests work **Documentation** * [ ] Component props documented with examples * [ ] Usage examples provided * [ ] Integration patterns documented * [ ] Migration notes added if applicable **Code Quality Verification** Run these commands to verify code quality: ```bash # TypeScript type checking npx tsc --noEmit # Linting check npm run lint:ui # Format check npm run format:check # Bundle size check npm run analyze:bundle -- --component=ComponentName ``` **Common Issues to Check** 1. **Architecture Compliance** * Component in correct category folder * Proper file structure maintained * Exports follow conventions 2. **Design Token Usage** ```typescript // ❌ Bad: Hardcoded values style={{ color: '#333', padding: 16 }} // ✅ Good: Design tokens style={{ color: theme.colors.text.primary, padding: theme.spacing.md }} ``` 3. **TypeScript Quality** * No `any` types without justification * Proper generic constraints * Exhaustive type checking **Test Coverage Verification** Ensure comprehensive test coverage: ```bash # Run tests with coverage npm test -- --coverage --testPathPattern=ComponentName # Check coverage thresholds # Foundation: 90-100% # Patterns: 80-100% # Business: 70-90% ``` **Key Testing Areas** 1. **Unit Tests** * All props tested * State changes verified * Event handlers called correctly 2. **Accessibility Tests** * Screen reader compatibility * Keyboard navigation * Focus management * WCAG compliance 3. **Visual Tests** * Snapshot tests for all variants * Platform-specific rendering * Theme variations 4. **Integration Tests** * Works with providers * Navigation integration * State management compatibility ## Step 2: Peer Review Submit component for peer review with clear context: **Peer Review Template** Use this template when creating your pull request: ```markdown ## Component Review: [ComponentName] ### Overview - **Type**: [Foundation/Pattern/Business] Component - **Purpose**: [Brief description of what the component does] - **Dependencies**: [List of key dependencies] - **New/Modified**: [New component or modification to existing] ### Changes - [List of key changes or features implemented] - [Design requirements addressed] - [Edge cases handled] ### Technical Details - **Architecture**: [Description of component architecture] - **Props API**: [Key props and their purpose] - **State Management**: [How state is managed] - **Performance Considerations**: [Optimizations applied] ### Testing - **Coverage**: [Test coverage percentage] - **Key Scenarios**: [Main test cases] - **Edge Cases**: [Unusual scenarios tested] - **Accessibility**: [a11y test results] ### Self-Review Checklist - [ ] Architecture compliance verified - [ ] All design tokens used (no hardcoded values) - [ ] TypeScript types complete and accurate - [ ] Tests pass with good coverage - [ ] Accessibility requirements met - [ ] Documentation complete - [ ] No console warnings/errors ### Screenshots/Videos [Add screenshots or videos showing the component in action] ### Questions for Reviewers 1. [Specific question about implementation] 2. [Question about API design] 3. [Question about accessibility approach] ### Related Issues - Resolves #[issue-number] - Related to #[issue-number] ``` **Code Review Focus Areas** Reviewers should pay special attention to these areas: **1. Architecture Compliance** * Component placed in correct folder * Follows naming conventions * Proper import/export patterns * No architecture rule violations **2. Design Token Usage** * All colors use theme tokens * Typography follows token system * Spacing uses consistent tokens * No hardcoded values **3. Accessibility** * Proper semantic roles * Screen reader support * Keyboard navigation * Focus management **4. Performance** * Appropriate memoization * Efficient re-rendering * Bundle size impact * Runtime performance **5. API Design** * Consistent with existing components * Intuitive prop names * Proper TypeScript types * Good defaults * Extensibility considered **Review Process Flow** #### Submit PR with Complete Context * Use the review template * Include all relevant information * Add screenshots/videos * Link related issues #### Initial Review (24-48 hours) * Reviewers examine code quality * Architecture compliance checked * API design reviewed * Performance considerations assessed #### Address Feedback (Iterative) * Respond to all comments * Make requested changes * Explain design decisions * Update tests as needed #### Final Approval * All comments addressed * Tests passing * Documentation complete * Ready for QA review **(Do ✅) Good Review Practices** * Provide constructive feedback * Suggest specific improvements * Acknowledge good patterns * Ask clarifying questions * Consider the bigger picture **(Don't ❌) Review Anti-patterns** * Nitpick without substance * Block on personal preferences * Ignore the PR template * Rush through reviews * Approve without testing ## Step 3: QA Review Components undergo QA testing across devices and scenarios: **QA Testing Checklist** **Cross-Platform Testing** * [ ] iOS device testing (iPhone SE, iPhone 14 Pro, iPad) * [ ] Android device testing (Small, Medium, Large screens) * [ ] Web browser testing (if applicable) **Visual Testing** * [ ] Dark/light theme switching * [ ] RTL language support * [ ] Different text sizes * [ ] Font scaling * [ ] Color contrast meets WCAG **Interaction Testing** * [ ] Touch/click interactions * [ ] Swipe/drag gestures * [ ] Keyboard navigation * [ ] Screen reader functionality **Performance Testing** * [ ] Load time \< 100ms * [ ] Smooth animations (60fps) * [ ] No jank on interaction * [ ] No memory leaks **Integration Testing** * [ ] Works within features * [ ] Integrates with navigation * [ ] State management integration * [ ] API integration (business components) * [ ] Theme switching works **Testing Scenarios** **Visual Testing** ```markdown 1. Component renders correctly in all states: - Default state - Hover/pressed states - Disabled state - Loading state - Error state 2. Theme compatibility: - Light theme - Dark theme - Custom themes - High contrast mode 3. Text handling: - Short text - Long text - Multi-line text - Different font sizes - RTL languages - Special characters 4. Layout testing: - Different screen sizes - Landscape/portrait - Inside scroll views - Within nested components - With/without flex containers ``` **Accessibility Testing** ```markdown 1. Screen reader support: - VoiceOver (iOS) - TalkBack (Android) - Labels read correctly - Actions described properly 2. Keyboard navigation: - Tab order logical - Focus visible and clear - Keyboard actions work - No keyboard traps 3. Touch targets: - At least 44x44pt - Clear tap feedback - No overlapping targets - Edge case handling ``` **Performance Testing** ```markdown 1. Rendering performance: - Initial render time - Re-render time - Animation smoothness - Multiple instances on screen 2. Data handling: - Empty data - Single item - Maximum items - Long text content - Special characters 2. Network conditions: - Fast connection - Slow 3G - Offline mode - Request failures 3. Device constraints: - Low memory - Battery saver mode - Limited storage - Background/foreground ``` **Issue Reporting Template** When reporting issues, follow this template: ```markdown ## Component Issue Report **Component Name**: [Name] **Component Version**: [Version] **Environment**: - Platform: iOS/Android/Web - Device/OS: [Device model / OS version] - Screen size: [Size in points/pixels] **Issue Description**: [Clear description of the problem] **Expected Behavior**: [What should happen] **Actual Behavior**: [What actually happens] **Reproduction Steps**: 1. [Step 1] 2. [Step 2] 3. [Step 3] **Visual Evidence**: [Screenshots or videos] **Severity**: [Critical/High/Medium/Low] **Impact**: [How this affects users] **Suggested Fix**: [If you have ideas] ``` **Issue Prioritization Criteria** * **P0 (Critical)**: Crashes, unusable component, data loss * **P1 (High)**: Major functional issue, serious visual problems * **P2 (Medium)**: Minor functional issues, visual inconsistencies * **P3 (Low)**: Cosmetic issues, enhancements, edge cases ## Next Steps After completing the review process: 1. Proceed with [final documentation and deployment](/docs/ui-development/architecture/development-workflow/deployment-integration) 2. Prepare for feature integration 3. Document any learnings from the review process ## Related Documents * [Development Phase](/docs/ui-development/architecture/development-workflow/development-phase) - Implementation details * [Deployment Integration](/docs/ui-development/architecture/development-workflow/deployment-integration) - Next steps after review * [Testing Strategies](/docs/ui-development/architecture/development-workflow/testing-strategies) - Comprehensive testing * [UI Architecture Standards](/docs/ui-development/architecture/standards) - Quality requirements # UTA UI Development: Component Deployment & Integration URL: https://dev.updatetheapp.com/docs/ui-development/architecture/development-workflow/deployment-integration Documentation: https://dev.updatetheapp.com/docs/ui-development/architecture/development-workflow/deployment-integration Completing documentation, integrating into features, and deploying UI components into production. *** title: Component Deployment & Integration description: Completing documentation, integrating into features, and deploying UI components into production. -------------------------------------------------------------------------------------------------------------- import { Tab, Tabs } from 'fumadocs-ui/components/tabs'; import { Callout } from 'fumadocs-ui/components/callout'; import { Steps, Step } from 'fumadocs-ui/components/steps'; # Component Deployment & Integration ## Overview After passing all review stages, components must be properly documented, integrated into features, and deployed. This document covers the processes for smooth integration and reliable deployment. **Consistent Integration** Consistent deployment and integration practices ensure that components behave reliably across all features and environments. These steps bridge the gap between development and production use. ## Purpose & Scope * Documentation requirements and best practices * Feature integration patterns * Component release processes * Versioning and change management * UI component developers * Feature developers * Documentation writers * Release managers Follow these processes: * After component review is complete * Before merging to main branch * When preparing releases * When updating production applications ## Step 1: Final Documentation Documentation is a critical part of component deployment: **Documentation Checklist** All components must include: **Component Documentation** * [ ] Purpose and use cases * [ ] Complete props API with types and descriptions * [ ] Code examples for common scenarios * [ ] Accessibility considerations * [ ] Performance notes **Integration Guides** * [ ] Basic usage examples * [ ] Advanced usage patterns * [ ] Feature integration examples * [ ] Common pitfalls to avoid **Implementation Notes** * [ ] Architecture decisions * [ ] Key dependencies * [ ] Browser/device compatibility * [ ] Known limitations **Changelog** * [ ] Version history * [ ] Breaking changes * [ ] Deprecation notices **Storybook Integration** Each component should have Storybook stories: ```typescript // ui/foundation/Button/Button.stories.tsx import { Meta, StoryObj } from '@storybook/react'; import { Button } from './Button'; const meta: Meta = { title: 'UI/Foundation/Button', component: Button, parameters: { layout: 'centered', }, tags: ['autodocs'], argTypes: { variant: { control: 'select', options: ['primary', 'secondary', 'ghost'], description: 'Button style variant' }, size: { control: 'select', options: ['small', 'medium', 'large'], description: 'Button size variant' }, disabled: { control: 'boolean', description: 'Disable button interactions' } } }; export default meta; type Story = StoryObj; export const Primary: Story = { args: { variant: 'primary', children: 'Primary Button', size: 'medium', }, }; export const Secondary: Story = { args: { variant: 'secondary', children: 'Secondary Button', size: 'medium', }, }; export const Small: Story = { args: { variant: 'primary', children: 'Small Button', size: 'small', }, }; export const Disabled: Story = { args: { variant: 'primary', children: 'Disabled Button', size: 'medium', disabled: true, }, }; ``` **Documentation Example** Here's how to document your component: **Component README Structure:** * Component purpose and description * Installation and import instructions * Basic usage examples * Props API documentation * Accessibility notes * Advanced usage patterns **Props Documentation Format:** | Prop | Type | Default | Description | | ---------- | ------------------------------------- | ----------- | --------------------------- | | `variant` | `'primary' \| 'secondary' \| 'ghost'` | `'primary'` | Visual style variant | | `size` | `'small' \| 'medium' \| 'large'` | `'medium'` | Size variant | | `disabled` | `boolean` | `false` | Disables button interaction | | `loading` | `boolean` | `false` | Shows loading indicator | | `onPress` | `() => void` | - | Callback when pressed | **Accessibility Guidelines:** * Use semantic HTML elements * Include focus indicators * Support screen readers * Provide proper ARIA labels * Test with assistive technology **Code Examples Structure:** * Basic usage example * Props demonstration * Integration patterns * Edge cases and error handling ## Step 2: Feature Integration Integrate the component into relevant features: **Feature Integration Process** #### Identify Integration Points * Review features needing the component * Identify existing components to replace * Define integration goals #### Feature Branch Integration * Create feature integration branch * Add component to feature * Adapt feature code as needed * Test integration thoroughly #### Shared Component Updates * Identify any common needs across features * Extract shared patterns to component * Ensure backward compatibility #### Cross-Feature Testing * Test with all integrated features * Verify no regressions * Document feature-specific usage **Feature Adaptation Examples** **1. Simple Integration** ```typescript // Before: features/product-catalog/screens/ProductList.tsx import { TouchableOpacity, Text } from 'react-native'; function ProductListScreen() { return ( {/* Old button implementation */} Load More ); } // After: features/product-catalog/screens/ProductList.tsx import { Button } from '@/ui/foundation/Button'; function ProductListScreen() { return ( {/* New component integration */} ); } ``` **2. Complex Integration with Props Mapping** ```typescript // Before: features/checkout/components/SubmitOrder.tsx function SubmitOrderButton({ isProcessing, canSubmit, onSubmitOrder }) { return ( {isProcessing ? ( ) : ( {canSubmit ? 'Place Order' : 'Complete Required Fields'} )} ); } // After: features/checkout/components/SubmitOrder.tsx import { Button } from '@/ui/foundation/Button'; function SubmitOrderButton({ isProcessing, canSubmit, onSubmitOrder }) { return ( ); } ``` **Migration Strategy** **1. Progressive Migration** For large applications, use a phased approach: ```typescript // Step 1: Create adapter that works with both old and new // features/shared/ButtonAdapter.tsx import { Button } from '@/ui/foundation/Button'; export const ButtonAdapter = ({ useNewComponent = false, style, textStyle, title, ...props }) => { // Use new component when specified if (useNewComponent) { return ( ); } // Fall back to old implementation return ( {title} ); }; // Step 2: Update features one by one with adapter // features/some-feature/SomeScreen.tsx import { ButtonAdapter } from '../shared/ButtonAdapter'; function SomeScreen() { return ( ); } // Step 3: Once migration is complete, remove adapter ``` **2. Feature Flag Approach** Use feature flags for safer rollout: ```typescript // core/config/featureFlags.ts export const featureFlags = { useNewButtonComponents: true, useNewFormComponents: false }; // features/some-feature/SomeScreen.tsx import { featureFlags } from '@/core/config/featureFlags'; function SomeScreen() { if (featureFlags.useNewButtonComponents) { return ; } // Fall back to old implementation return ( Submit ); } ``` ## Step 3: Release Process Deploy component through proper release channels: **Version Management** Follow semantic versioning for components: * **MAJOR (2.0.0)**: Breaking changes * **MINOR (1.1.0)**: New features, backward compatible * **PATCH (1.0.1)**: Bug fixes, backward compatible **Component Version Tracking** ```typescript // ui/foundation/Button/Button.tsx /** * Button component * @version 1.2.0 * @since 1.0.0 * @lastUpdated 2023-08-15 */ export const Button = ({ ... }) => { ... }; ``` **Version History** ```typescript // ui/foundation/Button/CHANGELOG.md # Button Component Changelog ## 1.2.0 (2023-08-15) Features: - Added loading state - Added icon support ## 1.1.0 (2023-07-20) Features: - Added ghost variant - Added size prop ## 1.0.0 (2023-07-01) Initial release ``` **Component Release Process** #### Pre-release Checklist * All reviews passed * Tests passing at 100% * Documentation complete * Storybook stories added * Performance benchmarks meet targets #### Release Preparation * Update version numbers * Update changelog * Create release branch * Prepare release notes #### CI/CD Pipeline * Run automated tests * Build package * Publish to internal registry * Deploy to staging environment #### Final Validation * Smoke test in staging * Verify documentation * Confirm integrations work * Get final approval #### Production Deployment * Merge to main branch * Publish to production registry * Tag release in git * Announce to team **Changelog Best Practices** **Template Structure** Your changelog should follow this format: * **Header**: Component name and changelog title * **Unreleased section**: For upcoming changes * **Version sections**: For each release with date * **Categories**: Added, Changed, Fixed, Removed * **Clear descriptions**: What changed and why **Version Example:** * `[1.2.0] - 2023-08-15` format for versions * Group changes by type (Added, Changed, Fixed) * Include specific details about each change * Reference any breaking changes clearly **Breaking Changes Documentation** When introducing breaking changes: * **List all breaking changes**: Be comprehensive * **Explain the impact**: What will break * **Provide migration examples**: Before/after code * **Include timeline**: When support ends * **Update documentation**: Reflect new patterns **Migration Guide Format:** * **Before/After examples**: Show exact changes needed * **Step-by-step instructions**: Clear migration path * **Common pitfalls**: What to watch out for * **Testing recommendations**: How to verify migration * **Support resources**: Where to get help ## Next Steps After deployment: 1. Continue to [monitoring and maintenance](/docs/ui-development/architecture/development-workflow/post-deployment) 2. Collect initial usage feedback 3. Plan for iterative improvements ## Related Documents * [Review Process](/docs/ui-development/architecture/development-workflow/review-process) - Pre-deployment quality checks * [Post-Deployment](/docs/ui-development/architecture/development-workflow/post-deployment) - Monitoring and maintenance * [Release Workflow](/docs/development-workflow/release-workflow) - General release process * [Documentation Standards](/docs/coding-standards/documentation) - Documentation requirements # UTA UI Development: Post-Deployment Monitoring & Maintenance URL: https://dev.updatetheapp.com/docs/ui-development/architecture/development-workflow/post-deployment Documentation: https://dev.updatetheapp.com/docs/ui-development/architecture/development-workflow/post-deployment Monitoring component performance, gathering feedback, and maintaining UI components over time. *** title: Post-Deployment Monitoring & Maintenance description: Monitoring component performance, gathering feedback, and maintaining UI components over time. ----------------------------------------------------------------------------------------------------------- import { Tab, Tabs } from 'fumadocs-ui/components/tabs'; import { Callout } from 'fumadocs-ui/components/callout'; import { Steps, Step } from 'fumadocs-ui/components/steps'; # Post-Deployment Monitoring & Maintenance ## Overview After deployment, components require ongoing monitoring and maintenance to ensure continued performance, reliability, and relevance. This document covers best practices for the post-deployment lifecycle. **Long-term Success** The difference between good and great components often lies in post-deployment care. Continuous monitoring, refinement, and maintenance ensure components remain valuable and performant over time. ## Purpose & Scope * Performance monitoring techniques * Usage analytics integration * Maintenance scheduling * Component deprecation process * UI component maintainers * Performance engineers * Product managers * Support engineers These practices apply: * After component deployment * During regular maintenance cycles * When planning component updates * Before component deprecation ## Step 1: Performance Monitoring Implement proper monitoring to ensure components perform well in production: **Performance Monitoring Setup** Implement these monitoring practices for all production components: **1. Performance Metrics Collection** * Initial render timing * Re-render timing * Memory usage * Interaction responsiveness **2. Error Tracking** * JS exceptions * Render errors * API interaction failures * Boundary catches **3. Usage Analytics** * Feature adoption * Interaction patterns * Configuration distribution * A/B test results **4. User Feedback** * Satisfaction surveys * Issue reports * Feature requests * Usability feedback **Key Performance Metrics** Track these metrics for each component: **Rendering Performance** * **Time to First Render**: \< 50ms target * **Re-render Time**: \< 16ms target (60fps) * **Interaction Response**: \< 100ms target * **Animation Smoothness**: 60fps target **Resource Usage** * **Memory Footprint**: Component-specific baseline * **Bundle Size Impact**: \< 5KB increase target * **API Call Efficiency**: \< 2 calls per interaction * **State Update Frequency**: Minimized to essential changes **User Experience** * **Time to Interactive**: When component is fully usable * **Input Latency**: Delay between user action and response * **Perceived Performance**: User rating of speed * **Error Rate**: Percentage of interactions with errors **Create component-specific baselines and alert on deviations of >20%.** **Component Instrumentation** Add performance tracking to components: ```typescript // ui/foundation/Button/Button.tsx import { useEffect } from 'react'; import { Performance } from '@/core/shared/monitoring'; export const Button = (props) => { // Mark render start const renderStart = Performance.now(); useEffect(() => { // Track render time const renderTime = Performance.now() - renderStart; Performance.trackMetric( 'ComponentRender', renderTime, { component: 'Button', variant: props.variant, } ); // Track interaction performance return () => { Performance.trackInteraction( 'ComponentName.renderTime', 'ComponentName.render' ); }; }, []); }; ``` **Error Monitoring** ```typescript // Add error boundary export class ComponentErrorBoundary extends Component { componentDidCatch(error: Error, errorInfo: ErrorInfo) { // Log to monitoring service ErrorReporter.log({ error, errorInfo, component: 'ComponentName', version: '1.2.0', }); } render() { if (this.state.hasError) { return ; } return this.props.children; } } ``` **Usage Analytics** ```typescript // Track component usage export const ComponentName = (props) => { useEffect(() => { Analytics.track('ComponentUsed', { component: 'ComponentName', variant: props.variant, size: props.size, }); }, [props.variant, props.size]); }; ``` **Monitoring Dashboard** Track key metrics: * Render performance (p50, p90, p99) * Error rates * Usage frequency * Props distribution * Platform breakdown ## Step 2: Ongoing Maintenance Establish regular maintenance processes: **Ongoing Maintenance** **Regular Tasks** **Weekly** * [ ] Check error reports * [ ] Review performance metrics * [ ] Triage new issues **Monthly** * [ ] Update dependencies * [ ] Review usage patterns * [ ] Update documentation * [ ] Performance optimization **Quarterly** * [ ] Architecture review * [ ] Accessibility audit * [ ] API consistency check * [ ] Deprecation planning **Issue Tracking** ```markdown ## Component Issue Template **Component**: ComponentName **Version**: 1.2.0 **Severity**: High/Medium/Low **Description**: [Clear description of the issue] **Impact**: - Affected users: [percentage] - Frequency: [occurrences/day] - Platforms: [iOS/Android/Both] **Proposed Solution**: [Description of fix] **Timeline**: - Investigation: [date] - Fix development: [date] - Testing: [date] - Release: [date] ``` **Issue Management Workflow** #### Issue Triage * Categorize by component * Assign severity and priority * Evaluate impact scope * Determine if hotfix needed #### Investigation * Reproduce issue * Identify root cause * Document in issue tracker * Create test case #### Resolution Planning * Design fix approach * Estimate effort * Add to sprint/backlog * Set target release #### Implementation * Develop fix * Add/update tests * Document changes * Update changelog #### Verification * Review code * Test fix * Regression testing * Performance verification #### Release * Merge to main * Version bump * Release notes * Communication to users **Prioritization Framework** | Priority | Description | Response Time | Resolution Time | | -------- | ------------------------------------ | ------------- | --------------- | | P0 | Critical issue, broken functionality | 2 hours | 24 hours | | P1 | High impact, major limitation | 24 hours | 1 week | | P2 | Medium impact, functional workaround | 3 days | 2 weeks | | P3 | Low impact, cosmetic | 1 week | Next release | **Deprecation Process** When retiring components: 1. **Announce** (3 months before) * Reason for deprecation * Migration path * Timeline 2. **Add Warnings** (2 months before) ```typescript console.warn( 'ComponentName is deprecated and will be removed in v2.0.0. ' + 'Please migrate to NewComponent.' ); ``` 3. **Support Period** (1 month) * Only critical fixes * Help with migration * Answer questions 4. **Remove** (On schedule) * Remove from codebase * Update documentation * Final announcement **Deprecation Documentation Structure** Your deprecation notice should include: **Header Information:** * Component name and status * Removal date * Replacement component **Reason for Deprecation:** * Technical limitations * Performance issues * Design system changes * Accessibility concerns **Migration Instructions:** * Before/after code examples * Prop mapping changes * Behavior differences * Testing recommendations **Support Timeline:** * Announcement date * Warning implementation * Support end date * Removal date **Contact Information:** * Team responsible * Support channels * Documentation links ## Summary Post-deployment monitoring and maintenance ensures components: * Perform reliably in production * Continue to meet evolving needs * Maintain compatibility with the platform * Provide consistent value over time ## Next Steps 1. Set up monitoring for your components 2. Establish regular maintenance schedules 3. Create deprecation plans for legacy components 4. Document performance baselines ## Related Documents * [Deployment Integration](/docs/ui-development/architecture/development-workflow/deployment-integration) - Pre-monitoring deployment processes * [Performance Standards](/docs/ui-development/performance) - Component performance requirements * [Analytics Integration](/docs/features/analytics) - Usage tracking implementation * [Deprecation Policy](/docs/coding-standards/deprecation-policy) - Component retirement process # UTA UI Development: Maintenance and Lifecycle URL: https://dev.updatetheapp.com/docs/ui-development/architecture/development-workflow/maintenance-lifecycle Documentation: https://dev.updatetheapp.com/docs/ui-development/architecture/development-workflow/maintenance-lifecycle Regular maintenance tasks, component evolution, deprecation processes, and troubleshooting common issues. *** title: Maintenance and Lifecycle description: Regular maintenance tasks, component evolution, deprecation processes, and troubleshooting common issues. ---------------------------------------------------------------------------------------------------------------------- import { Tab, Tabs } from 'fumadocs-ui/components/tabs'; import { Callout } from 'fumadocs-ui/components/callout'; import { Steps, Step } from 'fumadocs-ui/components/steps'; # Maintenance and Lifecycle ## Overview Component maintenance is an ongoing process that ensures components remain reliable, performant, and aligned with evolving requirements. This guide covers the complete lifecycle of components from initial release through eventual deprecation. **Living Components** Components are living entities that evolve with your application. Regular maintenance ensures they continue to meet user needs while maintaining quality standards. ## Component Lifecycle Management **Component Lifecycle Phases** Components go through distinct phases during their lifetime: 1. **Introduction** (0-3 months) * Initial release and adoption * Rapid iteration based on feedback * Documentation refinement * Performance baseline establishment 2. **Growth** (3-12 months) * Feature additions * API stabilization * Widespread adoption * Pattern establishment 3. **Maturity** (12+ months) * Stable API * Optimization focus * Minimal breaking changes * Comprehensive documentation 4. **Decline** (When needed) * Replacement planning * Migration preparation * Deprecation warnings * Support reduction 5. **Deprecation** (3-6 month process) * Official deprecation announcement * Migration guide publication * Limited maintenance * Final removal **Regular Maintenance Tasks** **Weekly Tasks** * [ ] Monitor error reports and crash analytics * [ ] Review performance metrics dashboards * [ ] Triage new issues and bug reports * [ ] Check automated test results * [ ] Review usage analytics **Monthly Tasks** * [ ] Update component dependencies * [ ] Review and merge dependency updates * [ ] Analyze usage patterns and metrics * [ ] Update documentation for clarity * [ ] Performance optimization if needed * [ ] Review accessibility compliance **Quarterly Tasks** * [ ] Comprehensive architecture review * [ ] Full accessibility audit * [ ] Performance benchmarking * [ ] API consistency check * [ ] Design token compliance review * [ ] Breaking change planning **Annual Tasks** * [ ] Major architecture assessment * [ ] Technology stack evaluation * [ ] Long-term roadmap planning * [ ] Deprecation planning for legacy components * [ ] Team knowledge sharing sessions **Tracking and Monitoring Tools** **Component Health Dashboard** ```typescript // Example monitoring setup import { ComponentMonitor } from '@/core/monitoring'; ComponentMonitor.track({ component: 'ComponentName', metrics: { renderTime: true, errorRate: true, usageCount: true, propUsage: true, }, alerts: { errorRateThreshold: 0.01, // 1% renderTimeThreshold: 50, // ms }, }); ``` **Key Metrics to Track** 1. **Performance Metrics** * Render time (p50, p90, p99) * Re-render frequency * Memory usage * Bundle size impact 2. **Quality Metrics** * Error rate * Crash frequency * Test coverage * TypeScript strictness 3. **Usage Metrics** * Adoption rate * Feature usage * Prop combinations * Platform distribution 4. **Developer Experience** * Time to implement * Support tickets * Documentation views * API satisfaction ## Evolution and Updates ### Managing Component Changes **Types of Updates** **Patch Updates (0.0.X)** * Bug fixes * Performance improvements * Documentation updates * Dependency updates (non-breaking) ```json // Example: 1.2.3 → 1.2.4 { "version": "1.2.4", "changes": [ "Fixed memory leak in animation cleanup", "Improved TypeScript definitions", "Updated accessibility labels" ] } ``` **Minor Updates (0.X.0)** * New features (backward compatible) * New props or options * Deprecation warnings * Performance enhancements ```json // Example: 1.2.4 → 1.3.0 { "version": "1.3.0", "changes": [ "Added 'size' prop with small/medium/large options", "Added loading state support", "Deprecated 'isLarge' prop (use size='large')", "30% render performance improvement" ] } ``` **Major Updates (X.0.0)** * Breaking API changes * Architecture redesign * Technology migrations * Removed deprecated features ```json // Example: 1.3.0 → 2.0.0 { "version": "2.0.0", "breaking": [ "Changed 'onPress' prop to 'onPress'", "Removed deprecated 'isLarge' prop", "New required prop 'accessibilityLabel'", "Migrated from class to function component" ] } ``` **Version Management Strategy** **Semantic Versioning Rules** Follow strict semantic versioning (SemVer): ```typescript // Version format: MAJOR.MINOR.PATCH // PATCH: Backward compatible bug fixes "1.2.3" → "1.2.4" // MINOR: Backward compatible new features "1.2.4" → "1.3.0" // MAJOR: Breaking changes "1.3.0" → "2.0.0" ``` **Pre-release Versions** Use pre-release versions for testing: ```json { "version": "2.0.0-beta.1", "version": "2.0.0-rc.1", "version": "2.0.0" } ``` **Version Tags** ```bash # Tag stable releases git tag -a v1.2.3 -m "Release version 1.2.3" # Tag pre-releases git tag -a v2.0.0-beta.1 -m "Beta release for v2.0.0" # Push tags git push origin --tags ``` **Branch Strategy** ``` main ├── develop │ ├── feature/new-component-prop │ └── feature/performance-improvement ├── release/v1.3.0 └── hotfix/critical-bug-fix ``` **Change Communication** **Changelog Format** ```markdown # Changelog ## [2.0.0] - 2024-01-15 ### 💥 Breaking Changes - Changed `onClick` prop to `onPress` for React Native consistency - Removed deprecated `isLarge` prop - use `size="large"` instead - Made `accessibilityLabel` a required prop ### ✨ New Features - Added `loading` prop with built-in spinner - Added `icon` prop for leading/trailing icons - Added haptic feedback support on iOS ### 🐛 Bug Fixes - Fixed memory leak in animation cleanup - Fixed touch target size on small devices - Fixed theme switching visual glitch ### 🚀 Performance - Improved render performance by 30% - Reduced bundle size by 15% ### 📝 Documentation - Added migration guide from v1.x - Added accessibility best practices - Updated all code examples ``` **Release Announcement Template** ```markdown # Component Update: ComponentName v2.0.0 🎉 We're excited to announce the release of ComponentName v2.0.0! ## What's New - ✨ Loading state support - 🎨 Icon integration - 📱 Haptic feedback - ⚡ 30% faster rendering ## Breaking Changes ⚠️ Please review the [migration guide](link) for: - Prop name changes - Required accessibility labels - API updates ## Resources - 📖 [Documentation](link) - 🔄 [Migration Guide](link) - 💬 [Discussion Thread](link) - 🐛 [Report Issues](link) ## Support - Slack: #ui-components - Migration period: 3 months ``` ## Deprecation Process **Thoughtful Deprecation** Deprecation should be a careful, well-communicated process that gives teams adequate time to migrate while maintaining application stability. #### Phase 1: Planning (Month -3) **Identify Deprecation Candidates** * Components with better alternatives * Outdated patterns or technologies * Low usage or high maintenance burden * Security or performance concerns **Create Migration Strategy** ```markdown ## Deprecation Plan: OldComponent **Reason**: Replaced by NewComponent with better performance **Timeline**: 6 months (Jan 2024 - Jun 2024) **Migration Path**: OldComponent → NewComponent **Breaking Changes**: Minor API differences **Team Impact**: ~50 usage instances across 10 features ``` #### Phase 2: Announcement (Month 0) **Add Deprecation Warnings** ```typescript export const OldComponent = (props: OldComponentProps) => { useEffect(() => { if (process.env.NODE_ENV !== 'production') { console.warn( 'OldComponent is deprecated and will be removed in v3.0.0.\n' + 'Please migrate to NewComponent.\n' + 'Migration guide: https://docs.example.com/migration/old-to-new' ); } }, []); // Component implementation }; /** * @deprecated Use NewComponent instead. Will be removed in v3.0.0. * @see https://docs.example.com/migration/old-to-new */ export interface OldComponentProps { // Props definition } ``` **Update Documentation** ```markdown # OldComponent > ⚠️ **Deprecated**: This component is deprecated and will be removed in v3.0.0. > Please use [NewComponent](/docs/new-component) instead. > See the [migration guide](/docs/migration/old-to-new) for details. ``` #### Phase 3: Migration Support (Months 1-3) **Provide Migration Tools** ```typescript // Codemod for automated migration // codemods/old-to-new-component.js module.exports = function(fileInfo, api) { const j = api.jscodeshift; return j(fileInfo.source) .find(j.ImportDeclaration, { source: { value: '@/ui/OldComponent' } }) .replaceWith( j.importDeclaration( [j.importDefaultSpecifier(j.identifier('NewComponent'))], j.literal('@/ui/NewComponent') ) ) .toSource(); }; ``` **Migration Guide** ````markdown # Migration Guide: OldComponent to NewComponent ## API Changes | OldComponent | NewComponent | Notes | |--------------|--------------|-------| | `type` | `variant` | Same values | | `onTap` | `onPress` | Same signature | | `large` | `size="large"` | Now uses size prop | ## Before ```tsx ```` ## After ```tsx ``` ```` #### Phase 4: Limited Support (Months 4-5) **Maintenance Mode** - Critical security fixes only - No new features - No non-critical bug fixes - Increased warning visibility ```typescript // Increase warning prominence export const OldComponent = (props: OldComponentProps) => { useEffect(() => { console.error( '🚨 OldComponent is deprecated and will be removed next month!\n' + 'Please migrate to NewComponent immediately.\n' + 'Migration guide: https://docs.example.com/migration/old-to-new' ); }, []); }; ```` #### Phase 5: Removal (Month 6) **Final Steps** * Remove component code * Update all exports * Clean up documentation * Archive migration guide * Final announcement ```bash # Remove component files rm -rf ui/deprecated/OldComponent # Update exports # Remove from ui/index.ts # Update package version npm version major # Tag removal git tag -a v3.0.0 -m "Remove deprecated OldComponent" ``` ## Troubleshooting Common Issues ### Development Issues **Design Token Issues** **Issue: Component Not Using Design Tokens** **Symptoms**: * Hardcoded colors, spacing, or typography * Inconsistent styling across themes * Theme switching not working **Solution**: ```typescript // ❌ Problem: Hardcoded values const styles = StyleSheet.create({ container: { backgroundColor: '#3b82f6', padding: 16, borderRadius: 8, }, text: { fontSize: 16, color: '#ffffff', }, }); // ✅ Solution: Use design tokens import { theme } from '@/core/shared/styles/theme'; const styles = StyleSheet.create({ container: { backgroundColor: theme.colors.primary[500], padding: theme.spacing.md, borderRadius: theme.radii.md, }, text: { fontSize: theme.typography.size.md, color: theme.colors.primary.contrast, }, }); ``` **Debugging Steps**: 1. Search for hex colors: `#[0-9a-fA-F]{3,6}` 2. Search for pixel values: `\d+px|\d+\s*,` 3. Run theme compliance linter 4. Check theme provider setup **Performance Issues** **Issue: Slow Component Rendering** **Symptoms**: * Janky animations * Delayed user interactions * High CPU usage * Memory leaks **Common Causes & Solutions**: **1. Unnecessary Re-renders** ```typescript // ❌ Problem: Component re-renders on every parent update export const ExpensiveComponent = ({ data, onPress }) => { const processedData = data.map(item => ({ ...item, computed: heavyComputation(item), })); return ; }; // ✅ Solution: Memoize expensive operations export const ExpensiveComponent = React.memo(({ data, onPress }) => { const processedData = useMemo( () => data.map(item => ({ ...item, computed: heavyComputation(item), })), [data] ); return ; }); ``` **2. Memory Leaks** ```typescript // ❌ Problem: Cleanup not performed useEffect(() => { const timer = setInterval(updateData, 1000); // Missing cleanup! }, []); // ✅ Solution: Proper cleanup useEffect(() => { const timer = setInterval(updateData, 1000); return () => { clearInterval(timer); }; }, []); ``` **Performance Debugging Tools**: ```typescript // Add performance markers import { PerformanceProfiler } from '@/core/debug'; export const Component = () => { return ( { console.log(`${id} (${phase}) took ${duration}ms`); }} > {/* Component content */} ); }; ``` **Accessibility Issues** **Issue: Poor Screen Reader Support** **Symptoms**: * Elements not announced * Incorrect role announcements * Missing state information * Navigation problems **Common Problems & Solutions**: **1. Missing Labels** ```typescript // ❌ Problem: No accessible label // ✅ Solution: Add accessibility label ``` **2. State Not Communicated** ```typescript // ❌ Problem: State changes not announced {isExpanded && } // ✅ Solution: Announce state changes {isExpanded && } ``` **3. Focus Management** ```typescript // ❌ Problem: Focus not managed after action const handleDelete = () => { deleteItem(item.id); // Focus is lost! }; // ✅ Solution: Manage focus properly const handleDelete = () => { deleteItem(item.id); // Move focus to next logical element if (nextItemRef.current) { AccessibilityInfo.setAccessibilityFocus( findNodeHandle(nextItemRef.current) ); } }; ``` **Accessibility Testing Checklist**: * [ ] Enable screen reader * [ ] Navigate using gestures only * [ ] Verify all content is announced * [ ] Check focus order is logical * [ ] Test with large text sizes * [ ] Verify color contrast ratios ### Testing Issues **Common Test Failures** **Issue: Tests Break After Token Updates** ```typescript // ❌ Problem: Hardcoded values in tests expect(button.props.style.backgroundColor).toBe('#3b82f6'); // ✅ Solution: Use token references import { theme } from '@/core/shared/styles/theme'; expect(button.props.style.backgroundColor).toBe( theme.colors.primary[500] ); ``` **Issue: Async Test Failures** ```typescript // ❌ Problem: Not waiting for async operations it('loads data', () => { const { getByText } = render(); expect(getByText('Data loaded')).toBeTruthy(); }); // ✅ Solution: Wait for async operations it('loads data', async () => { const { findByText } = render(); expect(await findByText('Data loaded')).toBeTruthy(); }); ``` **Issue: Flaky Tests** ```typescript // ❌ Problem: Timing-dependent tests it('shows tooltip', () => { const { getByText, getByTestId } = render(); fireEvent.press(getByTestId('trigger')); expect(getByText('Tooltip')).toBeTruthy(); }); // ✅ Solution: Wait for animations it('shows tooltip', async () => { const { getByTestId, findByText } = render(); fireEvent.press(getByTestId('trigger')); await waitFor(() => { expect(findByText('Tooltip')).toBeTruthy(); }); }); ``` **Coverage Issues** **Improving Test Coverage** ```typescript // Example: Comprehensive test coverage describe('Button Component', () => { // Props testing describe('Props', () => { it.each([ ['primary', theme.colors.primary[500]], ['secondary', theme.colors.secondary[500]], ['danger', theme.colors.danger[500]], ])('renders %s variant correctly', (variant, color) => { const { getByTestId } = render( ); expect( getByTestId('button').props.style.backgroundColor ).toBe(color); }); }); // Interaction testing describe('Interactions', () => { it('calls onPress when pressed', () => { const onPress = jest.fn(); const { getByText } = render( ); fireEvent.press(getByText('Press me')); expect(onPress).toHaveBeenCalledTimes(1); }); it('does not call onPress when disabled', () => { const onPress = jest.fn(); const { getByText } = render( ); fireEvent.press(getByText('Press me')); expect(onPress).not.toHaveBeenCalled(); }); }); // Edge cases describe('Edge Cases', () => { it('handles missing children gracefully', () => { const { container } = render(); expect(getByText(longText)).toBeTruthy(); }); }); }); ``` **Coverage Report Analysis** ```bash # Generate coverage report npm test -- --coverage --coverageReporters=html # Open coverage report open coverage/lcov-report/index.html ``` **Target Coverage Levels** * Statements: 80%+ * Branches: 75%+ * Functions: 80%+ * Lines: 80%+ **Test Maintenance** **Keeping Tests Maintainable** **1. Test Utilities** ```typescript // test-utils/render.tsx export const renderWithProviders = ( ui: React.ReactElement, options?: RenderOptions ) => { const Wrapper = ({ children }) => ( {children} ); return render(ui, { wrapper: Wrapper, ...options }); }; // Usage const { getByText } = renderWithProviders(); ``` **2. Test Data Factories** ```typescript // test-utils/factories.ts export const createMockUser = (overrides?: Partial): User => ({ id: '1', name: 'Test User', email: 'test@example.com', ...overrides, }); export const createMockProduct = ( overrides?: Partial ): Product => ({ id: '1', name: 'Test Product', price: 99.99, ...overrides, }); ``` **3. Custom Matchers** ```typescript // test-utils/matchers.ts expect.extend({ toHaveAccessibilityLabel(element, label) { const hasLabel = element.props.accessibilityLabel === label; return { pass: hasLabel, message: () => `Expected element to have accessibility label "${label}", ` + `but found "${element.props.accessibilityLabel}"`, }; }, }); // Usage expect(button).toHaveAccessibilityLabel('Submit form'); ``` ## Best Practices Summary **Maintenance Excellence** Great component maintenance combines proactive monitoring, thoughtful evolution, clear communication, and comprehensive testing to ensure long-term success. **(Do ✅) Maintenance Best Practices** * **Regular Reviews**: Conduct weekly, monthly, and quarterly reviews * **Monitor Metrics**: Track performance, quality, and usage metrics * **Document Changes**: Maintain comprehensive changelogs * **Communicate Early**: Announce deprecations well in advance * **Provide Migration Paths**: Always offer clear upgrade paths * **Test Thoroughly**: Maintain high test coverage * **Listen to Feedback**: Act on user and developer feedback * **Plan Ahead**: Consider long-term impact of changes * **Automate Checks**: Use CI/CD for quality gates * **Version Properly**: Follow semantic versioning strictly **(Don't ❌) Maintenance Anti-patterns** * **Sudden Breaking Changes**: Never break APIs without warning * **Poor Communication**: Don't surprise users with changes * **Neglect Testing**: Don't let test coverage decline * **Ignore Performance**: Don't let performance degrade over time * **Skip Documentation**: Don't forget to update docs * **Rush Deprecation**: Don't remove features too quickly * **Ignore Feedback**: Don't dismiss user concerns * **Hardcode Values**: Don't bypass design token system * **Mix Concerns**: Don't combine unrelated changes * **Break Conventions**: Don't deviate from established patterns **Quick Reference Guide** **Version Bumps** * Bug fix: `1.2.3` → `1.2.4` (PATCH) * New feature: `1.2.4` → `1.3.0` (MINOR) * Breaking change: `1.3.0` → `2.0.0` (MAJOR) **Deprecation Timeline** * Month -3: Planning * Month 0: Announcement * Months 1-3: Migration support * Months 4-5: Limited support * Month 6: Removal **Key Commands** ```bash # Check component health npm run component:health ComponentName # Run maintenance tasks npm run component:audit npm run component:update-deps npm run component:perf-check # Generate reports npm run component:coverage npm run component:bundle-analysis ``` **Support Channels** * Slack: #ui-components * Email: [ui-team@company.com](mailto:ui-team@company.com) * Docs: /docs/components * Issues: github.com/org/repo/issues ## Next Steps After establishing maintenance processes: 1. Set up automated monitoring and alerts 2. Create component health dashboards 3. Schedule regular review meetings 4. Document team-specific processes 5. Train team on maintenance procedures ## Related Documents * [Development Workflow](/docs/ui-development/architecture/development-workflow) - Complete development process * [Testing Strategies](/docs/ui-development/architecture/development-workflow/testing-strategies) - Testing best practices * [Deployment & Integration](/docs/ui-development/architecture/development-workflow/deployment-integration) - Release processes * [Post-Deployment](/docs/ui-development/architecture/development-workflow/post-deployment) - Monitoring and maintenance * [Component Patterns](/docs/ui-development/development/component-patterns) - Implementation patterns # UTA UI Development: AI Collaboration Guide URL: https://dev.updatetheapp.com/docs/ui-development/architecture/development-workflow/ai-collaboration Documentation: https://dev.updatetheapp.com/docs/ui-development/architecture/development-workflow/ai-collaboration Effective prompts and best practices for AI-assisted component development at each phase of the workflow. *** title: AI Collaboration Guide description: Effective prompts and best practices for AI-assisted component development at each phase of the workflow. ---------------------------------------------------------------------------------------------------------------------- import { Tab, Tabs } from 'fumadocs-ui/components/tabs'; import { Callout } from 'fumadocs-ui/components/callout'; import { Steps, Step } from 'fumadocs-ui/components/steps'; # AI Collaboration Guide ## Overview AI tools can significantly accelerate component development while maintaining quality standards. This guide provides structured prompts and best practices for leveraging AI assistance at each phase of the component development workflow. **AI as a Development Partner** AI works best as a collaborative partner that helps generate boilerplate, suggest patterns, and catch potential issues. However, human expertise remains essential for architectural decisions, code review, and ensuring alignment with project standards. ## Core Principles **(Do ✅) Effective AI Collaboration** * **Provide Context**: Include relevant project details, tech stack, and constraints * **Be Specific**: Use clear, detailed requirements rather than vague requests * **Reference Standards**: Mention your design system, architecture rules, and patterns * **Iterate and Refine**: Use follow-up questions to improve AI suggestions * **Validate Output**: Always review and test AI-generated code * **Learn Patterns**: Use AI to learn new patterns and best practices **Example of Good Context**: ```markdown We're building a React Native app using TypeScript, following a three-layer UI architecture. Components must use our design token system from @/core/shared/styles/theme. All components need comprehensive accessibility support and 80%+ test coverage. ``` **(Don't ❌) Common AI Collaboration Mistakes** * **Vague Requests**: "Create a button component" without specifications * **Blind Acceptance**: Using AI code without review or understanding * **Missing Context**: Not providing project-specific requirements * **Over-reliance**: Expecting AI to make architectural decisions * **Ignoring Standards**: Not validating against your coding standards * **Copy-paste Coding**: Using AI output without adaptation **Example of Poor Request**: ```markdown Make a form component that looks nice ``` **Why it's problematic**: * No technical specifications * No design requirements * No integration context * No accessibility considerations **Quality Control Checklist** When using AI-generated code, always verify: * [ ] **TypeScript Compliance**: No `any` types, proper type safety * [ ] **Design Token Usage**: All styling uses theme tokens * [ ] **Accessibility**: Proper roles, labels, and keyboard support * [ ] **Performance**: Appropriate memoization and optimizations * [ ] **Testing**: Comprehensive test coverage included * [ ] **Documentation**: Clear comments and prop descriptions * [ ] **Standards Alignment**: Follows your project conventions **Validation Commands**: ```bash # Type checking npx tsc --noEmit # Linting npm run lint # Test coverage npm test -- --coverage # Accessibility audit npm run a11y:check ``` ## Phase 1: Requirements & Planning ### AI Prompt: Requirements Definition ```markdown # Component Requirements Definition ## Component Context - **Component Name**: [Name] - **Purpose**: [What it does and why] - **Category**: [foundation/patterns/business] - **Usage**: [Where and how it will be used] ## Technical Requirements - React Native with TypeScript - Must use design tokens from @/core/shared/styles/theme - Accessibility compliant (WCAG 2.1 AA) - Cross-platform (iOS & Android) ## Please Define: 1. Complete props interface with types 2. All component states (default, loading, error, etc.) 3. Accessibility requirements 4. Performance considerations 5. Integration points ``` ```markdown # Advanced Component Specification ## Project Context - **Tech Stack**: React Native, TypeScript, Expo - **Architecture**: Three-layer UI (foundation/patterns/business) - **Design System**: [Link to design system] - **Similar Components**: [List existing similar components] ## Component Details - **Name**: [ComponentName] - **Purpose**: [Detailed purpose and use cases] - **User Stories**: - As a [user], I want to [action] so that [benefit] - [Add more user stories] ## Technical Specifications - **Props Schema**: [Describe expected props] - **State Management**: [Local state, context, or external] - **Data Flow**: [How data moves through component] - **Side Effects**: [API calls, subscriptions, etc.] ## Design Requirements - **Visual States**: [All visual variations] - **Responsive Behavior**: [Mobile, tablet adaptations] - **Animation**: [Transitions, loading states] - **Theme Support**: [Light/dark mode considerations] ## Quality Requirements - **Performance**: [Render time, memory usage targets] - **Accessibility**: [Specific a11y requirements] - **Testing**: [Unit, integration, visual tests needed] - **Documentation**: [Props table, examples needed] ## Constraints & Considerations - **Platform Differences**: [iOS vs Android specifics] - **Backwards Compatibility**: [Version support] - **Migration Path**: [From existing components] ``` **Example: Search Input Component** ```markdown # Component Requirements Definition ## Component Context - **Component Name**: SearchInput - **Purpose**: Provides search functionality with autocomplete - **Category**: patterns (combines Input + suggestions list) - **Usage**: Header search, product search, user search ## Technical Requirements - React Native with TypeScript - Must use design tokens from @/core/shared/styles/theme - Accessibility compliant (WCAG 2.1 AA) - Cross-platform (iOS & Android) - Debounced input for performance ## Please Define: 1. Complete props interface including: - Search query handling - Suggestion data structure - Loading states - Customization options 2. States: empty, typing, loading, results, no results, error 3. Accessibility: search role, live regions for results 4. Performance: debouncing, virtualized suggestion list 5. Integration: How it works with our API client ``` **AI Response Would Include**: * Complete TypeScript interfaces * State management approach * Accessibility implementation * Performance optimization strategies * Integration examples ### AI Prompt: Component Categorization ```markdown # Component Categorization Decision ## Component Information - **Name**: [ComponentName] - **Purpose**: [What the component does] - **Dependencies**: [What it depends on] - **Complexity**: [Simple/Medium/Complex] ## Questions to Answer 1. Does it contain business logic or domain rules? [Yes/No] - If yes, describe: [Details] 2. Does it combine multiple simpler components? [Yes/No] - If yes, list components: [Component list] 3. Is it a single, reusable UI element? [Yes/No] - If yes, describe core function: [Details] ## Please recommend: - Appropriate category (foundation/patterns/business) - Justification for the recommendation - Similar existing components in that category - Potential architectural concerns ``` ## Phase 2: Implementation ### AI Prompt: Component Structure ```markdown # Generate Component File Structure ## Component Details - **Name**: [ComponentName] - **Category**: [foundation/patterns/business] - **Purpose**: [Brief description] ## Generate Files: 1. `[ComponentName].tsx` - Main component 2. `types.ts` - TypeScript interfaces 3. `index.ts` - Exports 4. `[ComponentName].test.tsx` - Tests 5. `styles.ts` - Styled components (if needed) 6. `hooks.ts` - Custom hooks (if needed) ## Requirements: - TypeScript with strict mode - React Native components - Design tokens from @/core/shared/styles/theme - Accessibility attributes - JSDoc documentation - Export patterns matching our standards ``` ````markdown # Component Boilerplate Generation Create a [ComponentName] component with: ## Structure Requirements - Functional component with TypeScript - Props interface in separate types.ts - ForwardRef implementation (if applicable) - Memoization for performance - Error boundary support ## Include These Patterns: ```typescript // Example patterns to follow export const Component = React.memo( React.forwardRef((props, ref) => { // Implementation }) ); Component.displayName = 'ComponentName'; ```` ## Styling Requirements: * Use StyleSheet.create * Import theme tokens * Support theme variants * Handle responsive sizing ## Standard Props: * testID for testing * style for custom styling * accessibility props * className (if web support needed) ```` ```markdown # Complex Pattern Component Generate a [ComponentName] that: ## Architecture - Uses compound component pattern - Provides context for child components - Exports sub-components ## Example Structure: ```typescript Name Value ```` ## Requirements: * Type-safe compound components * Context for shared state * Flexible composition * Child component validation * Proper display names ## Files Needed: * Main component with context * Sub-components in separate files * Comprehensive types * Usage examples ```` ### AI Prompt: Implementation ```markdown # Implement [ComponentName] Component ## Specifications [Paste the agreed-upon props interface and requirements] ## Implementation Requirements: 1. **Core Functionality** - [List specific features to implement] - Handle all props appropriately - Implement proper state management 2. **Styling** - Use only design tokens from theme - Create responsive styles - Handle all visual states 3. **Accessibility** - Add appropriate ARIA attributes - Ensure keyboard navigation - Include screen reader support 4. **Performance** - Use React.memo if appropriate - Implement useMemo/useCallback where needed - Optimize re-renders 5. **Error Handling** - Validate props - Handle edge cases - Provide meaningful error messages ## Code Structure: - Clear separation of concerns - Extracted helper functions - Proper TypeScript types - Comprehensive JSDoc comments ```` ### AI Prompt: Design Token Application ````markdown # Apply Design Tokens to [ComponentName] ## Component Styling Needs: - Visual states: [default, hover, active, disabled] - Size variations: [small, medium, large] - Theme support: [light, dark] ## Available Tokens: ```typescript // Reference our theme structure theme = { colors: { primary, secondary, text, background }, spacing: { xs, sm, md, lg, xl }, typography: { size, weight, lineHeight }, radii: { sm, md, lg }, shadows: { sm, md, lg } } ```` ## Create styles.ts that: 1. Uses only theme tokens (no hardcoded values) 2. Supports all component variants 3. Handles responsive sizing 4. Includes platform-specific styles if needed 5. Exports typed style functions ## Example Pattern: ```typescript export const getStyles = (props: StyleProps) => { return StyleSheet.create({ container: { backgroundColor: theme.colors[props.variant], padding: theme.spacing[props.size], // etc. } }); }; ``` ```` ### AI Prompt: Accessibility Implementation ```markdown # Add Accessibility to [ComponentName] ## Component Type: [Button/Input/List/etc.] ## Accessibility Requirements: 1. **Screen Reader Support** - Appropriate role - Descriptive labels - State announcements - Value announcements (if applicable) 2. **Keyboard Navigation** - Focus management - Tab order - Keyboard shortcuts (if applicable) 3. **Visual Accessibility** - Focus indicators - Color contrast compliance - Touch target size (min 44x44) ## Platform Considerations: - iOS: VoiceOver specific attributes - Android: TalkBack specific attributes ## Implementation Checklist: - [ ] accessibilityRole - [ ] accessibilityLabel - [ ] accessibilityHint - [ ] accessibilityState - [ ] accessibilityValue - [ ] accessibilityActions - [ ] Focus management hooks Please implement comprehensive accessibility with examples. ```` ## Phase 3: Testing ### AI Prompt: Test Generation ```markdown # Generate Unit Tests for [ComponentName] ## Component Details: [Paste component props interface] ## Test Requirements: 1. **Rendering Tests** - Default props rendering - All prop variations - Conditional rendering 2. **Interaction Tests** - User interactions (press, type, etc.) - Event handler calls - State changes 3. **Props Tests** - Required vs optional props - Prop type validation - Default values 4. **Edge Cases** - Empty/null data - Very long content - Error states - Loading states ## Test Utilities: - @testing-library/react-native - Jest - Custom test utils from test-utils/ ## Coverage Target: 80%+ ``` ````markdown # Integration Tests for [ComponentName] ## Integration Points: - [ ] Theme Provider integration - [ ] Navigation integration - [ ] State management integration - [ ] API integration (if applicable) ## Test Scenarios: 1. **With Providers** ```typescript ```` 2. **With Feature Context** * Test within actual feature usage * Verify data flow * Check side effects 3. **Cross-Component Integration** * Test with related components * Verify communication * Check composed behavior ## Mock Requirements: * Navigation mocks * API mocks * Storage mocks * Platform mocks ```` ```markdown # Accessibility Tests for [ComponentName] ## Test Requirements: 1. **Screen Reader Tests** - Verify all content is announced - Check role accuracy - Test state change announcements - Validate reading order 2. **Keyboard Navigation Tests** - Tab order verification - Focus trap testing (if modal) - Keyboard shortcut functionality - Focus visible indicators 3. **ARIA Compliance** - Required ARIA attributes - Valid ARIA relationships - Live region updates 4. **Interaction Tests** - Touch target size (44x44 minimum) - Gesture alternatives - Time-based interaction alternatives ## Example Test Structure: ```typescript describe('Accessibility', () => { it('announces role correctly', () => { // Test implementation }); it('provides descriptive labels', () => { // Test implementation }); it('manages focus appropriately', () => { // Test implementation }); }); ```` ### AI Prompt: Snapshot Tests ````markdown # Generate Snapshot Tests for [ComponentName] ## Requirements: 1. Create snapshots for all visual variants 2. Include different prop combinations 3. Test responsive sizes 4. Capture loading/error states ## Structure: ```typescript describe('ComponentName Snapshots', () => { describe('Variants', () => { it.each(['primary', 'secondary', 'danger']) ('renders %s variant', (variant) => { // Snapshot test }); }); describe('States', () => { // Loading, disabled, error snapshots }); describe('Sizes', () => { // Small, medium, large snapshots }); }); ```` ## Include: * Platform-specific snapshots if needed * Theme variant snapshots * Complex content scenarios ```` ## Phase 4: Documentation ### AI Prompt: Component Documentation ```markdown # Document [ComponentName] Component ## Create Comprehensive Documentation: ### 1. Overview Section - Component purpose and use cases - When to use vs when not to use - Related components ### 2. Props Documentation ```typescript interface ComponentProps { // Include all props with descriptions } ```` ### 3. Usage Examples * Basic usage * Advanced usage * Common patterns * Integration examples ### 4. Styling Guide * Available variants * Customization options * Theme integration ### 5. Accessibility Guide * Keyboard shortcuts * Screen reader behavior * Best practices ### 6. Performance Considerations * Optimization tips * Common pitfalls * Benchmarks ### 7. Migration Guide (if replacing existing) * Breaking changes * Update steps * Codemod availability Format as MDX with live code examples. ```` ## Best Practices Summary **Maximizing AI Effectiveness** The key to successful AI collaboration is providing clear context, validating output against standards, and using AI as a tool to accelerate development rather than replace critical thinking. ### Quick Reference: Effective Prompts **Planning & Design Prompts** ```markdown # Requirements Definition "Define props and states for a [Component] that [purpose]" # API Design "Design a TypeScript interface for [Component] following React Native patterns" # Architecture Decision "Should [Component] be foundation/pattern/business? It [description]" # Accessibility Planning "What accessibility features does a [Component type] need?" ```` **Implementation Prompts** ```markdown # Component Creation "Create a React Native [Component] with TypeScript and design tokens" # Style Implementation "Apply theme tokens to style [Component] with [variants]" # State Management "Implement [feature] using React hooks and proper memoization" # Error Handling "Add comprehensive error handling to [Component]" ``` **Testing & Documentation Prompts** ```markdown # Test Generation "Write unit tests for [Component] with 80%+ coverage" # Accessibility Testing "Create accessibility tests for [Component] screen reader support" # Documentation "Document [Component] with props table and usage examples" # Performance Testing "Write performance tests for [Component] render optimization" ``` ### Integration with CI/CD Consider using AI tools in your CI/CD pipeline: ```yaml # Example: AI-assisted code review on: pull_request: types: [opened, synchronize] jobs: ai-review: steps: - name: AI Code Review run: | # Check for design token usage # Verify accessibility attributes # Validate TypeScript types # Suggest improvements ``` ## Next Steps 1. **Practice with Simple Components**: Start with foundation components 2. **Build Prompt Library**: Save effective prompts for reuse 3. **Share Team Learnings**: Document what works well 4. **Iterate and Improve**: Refine prompts based on results 5. **Maintain Standards**: Always validate against project guidelines ## Related Documents * [Development Workflow](/docs/ui-development/architecture/development-workflow) - Complete development process * [Component Patterns](/docs/ui-development/development/component-patterns) - Implementation patterns * [Testing Strategies](/docs/ui-development/architecture/development-workflow/testing-strategies) - Testing approaches * [Code Review Guidelines](/docs/development-workflow/code-review) - Review standards # UTA UI Development: Component Patterns URL: https://dev.updatetheapp.com/docs/ui-development/development/component-patterns Documentation: https://dev.updatetheapp.com/docs/ui-development/development/component-patterns Best practices and patterns for building reusable, maintainable React Native components *** title: Component Patterns description: Best practices and patterns for building reusable, maintainable React Native components ---------------------------------------------------------------------------------------------------- import { File, Folder, Files } from 'fumadocs-ui/components/files'; import { Callout } from 'fumadocs-ui/components/callout'; # Component Patterns ## Executive Summary This document outlines standardized patterns for building React Native components in our application. It covers component organization, TypeScript integration, performance optimization, and testing strategies. Following these patterns ensures consistency, maintainability, and optimal performance across the codebase. **Component Development Philosophy** Our component architecture emphasizes: * **Reusability**: Build once, use everywhere * **Type Safety**: Full TypeScript coverage * **Performance**: Optimized for 60fps * **Testability**: Easy to test in isolation * **Accessibility**: Inclusive by default ## Purpose & Scope * **Target Audience**: React Native developers building UI components for the application * **Problems Addressed**: Component reusability, type safety, performance optimization, and testing complexity * **Scope Boundaries**: Covers component architecture, patterns, and best practices but not state management or business logic implementation ## Guide Sections This comprehensive guide is organized into focused sections: ### 📋 [Component Architecture](/docs/ui-development/development/component-patterns/component-architecture) Fundamental patterns and organization strategies: * Directory structure and file organization * Component types (Pure, Container/Presenter, Compound, HOC, Render Props) * Data flow patterns * Architecture diagrams ### 📘 [TypeScript Patterns](/docs/ui-development/development/component-patterns/typescript-patterns) Type-safe component development: * Props interface patterns * Generic components * Discriminated unions * Advanced TypeScript techniques ### ⚡ [Performance Optimization](/docs/ui-development/development/component-patterns/performance-optimization) Building high-performance components: * Memoization strategies * Lazy loading patterns * Virtualization techniques * Bundle size optimization ### 🧪 [Testing Strategies](/docs/ui-development/development/component-patterns/testing-strategies) Comprehensive testing approaches: * Component testing patterns * Hook testing * Visual regression testing * Testing best practices ## Component Organization ### Standard Directory Structure ### File Responsibilities ```typescript // ComponentName.tsx - Main component implementation export const ComponentName: React.FC = ({ prop1, prop2 }) => { // Component logic return ...; }; // styles.ts - Component styles import { StyleSheet } from 'react-native'; export const styles = StyleSheet.create({ container: { flex: 1 }, }); // types.ts - Component types and interfaces export interface ComponentNameProps { prop1: string; prop2?: number; onPress?: () => void; } // index.ts - Barrel export export { ComponentName } from './ComponentName'; export type { ComponentNameProps } from './types'; ``` ## Quick Examples ### Pure Functional Component For a complete example of a pure functional component with all features, see the [Button Reference Implementation](/docs/reference-implementations/ui/foundation/Button). Key patterns demonstrated: * ✅ Pure functional component with TypeScript * ✅ Props interface with proper defaults * ✅ Design token integration * ✅ Accessibility implementation * ✅ Loading and disabled states ### Container/Presenter Pattern ```typescript // Container handles data and logic export const ProductListContainer: React.FC = () => { const { data: products, isLoading, error } = useProducts(); if (isLoading) return ; if (error) return ; return ; }; // Presenter handles UI rendering export const ProductList: React.FC = ({ products }) => { return ( } /> ); }; ``` ## Design Principles ### Core Principles 1. **Single Responsibility**: Each component should do one thing well 2. **Composition over Inheritance**: Build complex UIs from simple components 3. **Props over State**: Prefer controlled components 4. **Type Safety**: Full TypeScript coverage 5. **Performance First**: Optimize for 60fps ### Component Decision Matrix | Pattern | Benefits | Drawbacks | Use When | | ------------------- | ------------------- | -------------- | ---------------------- | | Container/Presenter | Clear separation | More files | Complex state logic | | Compound Components | Flexible API | More complex | Multi-part components | | Render Props | Maximum flexibility | Verbose syntax | Dynamic rendering | | HOCs | Reusable logic | Wrapper hell | Cross-cutting concerns | ## Best Practices Summary **Component Development Checklist** * ✅ Use TypeScript for all components * ✅ Include accessibility props * ✅ Export types alongside components * ✅ Optimize for performance * ✅ Write comprehensive tests * ✅ Document complex patterns * ✅ Follow naming conventions ### Quick Do's and Don'ts * **(Do ✅)** Keep components focused on a single responsibility * **(Do ✅)** Provide sensible default props * **(Do ✅)** Use design tokens for styling * **(Don't ❌)** Put business logic in presentational components * **(Don't ❌)** Use inline styles for complex styling * **(Don't ❌)** Create deeply nested component structures * **(Consider 🤔)** Breaking large components into smaller ones * **(Be Aware ❗)** Of performance implications for list items ## Summary Effective component patterns ensure: 1. **Consistent structure** across the codebase 2. **Reusable and testable** components 3. **Type-safe** implementations 4. **Performance** optimizations 5. **Clear separation** of concerns Following these patterns creates a maintainable, scalable UI architecture. ## Related Documents * [Project Structure](/docs/architecture/project-structure) - Overall project organization * [Three-Layer Architecture](/docs/ui-development/architecture#three-layer-architecture) - Component categorization * [Component Examples](/docs/reference-implementations/ui) - Real-world implementations * [TypeScript Guidelines](/docs/coding-standards/typescript-guidelines) - TypeScript best practices * [Testing Standards](/docs/testing/testing-standards) - Component testing strategies # UTA UI Development: Component Architecture URL: https://dev.updatetheapp.com/docs/ui-development/development/component-patterns/component-architecture Documentation: https://dev.updatetheapp.com/docs/ui-development/development/component-patterns/component-architecture Fundamental patterns and organization strategies for React Native components *** title: Component Architecture description: Fundamental patterns and organization strategies for React Native components ----------------------------------------------------------------------------------------- import { Callout } from 'fumadocs-ui/components/callout'; # Component Architecture ## Overview This section covers the fundamental architectural patterns for building React Native components. Understanding these patterns helps create consistent, maintainable, and scalable component architectures. **Architectural Goals** Our component architecture aims to: * **Separate concerns** between logic and presentation * **Maximize reusability** across different contexts * **Ensure testability** in isolation * **Optimize performance** by default ## Core Components/Architecture Hooks UI --> Props UI --> Styles Props --> State Events --> State State --> UI style UI fill:#bbf,stroke:#333 style Hooks fill:#f9f,stroke:#333 style Props fill:#bfb,stroke:#333 style Styles fill:#fbb,stroke:#333 style State fill:#ffe,stroke:#333 style Events fill:#ddf,stroke:#333 `} /> ## Component Types and Patterns ### 1. Pure Functional Components The default pattern for all components. Pure components are predictable and easy to test. ```typescript // ui/foundation/Button/Button.tsx import React from 'react'; import { TouchableOpacity, Text, ActivityIndicator } from 'react-native'; import { styles } from './styles'; import type { ButtonProps } from './types'; export const Button: React.FC = ({ title, onPress, variant = 'primary', size = 'medium', disabled = false, loading = false, testID, ...rest }) => { const isDisabled = disabled || loading; return ( {loading ? ( ) : ( {title} )} ); }; ``` ### 2. Container/Presentational Pattern |Props| Presenter[Presentational Component] Container --> |Data| Store[State/API] Store --> |Updates| Container style Container fill:#f9f,stroke:#333,stroke-width:2px style Presenter fill:#bbf,stroke:#333,stroke-width:2px style Store fill:#bfb,stroke:#333,stroke-width:2px `} /> #### Container Component (Smart) Handles data fetching, state management, and business logic: ```typescript // features/products/components/ProductList/ProductListContainer.tsx import React from 'react'; import { useProducts } from '@/core/domains/products/hooks'; import { ProductList } from './ProductList'; import { LoadingView, ErrorView } from '@/ui'; export const ProductListContainer: React.FC = () => { const { data: products, isLoading, error, refetch } = useProducts(); if (isLoading) return ; if (error) return ; return ( ); }; ``` #### Presentational Component (Dumb) Focuses purely on UI rendering: ```typescript // features/products/components/ProductList/ProductList.tsx import React from 'react'; import { FlatList, RefreshControl } from 'react-native'; import { ProductCard } from '../ProductCard'; import { EmptyState } from '@/ui'; import type { Product } from '@/core/domains/products/types'; import { styles } from './styles'; interface ProductListProps { products: Product[]; onRefresh?: () => void; refreshing?: boolean; } export const ProductList: React.FC = ({ products, onRefresh, refreshing = false }) => { return ( item.id} renderItem={({ item }) => } contentContainerStyle={styles.container} ItemSeparatorComponent={() => } ListEmptyComponent={} refreshControl={ onRefresh ? ( ) : undefined } /> ); }; ``` ### 3. Compound Component Pattern For complex components with multiple related parts: ```typescript // ui/Card/Card.tsx import React, { createContext, useContext } from 'react'; import { View, Text } from 'react-native'; import { styles } from './styles'; import type { CardProps, CardContextValue } from './types'; // Context for sharing state between compound components const CardContext = createContext(undefined); // Main component with sub-components export const Card: React.FC & { Header: typeof CardHeader; Body: typeof CardBody; Footer: typeof CardFooter; } = ({ children, variant = 'default', elevated = false }) => { const contextValue: CardContextValue = { variant, elevated }; return ( {children} ); }; // Sub-components const CardHeader: React.FC = ({ title, subtitle, action }) => { const context = useContext(CardContext); return ( {title} {subtitle && ( {subtitle} )} {action && {action}} ); }; const CardBody: React.FC = ({ children, padded = true }) => ( {children} ); const CardFooter: React.FC = ({ children, divider = true }) => ( {children} ); // Attach sub-components Card.Header = CardHeader; Card.Body = CardBody; Card.Footer = CardFooter; // Usage example: } /> ); expect(getByText('Click me')).toBeTruthy(); expect(getByTestId('button')).toBeTruthy(); }); it('renders all variants correctly', () => { const variants = ['primary', 'secondary', 'danger'] as const; variants.forEach(variant => { const { getByTestId } = renderWithTheme( ); const button = getByTestId('button'); expect(button).toHaveStyle(expect.objectContaining({ backgroundColor: expect.any(String) })); }); }); it('shows loading indicator when loading', () => { const { getByTestId, queryByText } = renderWithTheme( ); expect(getByTestId('button-loader')).toBeTruthy(); expect(queryByText('Click me')).toBeFalsy(); }); }); describe('Interactions', () => { it('calls onPress when pressed', () => { const onPress = jest.fn(); const { getByTestId } = renderWithTheme( ); fireEvent.press(getByTestId('button')); expect(onPress).toHaveBeenCalledTimes(1); }); it('does not call onPress when disabled', () => { const onPress = jest.fn(); const { getByTestId } = renderWithTheme( ); fireEvent.press(getByTestId('button')); expect(onPress).not.toHaveBeenCalled(); }); }); describe('Accessibility', () => { it('has correct accessibility props', () => { const { getByTestId } = renderWithTheme( ); const button = getByTestId('button'); expect(button).toHaveAccessibilityRole('button'); expect(button).toHaveAccessibilityState({ disabled: false }); }); it('updates accessibility state when disabled', () => { const { getByTestId } = renderWithTheme( ); expect(getByTestId('button')).toHaveAccessibilityState({ disabled: true }); }); }); }); ```
```typescript // src/ui/test-utils/index.tsx import React from 'react'; import { render, RenderOptions } from '@testing-library/react-native'; import { ThemeProvider } from '@/core/shared/styles'; import { SafeAreaProvider } from 'react-native-safe-area-context'; interface AllTheProvidersProps { children: React.ReactNode; } const AllTheProviders: React.FC = ({ children }) => { return ( {children} ); }; const customRender = ( ui: React.ReactElement, options?: Omit ) => render(ui, { wrapper: AllTheProviders, ...options }); // Re-export everything export * from '@testing-library/react-native'; export { customRender as render }; ```
### Pattern Components Testing Pattern components compose foundation components and may have internal state. Testing focuses on: * (Do ✅) Test component composition behavior * (Do ✅) Verify state management works correctly * (Do ✅) Test interaction between child components * (Consider 🤔) Integration tests for complex patterns * (Don't ❌) Test foundation component internals #### Example: Testing a Form Pattern ```typescript // src/ui/patterns/Form/Form.test.tsx import React from 'react'; import { render, fireEvent, waitFor } from '@/ui/test-utils'; import { Form, FormField } from './Form'; describe('Form Pattern', () => { const mockSubmit = jest.fn(); const TestForm = () => (
); beforeEach(() => { mockSubmit.mockClear(); }); it('validates required fields on submit', async () => { const { getByText } = render(); fireEvent.press(getByText('Submit')); await waitFor(() => { expect(getByText('Email is required')).toBeTruthy(); expect(getByText('Password is required')).toBeTruthy(); }); expect(mockSubmit).not.toHaveBeenCalled(); }); it('submits valid form data', async () => { const { getByLabelText, getByText } = render(); fireEvent.changeText(getByLabelText('Email'), 'test@example.com'); fireEvent.changeText(getByLabelText('Password'), 'password123'); fireEvent.press(getByText('Submit')); await waitFor(() => { expect(mockSubmit).toHaveBeenCalledWith({ email: 'test@example.com', password: 'password123' }); }); }); }); ``` ### Business Components Testing Business components integrate with domain logic and external services. Testing strategy: * (Do ✅) Mock external dependencies (API calls, navigation) * (Do ✅) Test loading, error, and success states * (Do ✅) Verify business logic execution * (Do ✅) Test edge cases and error scenarios * (Consider 🤔) Integration tests with real API in CI #### Example: Testing a Product Card ```typescript // src/ui/business/ProductCard/ProductCard.tsx import React from 'react'; import { View, Text } from 'react-native'; import { Button } from '@/ui/foundation/Button'; import { Card } from '@/ui/patterns/Card'; import { useCart } from '@/features/cart/hooks'; import { useProductDetails } from '@/features/products/hooks'; import { formatPrice } from '@shared/utils'; interface ProductCardProps { productId: string; onPress?: () => void; } export const ProductCard: React.FC = ({ productId, onPress }) => { const { data: product, isLoading, error } = useProductDetails(productId); const { addToCart, isAddingToCart } = useCart(); if (isLoading) return ; if (error) return ; if (!product) return null; const handleAddToCart = async () => { try { await addToCart(product); } catch (error) { // Error handling is done in the hook } }; return ( {product.name} {formatPrice(product.price)} ); }; ``` ```typescript // src/ui/business/ProductCard/ProductCard.test.tsx import React from 'react'; import { render, fireEvent, waitFor } from '@/ui/test-utils'; import { ProductCard } from './ProductCard'; import { useCart } from '@/features/cart/hooks'; import { useProductDetails } from '@/features/products/hooks'; // Mock the hooks jest.mock('@/features/cart/hooks'); jest.mock('@/features/products/hooks'); describe('ProductCard', () => { const mockProduct = { id: '1', name: 'Test Product', price: 99.99, imageUrl: 'https://example.com/product.jpg' }; const mockAddToCart = jest.fn(); const mockOnPress = jest.fn(); beforeEach(() => { jest.clearAllMocks(); (useCart as jest.Mock).mockReturnValue({ addToCart: mockAddToCart, isAddingToCart: false }); (useProductDetails as jest.Mock).mockReturnValue({ data: mockProduct, isLoading: false, error: null }); }); describe('Loading State', () => { it('shows skeleton while loading', () => { (useProductDetails as jest.Mock).mockReturnValue({ data: null, isLoading: true, error: null }); const { getByTestId } = render( ); expect(() => getByTestId('card-skeleton')).not.toThrow(); }); }); describe('Error State', () => { it('shows error message on failure', () => { (useProductDetails as jest.Mock).mockReturnValue({ data: null, isLoading: false, error: new Error('Network error') }); const { getByText } = render( ); expect(getByText('Failed to load product')).toBeTruthy(); }); }); describe('Success State', () => { it('displays product information', () => { const { getByTestId } = render( ); expect(getByTestId('product-name')).toHaveTextContent('Test Product'); expect(getByTestId('product-price')).toHaveTextContent('$99.99'); }); it('calls onPress when card is pressed', () => { const { getByTestId } = render( ); fireEvent.press(getByTestId('product-card')); expect(mockOnPress).toHaveBeenCalledTimes(1); }); it('adds product to cart', async () => { const { getByTestId } = render( ); fireEvent.press(getByTestId('add-to-cart')); await waitFor(() => { expect(mockAddToCart).toHaveBeenCalledWith(mockProduct); }); }); it('shows loading state while adding to cart', () => { (useCart as jest.Mock).mockReturnValue({ addToCart: mockAddToCart, isAddingToCart: true }); const { getByTestId } = render( ); expect(getByTestId('add-to-cart-loader')).toBeTruthy(); }); }); }); ``` ```typescript // src/test/mocks/handlers.ts import { rest } from 'msw'; import { API_BASE_URL } from '@config/api'; export const handlers = [ rest.get(`${API_BASE_URL}/products/:id`, (req, res, ctx) => { const { id } = req.params; return res( ctx.json({ id, name: 'Mocked Product', price: 99.99, imageUrl: 'https://example.com/product.jpg' }) ); }), rest.post(`${API_BASE_URL}/cart/items`, (req, res, ctx) => { return res( ctx.json({ success: true, cartId: 'cart-123', itemCount: 1 }) ); }) ]; // src/test/mocks/server.ts import { setupServer } from 'msw/node'; import { handlers } from './handlers'; export const server = setupServer(...handlers); ``` ## Testing Best Practices ### 1. Test Organization #### Group by Feature Organize tests to mirror component structure: ``` __tests__/ ├── foundation/ │ ├── Button.test.tsx │ └── Input.test.tsx ├── patterns/ │ ├── Form.test.tsx │ └── Modal.test.tsx └── business/ ├── ProductCard.test.tsx └── UserProfile.test.tsx ``` #### Use Descriptive Names * Test files: `ComponentName.test.tsx` * Test suites: `describe('ComponentName', ...)` * Test cases: `it('should verb when condition', ...)` #### Follow AAA Pattern ```typescript it('should update count when increment button is pressed', () => { // Arrange const { getByTestId } = render(); // Act fireEvent.press(getByTestId('increment-button')); // Assert expect(getByTestId('count-display')).toHaveTextContent('1'); }); ``` ### 2. Testing Utilities Create reusable testing utilities for common scenarios: ```typescript // src/test/utils/gesture.ts export const swipeLeft = (element: ReactTestInstance) => { fireEvent(element, 'swipeableHandlerStateChange', { nativeEvent: { state: State.ACTIVE } }); }; // src/test/utils/async.ts export const waitForLoadingToFinish = async () => { await waitFor(() => { expect(screen.queryByTestId('loading')).toBeNull(); }); }; ``` ### 3. Snapshot Testing Use snapshots judiciously: * (Do ✅) Snapshot small, stable components * (Do ✅) Review snapshot changes carefully * (Don't ❌) Snapshot entire screens * (Don't ❌) Ignore snapshot updates ```typescript describe('Button Snapshots', () => { it('matches snapshot for primary variant', () => { const tree = render( ).toJSON(); expect(tree).toMatchSnapshot(); }); }); ``` ## Performance Testing ### Measuring Component Performance ```typescript // src/ui/foundation/List/List.perf.test.tsx import React from 'react'; import { measurePerformance } from '@test/utils/performance'; import { List } from './List'; describe('List Performance', () => { it('renders 1000 items within performance budget', async () => { const items = Array.from({ length: 1000 }, (_, i) => ({ id: `item-${i}`, title: `Item ${i}` })); const { renderTime, rerenderTime } = await measurePerformance( {item.title}} /> ); expect(renderTime).toBeLessThan(100); // Initial render < 100ms expect(rerenderTime).toBeLessThan(50); // Re-render < 50ms }); }); ``` ## Common Testing Patterns ### Testing Hooks ```typescript // src/features/auth/hooks/useAuth.test.ts import { renderHook, act } from '@testing-library/react-hooks'; import { useAuth } from './useAuth'; import { AuthProvider } from '../providers/AuthProvider'; describe('useAuth', () => { const wrapper = ({ children }: { children: React.ReactNode }) => ( {children} ); it('logs in user successfully', async () => { const { result } = renderHook(() => useAuth(), { wrapper }); await act(async () => { await result.current.login('user@example.com', 'password'); }); expect(result.current.isAuthenticated).toBe(true); expect(result.current.user).toEqual( expect.objectContaining({ email: 'user@example.com' }) ); }); }); ``` ### Testing Navigation ```typescript // src/features/navigation/Navigation.test.tsx import React from 'react'; import { NavigationContainer } from '@react-navigation/native'; import { render, fireEvent } from '@testing-library/react-native'; import { AppNavigator } from './AppNavigator'; const TestNavigator = () => ( ); describe('Navigation', () => { it('navigates to product details on card press', () => { const { getByTestId } = render(); fireEvent.press(getByTestId('product-card-1')); expect(getByTestId('product-details-screen')).toBeTruthy(); }); }); ``` ## Testing Checklist Before considering your component tests complete: * [ ] All props are tested * [ ] User interactions work correctly * [ ] Loading states are handled * [ ] Error states show appropriate UI * [ ] Accessibility props are verified * [ ] Edge cases are covered * [ ] Performance is within budget * [ ] Mocks are properly cleaned up ## Common Pitfalls 1. **Over-mocking**: Don't mock everything - keep integration points when valuable 2. **Testing Implementation**: Focus on behavior, not how it's achieved 3. **Ignoring Async**: Always handle async operations with proper waitFor 4. **Missing Cleanup**: Clean up timers, mocks, and subscriptions ## Related Documents * [Component Patterns](/docs/ui-development/development/component-patterns) - Learn about component design * [Performance Guide](/docs/ui-development/development/performance-guide) - Optimize component performance * [TypeScript Patterns](/docs/guides/typescript-react-patterns) - Type-safe testing approaches * [Error Handling](/docs/guides/error-handling) - Testing error scenarios # UTA UI Development: Brand Assets Overview URL: https://dev.updatetheapp.com/docs/ui-development/assets/assets-management Documentation: https://dev.updatetheapp.com/docs/ui-development/assets/assets-management Comprehensive guide for handling brand assets, fonts, and animations in React Native applications with clear decision frameworks and best practices *** title: "Brand Assets Overview" description: "Comprehensive guide for handling brand assets, fonts, and animations in React Native applications with clear decision frameworks and best practices" ------------------------------------------------------------------------------------------------------------------------------------------------------------------ import { Card, Cards } from 'fumadocs-ui/components/card'; import { Callout } from 'fumadocs-ui/components/callout'; # Brand Assets Overview ## What This Guide Covers This comprehensive guide provides best practices for managing **brand assets**, **fonts**, **animations**, and other static assets in React Native applications. Our approach emphasizes performance optimization, proper organization, and maintainable workflows. **Important Scope Distinction**: This guide covers brand assets, illustrations, fonts, and animations. For **UI icons** (navigation, actions, status), see the [Icon Management Guide](/docs/ui-development/assets/icon-management). ## Asset Categories ### Brand Assets (This Guide) * **Logos**: Company, product, partner logos * **Illustrations**: Onboarding graphics, empty states, hero images * **Complex Graphics**: Marketing materials, detailed graphics * **Backgrounds**: Splash screens, hero banners, decorative images * **Screenshots**: App previews, feature showcases ### UI Icons (Icon Management Guide) * **Navigation Icons**: Arrows, menu, close, back * **Action Icons**: Edit, delete, save, search, share * **Status Icons**: Check, error, warning, info, loading * **Social Icons**: Platform icons used systematically ## Purpose & Scope * **Target Audience**: React Native developers working with brand assets, fonts, animations, and complex graphics * **Problems Addressed**: Asset optimization, bundle size management, multi-resolution handling, performance issues * **Scope Boundaries**: Covers static brand asset management but excludes UI icons and dynamic content from APIs ## Asset Management Guides ## Quick Decision Framework ### When to Use This Guide vs Icon Management | Asset Type | Use This Guide | Use Icon Management | Rationale | | ----------------------- | -------------- | ------------------- | -------------------------------- | | Company Logo | ✅ | ❌ | Brand asset, not UI element | | Navigation Arrow | ❌ | ✅ | UI icon, systematic usage | | Onboarding Illustration | ✅ | ❌ | Complex graphic, brand-specific | | Status Check Icon | ❌ | ✅ | UI icon, repeated usage | | Hero Banner Photo | ✅ | ❌ | Brand asset, marketing content | | Social Media Icon | ❌ | ✅ | UI icon, systematic in interface | ### Asset Format Decision Matrix | Asset Type | SVG | PNG | JPG | Recommendation | | -------------------- | --- | --- | --- | ------------------------------ | | Company Logo | ✅ | 🤔 | ❌ | **SVG** - Perfect scalability | | Product Logo | ✅ | 🤔 | ❌ | **SVG** - Brand consistency | | Simple Illustration | ✅ | 🤔 | ❌ | **SVG** - Small size, scalable | | Complex Illustration | 🤔 | ✅ | ✅ | **PNG/JPG** - Better rendering | | Photo Background | ❌ | 🤔 | ✅ | **JPG** - Optimized for photos | | App Screenshots | ❌ | ✅ | 🤔 | **PNG** - Lossless UI elements | ## Asset Organization Structure ``` assets/ # Project root level ├── brand/ # Brand assets (this guide) │ ├── logos/ # Company/product logos │ ├── illustrations/ # Marketing/onboarding graphics │ └── backgrounds/ # Hero banners, splash screens ├── fonts/ # Custom font files (this guide) ├── animations/ # Lottie and other animations (this guide) └── sounds/ # Audio assets (this guide) ``` **Why Project Root?** Placing assets at project root (same level as `src/`) provides cleaner import paths, Metro bundler compatibility, and follows React Native community conventions. ## Brand Asset Implementation Scenarios ### "I need to add a company or product logo" → Follow [Image Optimization Guide](/docs/ui-development/assets/assets-management/image-optimization-guide) to choose between SVG (scalable) vs PNG (multi-resolution) based on logo complexity ### "I want to add custom fonts for branding" → Use [Font Management Guide](/docs/ui-development/assets/assets-management/font-management-guide) for font loading strategies, fallbacks, and performance optimization ### "I need to add hero images or marketing graphics" → Review [Image Optimization Guide](/docs/ui-development/assets/assets-management/image-optimization-guide) for JPG vs PNG decisions and multi-resolution handling ### "I have large image assets affecting app performance" → Check [Performance Optimization](/docs/ui-development/assets/assets-management/performance-optimization) for bundle size analysis and optimization techniques ## Key Principles 1. **Performance First**: Always optimize assets before adding to project 2. **Format Selection**: Choose the right format (SVG for logos, PNG for complex graphics, JPG for photos) 3. **Resolution Independence**: Provide multiple resolutions (@1x, @2x, @3x) for PNG/JPG assets 4. **Bundle Size Awareness**: Monitor impact on app bundle size 5. **Platform Compatibility**: Ensure assets work across iOS and Android ## Getting Started 1. **Start Here**: Review the [Image Optimization Guide](/docs/ui-development/assets/assets-management/image-optimization-guide) to understand format decisions 2. **Set Up Fonts**: Follow [Font Management Guide](/docs/ui-development/assets/assets-management/font-management-guide) for custom typography 3. **Add Animations**: Use [Animation Assets Guide](/docs/ui-development/assets/assets-management/animation-assets-guide) for motion graphics 4. **Optimize Performance**: Apply [Performance Optimization](/docs/ui-development/assets/assets-management/performance-optimization) techniques ## Related Documentation * [Icon Management Guide](/docs/ui-development/assets/icon-management) - For UI icons and systematic iconography * [Project Structure Guide](/docs/architecture/project-structure) - For overall project organization * [Performance Guide](/docs/performance) - For general performance optimization * [Build Configuration](/docs/deployment/build-configuration) - For bundling and deployment *** **Remember**: Proper asset management directly impacts app performance, user experience, and development efficiency. Take time to understand format decisions and optimization strategies before implementing. # UTA UI Development: Image Optimization Guide URL: https://dev.updatetheapp.com/docs/ui-development/assets/assets-management/image-optimization-guide Documentation: https://dev.updatetheapp.com/docs/ui-development/assets/assets-management/image-optimization-guide Comprehensive guide for optimizing images in React Native applications including format decisions, multi-resolution handling, and automated workflows *** title: "Image Optimization Guide" description: "Comprehensive guide for optimizing images in React Native applications including format decisions, multi-resolution handling, and automated workflows" -------------------------------------------------------------------------------------------------------------------------------------------------------------------- import { Callout } from 'fumadocs-ui/components/callout'; import { Step, Steps } from 'fumadocs-ui/components/steps'; import { Tabs, Tab } from 'fumadocs-ui/components/tabs'; # Image Optimization Guide ## Overview This guide provides comprehensive strategies for optimizing brand assets, illustrations, and complex graphics in React Native applications. We focus on format selection, multi-resolution handling, and automated optimization workflows. ## SVG vs Image Format Decision Framework ### When to Use SVG for Brand Assets **(Do ✅) Use SVG for:** * **Logos**: Perfect scalability, crisp at any size, small file size * **Simple Illustrations**: Vector-based graphics with solid colors * **Icons in Brand Assets**: Brand-specific iconography * **Geometric Graphics**: Shapes, patterns, simple designs **Benefits:** * Resolution independent (perfect on all screen densities) * Smaller file sizes for simple graphics * Easy to programmatically modify colors/styling * Crisp rendering at any scale ### When to Use PNG/JPG for Brand Assets **(Do ✅) Use PNG/JPG for:** * **Photographic Content**: Real photos, complex textures * **Complex Illustrations**: Detailed artwork with gradients, shadows, effects * **Screenshots**: App screenshots, UI mockups * **Backgrounds**: Complex gradients, photographic backgrounds **Benefits:** * Better for photographic/complex content * Faster rendering for complex graphics * No dependency on react-native-svg * Better browser/platform compatibility ### Decision Matrix | Asset Type | SVG | PNG | JPG | Recommendation | | --------------------- | --- | --- | --- | ------------------------------ | | Company Logo | ✅ | 🤔 | ❌ | **SVG** - Perfect scalability | | Product Logo | ✅ | 🤔 | ❌ | **SVG** - Brand consistency | | Simple Illustration | ✅ | 🤔 | ❌ | **SVG** - Small size, scalable | | Complex Illustration | 🤔 | ✅ | ✅ | **PNG/JPG** - Better rendering | | Photo Background | ❌ | 🤔 | ✅ | **JPG** - Optimized for photos | | App Screenshots | ❌ | ✅ | 🤔 | **PNG** - Lossless UI elements | | Hero Banner (photo) | ❌ | 🤔 | ✅ | **JPG** - Photographic content | | Hero Banner (graphic) | ✅ | 🤔 | ❌ | **SVG** - Vector graphics | ## Multi-Resolution Image Management ### Understanding Screen Densities **Screen Density Basics**: React Native automatically selects the appropriate image resolution based on device pixel density. Provide multiple resolutions to ensure crisp rendering across all devices. | Density | Scale Factor | Typical Devices | File Suffix | | ----------- | ------------ | ------------------- | -------------- | | MDPI | 1x | Lower-end Android | `image.png` | | HDPI/Retina | 2x | Most modern devices | `image@2x.png` | | XHDPI | 3x | High-end devices | `image@3x.png` | ### Multi-Resolution Implementation #### PNG/JPG Multi-Resolution Setup ``` assets/brand/logos/ ├── partner-logo.png # 1x resolution (base) ├── partner-logo@2x.png # 2x resolution └── partner-logo@3x.png # 3x resolution ``` **Best Practices:** * **(Do ✅)** Start with highest resolution and scale down * **(Do ✅)** Maintain aspect ratio across all resolutions * **(Do ✅)** Use consistent naming with @2x, @3x suffixes * **(Don't ❌)** Use only single resolution images #### SVG Assets (Resolution Independent) ``` assets/brand/logos/ ├── company-logo.svg # Single file, all resolutions └── product-logo.svg # Scales automatically ``` **Advantages:** * Single file serves all screen densities * Perfect scaling at any size * Smaller bundle size for simple graphics * Easy to modify colors programmatically #### Usage in Code ```typescript // Multi-resolution PNG/JPG (React Native auto-selects) // SVG Component (resolution independent) import CompanyLogo from '@/assets/brand/logos/CompanyLogo'; // Assets constant approach import { Images } from '@/core/constants/images'; ``` ## Image Optimization Workflows ### Manual Optimization Tools #### Online Optimization Tools * **TinyPNG**: [https://tinypng.com](https://tinypng.com) - PNG/JPG compression * **SVGO**: [https://jakearchibald.github.io/svgomg/](https://jakearchibald.github.io/svgomg/) - SVG optimization * **Squoosh**: [https://squoosh.app](https://squoosh.app) - Advanced image compression **Privacy Consideration**: Be aware of privacy implications when using online tools with sensitive brand assets. Consider on-premise solutions for confidential materials. #### Desktop Applications * **ImageOptim** (macOS): [https://imageoptim.com](https://imageoptim.com) * **FileOptimizer** (Windows): Free lossless file compressor * **GIMP**: Free alternative to Photoshop with export optimization **Benefits:** * No internet required * Batch processing capabilities * Privacy protection for sensitive assets #### Command Line Tools ```bash # ImageMagick - Resize and optimize convert input.png -quality 85 -resize 200x200 output.png # WebP conversion cwebp -q 80 input.png -o output.webp # SVGO - SVG optimization npx svgo input.svg -o output.svg # Batch processing with ImageMagick mogrify -quality 85 -resize 300x300 *.png ``` ### Automated Optimization Pipeline #### Set Up Package Scripts Add optimization scripts to your package.json ```json { "scripts": { "optimize:images": "imagemin assets/brand/**/*.{jpg,png} --out-dir=assets/brand/optimized", "optimize:svg": "svgo assets/brand/**/*.svg", "generate:webp": "node scripts/generate-webp.js", "optimize:all": "npm run optimize:images && npm run optimize:svg" } } ``` #### Install Optimization Dependencies Add necessary packages for automated optimization ```bash npm install --save-dev imagemin imagemin-pngquant imagemin-mozjpeg svgo ``` #### Create Optimization Script Set up automated processing for new assets ```javascript // scripts/optimize-assets.js const imagemin = require('imagemin'); const imageminPngquant = require('imagemin-pngquant'); const imageminMozjpeg = require('imagemin-mozjpeg'); async function optimizeImages() { await imagemin(['assets/brand/**/*.{jpg,png}'], { destination: 'assets/brand/optimized', plugins: [ imageminMozjpeg({ quality: 85 }), imageminPngquant({ quality: [0.6, 0.8] }) ] }); console.log('Images optimized!'); } optimizeImages(); ``` #### Set Up Pre-commit Hook Automatically optimize assets before committing ```json // package.json { "husky": { "hooks": { "pre-commit": "lint-staged" } }, "lint-staged": { "assets/**/*.{png,jpg,jpeg}": [ "npm run optimize:images", "git add" ], "assets/**/*.svg": [ "npm run optimize:svg", "git add" ] } } ``` ## Asset Constants Pattern ### Centralized Asset Management ```typescript // core/constants/images.ts export const Images = { brand: { logos: { // SVG logos (recommended for scalability) company: require('@/assets/brand/logos/company-logo.svg'), product: require('@/assets/brand/logos/product-logo.svg'), // PNG logos (when SVG not available) partner: require('@/assets/brand/logos/partner-logo.png'), }, illustrations: { onboarding: { // SVG for simple illustrations welcomeSimple: require('@/assets/brand/illustrations/onboarding/welcome-simple.svg'), // PNG for complex illustrations featuresComplex: require('@/assets/brand/illustrations/onboarding/features-complex.png'), }, emptyStates: { // SVG for simple graphics noData: require('@/assets/brand/illustrations/empty-states/no-data.svg'), noNetwork: require('@/assets/brand/illustrations/empty-states/no-network.svg'), }, }, backgrounds: { // JPG for photographic backgrounds splashPhoto: require('@/assets/brand/backgrounds/splash-photo.jpg'), heroBanner: require('@/assets/brand/backgrounds/hero-banner.jpg'), // SVG for geometric/gradient backgrounds gradientBg: require('@/assets/brand/backgrounds/gradient-bg.svg'), }, }, }; ``` ### Usage Examples #### SVG Component Usage ```typescript // SVG Components (for logos and simple graphics) import CompanyLogo from '@/assets/brand/logos/CompanyLogo'; export const Header = () => ( ); ``` #### Image Component Usage ```typescript import { Images } from '@/core/constants/images'; export const HeroBanner = () => ( ); ``` #### TypeScript Type Definitions ```typescript // types/assets.ts export interface ImageAssets { brand: { logos: { company: any; product: any; partner: any; }; illustrations: { onboarding: { welcomeSimple: any; featuresComplex: any; }; emptyStates: { noData: any; noNetwork: any; }; }; backgrounds: { splashPhoto: any; heroBanner: any; gradientBg: any; }; }; } ``` ## Common Optimization Issues ### Issue 1: Oversized Images **Problem**: Using large images for small display areas **Impact**: Increased bundle size, slower loading, memory issues ```typescript // ❌ BAD: Using unoptimized images // 5MB image displayed at 200x200 // ✅ GOOD: Using optimized assets // 150KB image sized appropriately ``` ### Issue 2: Missing Multi-Resolution Assets **Problem**: Only providing single resolution images **Impact**: Blurry images on high-density screens ```typescript // ❌ BAD: Single resolution only assets/brand/logos/logo.png // ✅ GOOD: Multiple resolutions assets/brand/logos/logo.png # 1x assets/brand/logos/logo@2x.png # 2x assets/brand/logos/logo@3x.png # 3x ``` ### Issue 3: Wrong Format Selection **Problem**: Using PNG for photographic content or JPG for logos **Impact**: Larger file sizes, quality issues ```typescript // ❌ BAD: Wrong format choices company-logo.jpg // Should be SVG or PNG photo-background.png // Should be JPG complex-illustration.svg // Should be PNG for complex artwork // ✅ GOOD: Correct format choices company-logo.svg // Vector logo photo-background.jpg // Photographic content complex-illustration.png // Complex raster artwork ``` ## Performance Best Practices ### Bundle Size Management * **(Do ✅)** Monitor bundle size impact when adding new assets * **(Do ✅)** Use bundle analyzer to identify large assets * **(Do ✅)** Implement lazy loading for large assets * **(Consider 🤔)** Using remote assets for very large images ### Memory Management * **(Do ✅)** Specify image dimensions to prevent layout shifts * **(Do ✅)** Use appropriate `resizeMode` for your use case * **(Do ✅)** Implement image caching with libraries like FastImage * **(Don't ❌)** Load large images unnecessarily ### Loading Strategies ```typescript // ✅ GOOD: Lazy loading with FastImage import FastImage from 'react-native-fast-image'; export const OptimizedImage = ({ source, style }) => ( ); ``` ## Asset Naming Conventions ### Recommended Naming Pattern ``` ✅ GOOD: - logo-company-primary.svg - logo-product-secondary.svg - bg-onboarding-welcome.jpg - bg-onboarding-welcome@2x.jpg - bg-onboarding-welcome@3x.jpg - illustration-empty-state-no-data.svg ❌ BAD: - company_logo.png - CompanyLogo.png - logo-2x.png (missing descriptive name) - onboarding_bg_1.jpg (unclear purpose) ``` ### Naming Conventions * Use **kebab-case** for all filenames * Include **asset type prefix** (logo-, bg-, illustration-) * Add **descriptive context** (company, product, onboarding) * Use **semantic suffixes** (@2x, @3x for multi-resolution) ## Testing Asset Optimization ```typescript // __tests__/assets.test.ts describe('Asset Optimization', () => { it('should load all required brand assets', () => { Object.values(Images.brand.logos).forEach(image => { expect(image).toBeDefined(); }); }); it('should have optimized file sizes', async () => { // Test that images are under reasonable size limits const logoSize = await getAssetSize(Images.brand.logos.company); expect(logoSize).toBeLessThan(50000); // 50KB limit for logos }); it('should provide multi-resolution assets', () => { // Verify that @2x and @3x variants exist for PNG/JPG const partnerLogo = Images.brand.logos.partner; expect(partnerLogo).toBeDefined(); // Additional checks for resolution variants }); }); ``` ## Related Documentation * [Font Management Guide](/docs/ui-development/assets/assets-management/font-management-guide) - Custom typography integration * [Animation Assets Guide](/docs/ui-development/assets/assets-management/animation-assets-guide) - Motion graphics optimization * [Performance Optimization](/docs/ui-development/assets/assets-management/performance-optimization) - Bundle size and caching * [Icon Management Guide](/docs/ui-development/assets/icon-management) - UI icon system implementation # UTA UI Development: Font Management Guide URL: https://dev.updatetheapp.com/docs/ui-development/assets/assets-management/font-management-guide Documentation: https://dev.updatetheapp.com/docs/ui-development/assets/assets-management/font-management-guide Comprehensive guide for managing custom fonts in React Native applications including loading strategies, fallbacks, and performance optimization *** title: "Font Management Guide" description: "Comprehensive guide for managing custom fonts in React Native applications including loading strategies, fallbacks, and performance optimization" --------------------------------------------------------------------------------------------------------------------------------------------------------------- import { Callout } from 'fumadocs-ui/components/callout'; import { Step, Steps } from 'fumadocs-ui/components/steps'; import { Tabs, Tab } from 'fumadocs-ui/components/tabs'; # Font Management Guide ## Overview This guide provides best practices for **loading and configuring custom fonts** in React Native applications, including loading strategies, fallback mechanisms, performance optimization, and platform-specific considerations. **Scope**: This guide focuses on font **asset loading and technical setup**. For typography scales, font sizes, and design system integration, see [Theme Management](/docs/ui-development/theme-management). ## Font Loading Strategies ### Expo Font Loading #### Basic Font Loading with Expo ```typescript // App.tsx import * as Font from 'expo-font'; import { useState, useEffect } from 'react'; const loadFonts = async () => { await Font.loadAsync({ 'Roboto-Regular': require('@/assets/fonts/Roboto-Regular.ttf'), 'Roboto-Bold': require('@/assets/fonts/Roboto-Bold.ttf'), 'Roboto-Light': require('@/assets/fonts/Roboto-Light.ttf'), 'CustomIcons': require('@/assets/fonts/CustomIcons.ttf'), }); }; export default function App() { const [fontsLoaded, setFontsLoaded] = useState(false); useEffect(() => { loadFonts().then(() => setFontsLoaded(true)); }, []); if (!fontsLoaded) { return ; } return ; } ``` #### Advanced Font Loading with Caching ```typescript // core/services/fontService.ts import * as Font from 'expo-font'; import AsyncStorage from '@react-native-async-storage/async-storage'; const FONT_CACHE_KEY = 'fonts_loaded_v1'; export const FontService = { async loadFonts(): Promise { try { // Check if fonts were previously loaded const cached = await AsyncStorage.getItem(FONT_CACHE_KEY); if (!cached) { await Font.loadAsync({ 'Inter-Regular': require('@/assets/fonts/Inter-Regular.ttf'), 'Inter-Medium': require('@/assets/fonts/Inter-Medium.ttf'), 'Inter-SemiBold': require('@/assets/fonts/Inter-SemiBold.ttf'), 'Inter-Bold': require('@/assets/fonts/Inter-Bold.ttf'), }); // Cache successful load await AsyncStorage.setItem(FONT_CACHE_KEY, 'true'); } } catch (error) { console.warn('Font loading failed:', error); // Continue with system fonts } }, isLoaded(fontFamily: string): boolean { return Font.isLoaded(fontFamily); } }; ``` #### Error Handling and Fallbacks ```typescript // hooks/useFonts.ts import { useState, useEffect } from 'react'; import { FontService } from '@/core/services/fontService'; export const useFonts = () => { const [fontsLoaded, setFontsLoaded] = useState(false); const [fontError, setFontError] = useState(null); useEffect(() => { const loadFonts = async () => { try { await FontService.loadFonts(); setFontsLoaded(true); } catch (error) { setFontError(error.message); setFontsLoaded(true); // Continue with fallback fonts } }; loadFonts(); }, []); return { fontsLoaded, fontError }; }; ``` ### React Native CLI Font Setup #### Place Font Files Copy font files to the appropriate platform directories ``` # iOS ios/YourApp/fonts/ ├── Inter-Regular.ttf ├── Inter-Bold.ttf └── Inter-SemiBold.ttf # Android android/app/src/main/assets/fonts/ ├── Inter-Regular.ttf ├── Inter-Bold.ttf └── Inter-SemiBold.ttf ``` #### Configure iOS Info.plist Add font declarations to iOS configuration ```xml UIAppFonts Inter-Regular.ttf Inter-Bold.ttf Inter-SemiBold.ttf ``` #### Link Fonts (if using React Native CLI) Run linking command for automatic setup ```bash # React Native 0.60+ (auto-linking) npx react-native run-ios npx react-native run-android # Older versions npx react-native link ``` #### Test Font Loading Verify fonts are available in your app ```typescript // Test component const FontTest = () => ( Regular Text Bold Text SemiBold Text ); ``` ## Font Organization and Constants ### Font Constants Pattern **Typography Scale Integration**: For font sizes, weights, and design system typography, use the centralized theme management system. See [Theme Management Implementation](/docs/ui-development/theme-management/implementation) for the canonical typography definitions. ```typescript // core/constants/fonts.ts export const Fonts = { // Primary font family primary: { regular: 'Inter-Regular', medium: 'Inter-Medium', semiBold: 'Inter-SemiBold', bold: 'Inter-Bold', }, // Secondary font family (if needed) secondary: { regular: 'Roboto-Regular', medium: 'Roboto-Medium', bold: 'Roboto-Bold', }, // Icon fonts icons: { custom: 'CustomIcons', material: 'MaterialIcons', }, // System font fallbacks system: { ios: 'San Francisco', android: 'Roboto', default: 'System', } }; ``` **Design System Integration**: Instead of defining font sizes and weights here, import them from the theme management system to maintain consistency across your application. ```typescript // Use theme management for typography scale import { useTheme } from '@core/shared/styles'; // Example: Integrating with theme management const ProductCard = () => { const theme = useTheme(); return ( Typography using design system ); }; ``` ### Typography Component Integration **Recommended Approach**: Use the centralized Typography component from the theme management system instead of creating custom typography components. This ensures consistency with your design system. **Naming Standards**: Follow our [File Naming Conventions](/docs/coding-standards/file-naming-conventions) and [Project Structure](/docs/architecture/project-structure) when creating components. Use concise, descriptive names that align with React community practices. ```typescript // Recommended: Use theme management Typography import { useTheme } from '@core/shared/styles'; const ProductDetail = () => { const theme = useTheme(); return ( Product Name Product description Price details ); }; ``` If you need custom typography components that integrate with font loading, combine font management with theme system: ```typescript // ui/foundation/Text/Text.tsx import React from 'react'; import { Text as RNText, TextProps, Platform } from 'react-native'; import { useTheme } from '@core/shared/styles'; import { useFonts } from '@/hooks/useFonts'; import { Fonts } from '@/core/constants/fonts'; interface CustomTextProps extends TextProps { variant?: 'h1' | 'h2' | 'h3' | 'body' | 'bodySmall' | 'button' | 'caption'; } export const Text: React.FC = ({ variant = 'body', style, children, ...props }) => { const theme = useTheme(); const { fontsLoaded } = useFonts(); // Get theme text styles const baseStyle = theme.textStyles[variant] || theme.textStyles.body; // Apply fallback font if custom fonts aren't loaded const textStyle = fontsLoaded ? baseStyle : { ...baseStyle, fontFamily: Platform.select({ ios: Fonts.system.ios, android: Fonts.system.android, default: Fonts.system.default, }), }; return ( {children} ); }; ``` ## Platform-Specific Considerations ### iOS Font Configuration #### Configuring iOS Info.plist ```xml UIAppFonts Inter-Regular.ttf Inter-Medium.ttf Inter-SemiBold.ttf Inter-Bold.ttf CustomIcons.ttf ``` **Font File Names**: Use the exact filename (including extension) as it appears in your bundle, not the PostScript name. #### Finding Font Display Names ```typescript // Development helper to find font names const FontDebugger = () => { useEffect(() => { if (__DEV__) { // iOS: List all available fonts const fontFamilies = require('react-native').Platform.select({ ios: () => { const { NativeModules } = require('react-native'); return NativeModules.RNFontList?.getFontList() || []; }, default: () => [] })(); console.log('Available fonts:', fontFamilies); } }, []); return null; }; ``` #### iOS Font Weight Mapping ```typescript // Platform-specific font weight handling const getIOSFontFamily = (family: string, weight: string) => { const weightMap = { '300': `${family}-Light`, '400': `${family}-Regular`, '500': `${family}-Medium`, '600': `${family}-SemiBold`, '700': `${family}-Bold`, '800': `${family}-ExtraBold`, }; return weightMap[weight] || `${family}-Regular`; }; ``` ### Android Font Configuration ```typescript // Platform-specific font handling for Android const getAndroidFontFamily = (family: string, weight: string) => { // Android can use fontWeight property with single family return { fontFamily: family, fontWeight: weight, }; }; // Platform-agnostic font style helper export const getFontStyle = (family: string, weight: string) => { return Platform.select({ ios: { fontFamily: getIOSFontFamily(family, weight), }, android: getAndroidFontFamily(family, weight), default: { fontFamily: family, fontWeight: weight, }, }); }; ``` ## Font Fallback Strategies ### Graceful Degradation ```typescript // core/utils/fontFallback.ts import { Platform } from 'react-native'; import { Fonts } from '@/core/constants/fonts'; export const FontFallback = { getFont( preferredFont: string, fallbackFont?: string ): string { // Check if font is loaded (Expo only) if (typeof Font !== 'undefined' && Font.isLoaded?.(preferredFont)) { return preferredFont; } // Use provided fallback if (fallbackFont) { return fallbackFont; } // Use system font as last resort return Platform.select({ ios: Fonts.system.ios, android: Fonts.system.android, default: Fonts.system.default, }); }, createFontStack(fonts: string[]): string { // Create font family stack for CSS-like fallback return fonts.join(', '); } }; // Usage in styles const styles = StyleSheet.create({ title: { fontFamily: FontFallback.getFont('Inter-Bold', 'System'), fontSize: 24, }, body: { fontFamily: FontFallback.createFontStack([ 'Inter-Regular', 'San Francisco', 'Roboto', 'System' ]), fontSize: 16, }, }); ``` ### Loading State Management ```typescript // components/FontAwareText.tsx import React from 'react'; import { Text, TextProps } from 'react-native'; import { useFonts } from '@/hooks/useFonts'; import { FontFallback } from '@/core/utils/fontFallback'; interface FontAwareTextProps extends TextProps { fontFamily?: string; fallbackFont?: string; } export const FontAwareText: React.FC = ({ fontFamily = 'Inter-Regular', fallbackFont, style, children, ...props }) => { const { fontsLoaded } = useFonts(); const resolvedFontFamily = fontsLoaded ? fontFamily : FontFallback.getFont(fontFamily, fallbackFont); return ( {children} ); }; ``` ## Performance Optimization ### Font Loading Performance **Performance Impact**: Loading many custom fonts can significantly impact app startup time. Follow these strategies to minimize the impact. #### Essential vs Optional Fonts ```typescript // Prioritize critical fonts for immediate loading const FontLoader = { async loadEssentialFonts() { // Load only fonts needed for initial screen await Font.loadAsync({ 'Inter-Regular': require('@/assets/fonts/Inter-Regular.ttf'), 'Inter-Bold': require('@/assets/fonts/Inter-Bold.ttf'), }); }, async loadOptionalFonts() { // Load additional fonts in background await Font.loadAsync({ 'Inter-Light': require('@/assets/fonts/Inter-Light.ttf'), 'Inter-SemiBold': require('@/assets/fonts/Inter-SemiBold.ttf'), 'CustomIcons': require('@/assets/fonts/CustomIcons.ttf'), }); } }; // In your app export default function App() { const [essentialFontsLoaded, setEssentialFontsLoaded] = useState(false); useEffect(() => { FontLoader.loadEssentialFonts() .then(() => setEssentialFontsLoaded(true)); // Load optional fonts in background FontLoader.loadOptionalFonts(); }, []); if (!essentialFontsLoaded) { return ; } return ; } ``` ### Bundle Size Optimization #### Font Subsetting ```typescript // Only include characters you need const FontOptimization = { // Analyze text usage to determine required character set analyzeCharacterUsage(texts: string[]): Set { const chars = new Set(); texts.forEach(text => { for (const char of text) { chars.add(char); } }); return chars; }, // Generate subset specification for font optimization tools generateSubsetSpec(chars: Set): string { return Array.from(chars).join(''); } }; ``` #### Font Format Optimization ```bash # Use tools like fonttools to create optimized font files pip install fonttools # Subset font to specific characters pyftsubset Inter-Regular.ttf \ --text="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789" \ --output-file=Inter-Regular-subset.ttf # Convert to WOFF2 for web (if using web components) fonttools ttLib.woff2 compress Inter-Regular.ttf ``` ## Custom Icon Fonts ### Creating Icon Fonts #### Design Icons Create SVG icons following these guidelines: * Use consistent artboard size (24x24px recommended) * Maintain uniform stroke width (2px) * Use simple paths without complex effects * Name files descriptively (arrow-left.svg, user-profile.svg) #### Generate Icon Font Use tools like IcoMoon or Fontello to create icon fonts ```bash # Using fontello-cli npm install -g fontello-cli # Upload SVGs and download font fontello-cli install --config fontello-config.json ``` #### Integrate Icon Font Add the generated font to your React Native app ```typescript // core/constants/icons.ts export const IconFont = { family: 'CustomIcons', glyphs: { home: '\ue900', profile: '\ue901', settings: '\ue902', search: '\ue903', } }; // components/IconText.tsx export const IconText = ({ icon, size = 24, color = '#000' }) => ( {IconFont.glyphs[icon]} ); ``` ## Testing Font Implementation ### Font Loading Tests ```typescript // __tests__/fonts.test.ts import { FontService } from '@/core/services/fontService'; import { Fonts } from '@/core/constants/fonts'; describe('Font Management', () => { it('should load all essential fonts', async () => { await FontService.loadFonts(); // Test that fonts are available expect(FontService.isLoaded('Inter-Regular')).toBe(true); expect(FontService.isLoaded('Inter-Bold')).toBe(true); }); it('should handle font loading failures gracefully', async () => { // Mock font loading failure jest.spyOn(Font, 'loadAsync').mockRejectedValue(new Error('Font load failed')); // Should not throw error await expect(FontService.loadFonts()).resolves.not.toThrow(); }); it('should provide correct font family constants', () => { expect(Fonts.primary.regular).toBeDefined(); expect(Fonts.primary.bold).toBeDefined(); expect(typeof Fonts.primary.regular).toBe('string'); }); }); ``` ### Typography Component Tests ```typescript // __tests__/Typography.test.tsx import React from 'react'; import { render } from '@testing-library/react-native'; import { Typography } from '@/ui/foundation/Typography/Typography'; describe('Typography Component', () => { it('should render with correct font family', () => { const { getByTestId } = render( Test Text ); const element = getByTestId('typography'); expect(element.props.style).toMatchObject({ fontFamily: expect.stringContaining('Inter'), }); }); it('should handle font fallbacks', () => { const { getByTestId } = render( Test Text ); const element = getByTestId('typography'); // Should fall back to system font expect(element.props.style.fontFamily).toBeDefined(); }); }); ``` ## Common Font Issues ### Issue 1: Fonts Not Loading on iOS **Problem**: Custom fonts appear as system fonts on iOS **Solutions**: ```typescript // ❌ Common mistakes // Using PostScript name instead of filename 'InterRegular' // Wrong // ✅ Correct approach // Use exact filename in Info.plist and code 'Inter-Regular' // Correct filename ``` ### Issue 2: Font Weight Not Working **Problem**: Different font weights appear identical **Solutions**: ```typescript // ❌ BAD: Relying on fontWeight property only { fontFamily: 'Inter', fontWeight: 'bold', // May not work on iOS } // ✅ GOOD: Use specific font family names { fontFamily: Platform.select({ ios: 'Inter-Bold', android: 'Inter', }), fontWeight: Platform.select({ ios: undefined, android: 'bold', }), } ``` ### Issue 3: Missing Character Glyphs **Problem**: Some characters display as squares or missing glyphs **Solutions**: ```typescript // Check character support in font const hasCharacterSupport = (font: string, character: string): boolean => { // Implementation to check if font supports character // Use fallback font for unsupported characters return true; // Simplified }; // Provide fallback for unsupported characters const TextWithFallback = ({ text, primaryFont, fallbackFont }) => { const hasSupport = hasCharacterSupport(primaryFont, text); return ( {text} ); }; ``` ## Related Documentation * [Theme Management Implementation](/docs/ui-development/theme-management/implementation) - **Typography scales, font sizes, and design system integration** * [Theme Management Overview](/docs/ui-development/theme-management) - Complete design system and theming guide * [Image Optimization Guide](/docs/ui-development/assets/assets-management/image-optimization-guide) - Asset format decisions and optimization * [Animation Assets Guide](/docs/ui-development/assets/assets-management/animation-assets-guide) - Motion graphics and animations * [Performance Optimization](/docs/ui-development/assets/assets-management/performance-optimization) - Bundle size and loading optimization * [Icon Management Guide](/docs/ui-development/assets/icon-management) - UI icon systems and vector icons # UTA UI Development: Animation Assets Guide URL: https://dev.updatetheapp.com/docs/ui-development/assets/assets-management/animation-assets-guide Documentation: https://dev.updatetheapp.com/docs/ui-development/assets/assets-management/animation-assets-guide Comprehensive guide for implementing and optimizing animations in React Native applications including Lottie animations, video assets, performance optimization, and memory management *** title: "Animation Assets Guide" description: "Comprehensive guide for implementing and optimizing animations in React Native applications including Lottie animations, video assets, performance optimization, and memory management" ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- import { Tabs, Tab } from 'fumadocs-ui/components/tabs'; import { Callout } from 'fumadocs-ui/components/callout'; import { Step, Steps } from 'fumadocs-ui/components/steps'; # Animation Assets Guide ## Overview Animations enhance user experience but can significantly impact app performance if not implemented properly. This guide covers Lottie animations, video assets, performance optimization, and memory management strategies for React Native applications. **Scope**: This guide focuses on animation assets (Lottie, videos, GIFs) for brand experiences. For UI micro-interactions and transitions, see the UI Development guides. ## Lottie Animations ### Getting Started with Lottie #### Install Dependencies Add the required packages for Lottie support ```bash # For Expo projects npx expo install lottie-react-native # For React Native CLI projects npm install lottie-react-native npx react-native link lottie-react-native ``` #### Basic Implementation ```typescript // components/LottieAnimation.tsx import React from 'react'; import LottieView from 'lottie-react-native'; interface LottieAnimationProps { source: any; autoPlay?: boolean; loop?: boolean; style?: any; } export const LottieAnimation: React.FC = ({ source, autoPlay = true, loop = false, style, }) => { return ( ); }; ``` #### Asset Organization Organize Lottie files in a structured manner ``` assets/animations/ ├── onboarding/ │ ├── welcome.json │ ├── features.json │ └── complete.json ├── loaders/ │ ├── spinner.json │ └── progress.json └── feedback/ ├── success.json ├── error.json └── celebration.json ``` ### Advanced Lottie Implementation #### Controlled Animation with Refs ```typescript // components/ControlledLottieView.tsx import React, { useRef, useImperativeHandle, forwardRef } from 'react'; import LottieView from 'lottie-react-native'; export interface LottieAnimationHandle { play: () => void; pause: () => void; reset: () => void; } export const ControlledLottieView = forwardRef< LottieAnimationHandle, any >(({ source, style, onAnimationFinish }, ref) => { const animationRef = useRef(null); useImperativeHandle(ref, () => ({ play: () => animationRef.current?.play(), pause: () => animationRef.current?.pause(), reset: () => animationRef.current?.reset(), })); return ( ); }); ``` #### Dynamic Color Properties ```typescript // components/DynamicLottieView.tsx import React from 'react'; import LottieView from 'lottie-react-native'; interface DynamicLottieViewProps { source: any; colorFilters?: Array<{ keypath: string; color: string; }>; } export const DynamicLottieView: React.FC = ({ source, colorFilters, ...props }) => { return ( ); }; ``` #### Performance-Optimized Animation ```typescript // components/OptimizedLottieView.tsx import React, { useEffect, useRef, useState } from 'react'; import { AppState, Platform } from 'react-native'; import LottieView from 'lottie-react-native'; export const OptimizedLottieView: React.FC = ({ source, pauseOnBackground = true, ...props }) => { const animationRef = useRef(null); const [appState, setAppState] = useState(AppState.currentState); useEffect(() => { if (!pauseOnBackground) return; const subscription = AppState.addEventListener('change', (nextAppState) => { if (appState.match(/inactive|background/) && nextAppState === 'active') { animationRef.current?.play(); } else if (nextAppState.match(/inactive|background/)) { animationRef.current?.pause(); } setAppState(nextAppState); }); return () => subscription?.remove(); }, [appState, pauseOnBackground]); return ( ); }; ``` ## Video Assets ### Video Implementation ```typescript // components/VideoPlayer.tsx import React, { useRef, useState } from 'react'; import { View, StyleSheet } from 'react-native'; import Video from 'react-native-video'; interface VideoPlayerProps { source: any; autoPlay?: boolean; loop?: boolean; muted?: boolean; onLoadStart?: () => void; onLoad?: () => void; onError?: (error: any) => void; } export const VideoPlayer: React.FC = ({ source, autoPlay = false, loop = false, muted = true, onLoadStart, onLoad, onError, }) => { const videoRef = useRef