Skip to main content

Best practices

Follow these best practices to build maintainable, performant, and user-friendly applications with FlowX.AI UI Toolkits. These recommendations are based on real-world implementations and common patterns.

Architecture patterns

Component organization

Organize your components by feature rather than by type for better maintainability:
src/
├── features/
│   ├── customer-onboarding/
│   │   ├── components/
│   │   │   ├── PersonalInfoForm.tsx
│   │   │   ├── AddressForm.tsx
│   │   │   └── DocumentUpload.tsx
│   │   ├── CustomerOnboarding.tsx
│   │   └── index.ts
│   ├── loan-application/
│   │   ├── components/
│   │   └── LoanApplication.tsx
│   └── account-management/
├── shared/
│   ├── components/
│   ├── hooks/
│   └── utils/
└── App.tsx

Separation of concerns

Keep process logic separate from presentation:
customer-onboarding.component.ts
// Container component - handles process logic
@Component({
  selector: 'app-customer-onboarding',
  template: `
    <flx-process-container
      [processName]="processName"
      [processStartData]="startData"
      (processCompleted)="handleCompletion($event)"
    >
    </flx-process-container>
  `
})
export class CustomerOnboardingComponent {
  processName = 'customer-onboarding';
  startData = { source: 'web' };
  
  constructor(private router: Router) {}
  
  handleCompletion(event: any) {
    this.router.navigate(['/success'], {
      queryParams: { customerId: event.data.customerId }
    });
  }
}

// Presentation component - handles UI only
@Component({
  selector: 'app-personal-info-form',
  template: `
    <form flxForm>
      <flx-input name="firstName" label="First Name" [required]="true"></flx-input>
      <flx-input name="lastName" label="Last Name" [required]="true"></flx-input>
      <flx-button actionName="NEXT" label="Continue"></flx-button>
    </form>
  `
})
export class PersonalInfoFormComponent {}

Performance optimization

Lazy loading processes

Load process containers only when needed:
app-routing.module.ts
const routes: Routes = [
  {
    path: 'onboarding',
    loadChildren: () => import('./features/customer-onboarding/customer-onboarding.module')
      .then(m => m.CustomerOnboardingModule)
  },
  {
    path: 'loan',
    loadChildren: () => import('./features/loan-application/loan-application.module')
      .then(m => m.LoanApplicationModule)
  }
];

Minimize re-renders

Use memoization to prevent unnecessary re-renders:
data-table.component.ts
@Component({
  selector: 'app-data-table',
  template: `
    <flx-table
      [name]="tableName"
      [columns]="columns"
      [pageSize]="pageSize"
    ></flx-table>
  `,
  changeDetection: ChangeDetectionStrategy.OnPush
})
export class DataTableComponent {
  tableName = 'customers';
  pageSize = 20;
  
  // Define columns once
  columns = [
    { field: 'id', header: 'ID' },
    { field: 'name', header: 'Name' },
    { field: 'email', header: 'Email' }
  ];
}

Error handling

Global error handling

Implement centralized error handling for process operations:
error-handler.service.ts
@Injectable({ providedIn: 'root' })
export class ProcessErrorHandler {
  constructor(
    private notificationService: NotificationService,
    private logger: LoggerService
  ) {}
  
  handleProcessError(error: any, context?: string) {
    // Log error
    this.logger.error('Process error', { error, context });
    
    // Show user-friendly message
    if (error.status === 401) {
      this.notificationService.error('Your session has expired. Please log in again.');
    } else if (error.status === 403) {
      this.notificationService.error('You do not have permission to perform this action.');
    } else if (error.status >= 500) {
      this.notificationService.error('A server error occurred. Please try again later.');
    } else {
      this.notificationService.error(error.message || 'An unexpected error occurred.');
    }
  }
}

Graceful degradation

Provide fallback UI when processes fail to load:
ProcessErrorBoundary.tsx
import React, { Component, ReactNode } from 'react';

interface Props {
  children: ReactNode;
  fallback?: ReactNode;
}

interface State {
  hasError: boolean;
  error?: Error;
}

export class ProcessErrorBoundary extends Component<Props, State> {
  state: State = { hasError: false };
  
  static getDerivedStateFromError(error: Error): State {
    return { hasError: true, error };
  }
  
  componentDidCatch(error: Error, errorInfo: any) {
    console.error('Process error:', error, errorInfo);
  }
  
  render() {
    if (this.state.hasError) {
      return this.props.fallback || (
        <div className="error-container">
          <h2>Something went wrong</h2>
          <p>We're having trouble loading this process. Please try refreshing the page.</p>
          <button onClick={() => window.location.reload()}>Refresh</button>
        </div>
      );
    }
    
    return this.props.children;
  }
}

Security best practices

Token management

Never hardcode authentication tokens:
// ❌ Bad - hardcoded token
const config = {
  authToken: 'sk_live_abc123xyz'
};

// ✅ Good - token from environment
const config = {
  authToken: process.env.REACT_APP_FLOWX_AUTH_TOKEN
};

// ✅ Better - token from secure storage
const config = {
  authToken: await secureStorage.getToken()
};

Input sanitization

Validate and sanitize user input before sending to processes:
function sanitizeInput(value: string): string {
  return value
    .trim()
    .replace(/[<>]/g, '') // Remove potential HTML tags
    .substring(0, 1000); // Limit length
}

const startData = {
  customerName: sanitizeInput(userInput.name),
  email: sanitizeInput(userInput.email).toLowerCase()
};

HTTPS only

Always use HTTPS for API connections in production:
const config = {
  apiUrl: process.env.NODE_ENV === 'production'
    ? 'https://api.flowx.example.com'
    : 'http://localhost:3000'
};

Accessibility

Keyboard navigation

Ensure all interactive elements are keyboard accessible:
// Components include built-in keyboard support
<FlxButton
  label="Submit"
  actionName="SUBMIT"
  // Automatically handles Enter and Space key presses
/>

Screen reader support

Provide descriptive labels and ARIA attributes:
<FlxInput
  name="email"
  label="Email Address"
  aria-label="Enter your email address"
  aria-describedby="email-hint"
  required
/>
<span id="email-hint" className="hint-text">
  We'll never share your email with anyone else.
</span>

Focus management

Manage focus for better UX, especially after actions:
@Component({
  template: `
    <flx-button
      actionName="DELETE"
      (actionExecuted)="onDelete()"
    >Delete</flx-button>
    <div #successMessage tabindex="-1">Item deleted successfully</div>
  `
})
export class MyComponent {
  @ViewChild('successMessage') successMessage!: ElementRef;
  
  onDelete() {
    // Focus the success message for screen readers
    setTimeout(() => {
      this.successMessage.nativeElement.focus();
    }, 100);
  }
}

Testing

Component testing

Test components with mocked process data:
customer-form.component.spec.ts
describe('CustomerFormComponent', () => {
  let component: CustomerFormComponent;
  let fixture: ComponentFixture<CustomerFormComponent>;
  let mockProcessService: jasmine.SpyObj<FlowxProcessService>;
  
  beforeEach(() => {
    mockProcessService = jasmine.createSpyObj('FlowxProcessService', [
      'startProcess',
      'executeAction'
    ]);
    
    TestBed.configureTestingModule({
      declarations: [CustomerFormComponent],
      providers: [
        { provide: FlowxProcessService, useValue: mockProcessService }
      ]
    });
    
    fixture = TestBed.createComponent(CustomerFormComponent);
    component = fixture.componentInstance;
  });
  
  it('should submit form data', () => {
    const formData = { firstName: 'John', lastName: 'Doe' };
    mockProcessService.executeAction.and.returnValue(of({ success: true }));
    
    component.submitForm(formData);
    
    expect(mockProcessService.executeAction).toHaveBeenCalledWith(
      'SUBMIT',
      formData
    );
  });
});

Integration testing

Test complete process flows end-to-end:
e2e/customer-onboarding.spec.ts
describe('Customer Onboarding Process', () => {
  it('should complete onboarding flow', () => {
    cy.visit('/onboarding');
    
    // Step 1: Personal Information
    cy.get('[name="firstName"]').type('John');
    cy.get('[name="lastName"]').type('Doe');
    cy.get('[name="email"]').type('[email protected]');
    cy.contains('Continue').click();
    
    // Step 2: Address
    cy.get('[name="street"]').type('123 Main St');
    cy.get('[name="city"]').type('New York');
    cy.contains('Continue').click();
    
    // Step 3: Confirmation
    cy.contains('Submit').click();
    
    // Verify completion
    cy.url().should('include', '/success');
    cy.contains('Onboarding completed successfully');
  });
});

Monitoring and logging

Process analytics

Track process metrics for insights:
export class ProcessAnalytics {
  trackProcessStart(processName: string, data: any) {
    analytics.track('Process Started', {
      processName,
      timestamp: new Date().toISOString(),
      startData: data
    });
  }
  
  trackProcessComplete(processName: string, duration: number) {
    analytics.track('Process Completed', {
      processName,
      duration,
      timestamp: new Date().toISOString()
    });
  }
  
  trackProcessError(processName: string, error: any) {
    analytics.track('Process Error', {
      processName,
      errorMessage: error.message,
      errorCode: error.code,
      timestamp: new Date().toISOString()
    });
  }
}

Performance monitoring

Monitor component performance:
import { performance } from 'perf_hooks';

export function measureProcessTime(processName: string) {
  const startTime = performance.now();
  
  return () => {
    const endTime = performance.now();
    const duration = endTime - startTime;
    
    if (duration > 3000) {
      console.warn(`Process ${processName} took ${duration}ms`);
    }
    
    return duration;
  };
}

Documentation

Document your process integrations:
/**
 * Customer Onboarding Process
 * 
 * Process Name: customer-onboarding
 * Version: 1.2.0
 * 
 * Start Data:
 * @param {string} source - Traffic source (web, mobile, referral)
 * @param {string} [campaignId] - Optional marketing campaign ID
 * 
 * Process Variables:
 * - personalInfo: Customer personal information
 * - address: Customer address details
 * - documents: Uploaded verification documents
 * 
 * Actions:
 * - CONTINUE: Proceed to next step
 * - SAVE_DRAFT: Save progress and exit
 * - CANCEL: Abandon process
 * - SUBMIT: Complete onboarding
 * 
 * Completion Data:
 * @returns {string} customerId - Newly created customer ID
 * @returns {string} accountNumber - Account number assigned
 */
export class CustomerOnboardingProcess {}

Summary checklist

Before deploying to production, verify:
  • Components are organized by feature
  • Lazy loading is implemented for routes
  • Error handling covers all process operations
  • Authentication tokens are stored securely
  • All API calls use HTTPS
  • Components are keyboard accessible
  • ARIA labels are present where needed
  • Unit tests cover critical functionality
  • E2E tests validate complete flows
  • Analytics tracking is configured
  • Performance monitoring is active
  • Process integrations are documented

Build docs developers (and LLMs) love