Skip to main content

Component Architecture

The Launch mobile app uses a well-organized component system that promotes reusability, consistency, and maintainability.

Prerequisites

  • Familiarity with apps/mobile/components
  • AppText and base components available

Steps

Common Components

Components live in a flat components/ folder. Here are a few real examples from the codebase:
import { AuthButton } from "@/components/auth-button";

<AuthButton
  provider="apple"
  text="Continue with Apple"
  onPress={handleAppleSignIn}
/>

Creating New Components

Component Template

// components/ui/my-component.tsx
import { View } from "react-native";
import { AppText } from "./app-text";
import { getCopy } from "@/config/launch.config";

interface MyComponentProps {
  title: string;
  variant?: "primary" | "secondary";
  onPress?: () => void;
  className?: string;
}

export function MyComponent({
  title,
  variant = "primary",
  onPress,
  className = "",
}: MyComponentProps) {
  const variantStyles = {
    primary: "bg-blue-500 text-white",
    secondary: "bg-gray-200 text-gray-800",
  };

  return (
    <View className={`p-4 rounded-lg ${variantStyles[variant]} ${className}`}>
      <AppText className="font-bold">{title}</AppText>
    </View>
  );
}

Troubleshooting

  • Styles not applied: verify NativeWind classes and theme tokens
  • Component not found: check exports and import paths

Next Steps

Import Pattern

Components are imported directly from their files:
import { AppText } from "@/components/app-text";
import { ScreenContainer } from "@/components/screen-container";

TypeScript Best Practices

// Define clear interfaces
interface ButtonProps {
  children: React.ReactNode;
  variant: "primary" | "secondary" | "danger";
  size?: "sm" | "md" | "lg";
  disabled?: boolean;
  onPress: () => void; // Required callback
  className?: string; // Optional styling
}

// Use union types for variants
type AlertType = "success" | "warning" | "error" | "info";

// Export types for reuse
export type { ButtonProps, AlertType };

Styling Guidelines

NativeWind Classes

Use Tailwind CSS classes through NativeWind:
// Good - Semantic classes
<View className="flex-1 bg-background p-4 gap-2">
  <AppText className="text-2xl font-bold text-foreground">
    Title
  </AppText>
</View>

// Avoid - Arbitrary values
<View style={{ flex: 1, backgroundColor: '#fff', padding: 16 }}>

Theme Integration

Components should respect the theme system:
import { useTheme } from "heroui-native";

export function ThemedComponent() {
  const { colors, isDark } = useTheme();

  return (
    <View
      className="p-4 rounded-lg"
      style={{
        backgroundColor: isDark ? colors.background : colors.card
      }}
    >
      <AppText className="text-foreground">Themed content</AppText>
    </View>
  );
}

Responsive Design

Handle different screen sizes and form factors:
import { useResponsive } from "@/components/useResponsive";

export function ResponsiveComponent() {
  const { isTablet } = useResponsive();

  return (
    <View className={`p-4 ${isTablet ? "max-w-md mx-auto" : "w-full"}`}>
      {/* Component content */}
    </View>
  );
}

Copy Organization

Launch does not ship with a centralized copy system. Keep copy close to the component or feature module it belongs to, and extract shared strings into constants when needed.

Testing Components

Component Testing

// __tests__/my-component.test.tsx
import { render, fireEvent } from "@testing-library/react-native";
import { MyComponent } from "../my-component";

describe("MyComponent", () => {
  it("renders correctly", () => {
    const { getByText } = render(
      <MyComponent title="Test Title" />
    );

    expect(getByText("Test Title")).toBeTruthy();
  });

  it("handles press events", () => {
    const mockOnPress = jest.fn();
    const { getByText } = render(
      <MyComponent title="Test" onPress={mockOnPress} />
    );

    fireEvent.press(getByText("Test"));
    expect(mockOnPress).toHaveBeenCalled();
  });
});

Visual Testing

Use Storybook or component galleries for visual testing:
// stories/MyComponent.stories.tsx
export default {
  title: "UI/MyComponent",
  component: MyComponent,
};

export const Primary = {
  args: {
    title: "Primary Button",
    variant: "primary",
  },
};

export const Secondary = {
  args: {
    title: "Secondary Button",
    variant: "secondary",
  },
};

Performance Considerations

Memoization

Use React.memo for expensive components:
import React from "react";

interface ExpensiveComponentProps {
  data: ComplexDataType[];
  onItemPress: (id: string) => void;
}

export const ExpensiveComponent = React.memo<ExpensiveComponentProps>(
  ({ data, onItemPress }) => {
    return (
      <View>
        {data.map(item => (
          <ExpensiveItem
            key={item.id}
            item={item}
            onPress={() => onItemPress(item.id)}
          />
        ))}
      </View>
    );
  }
);

Lazy Loading

Use lazy loading for heavy components:
import { lazy, Suspense } from "react";
import { AppText } from "@/components/app-text";

const HeavyComponent = lazy(() => import("./heavy-component"));

export function ParentComponent() {
  return (
    <Suspense fallback={<AppText>Loading...</AppText>}>
      <HeavyComponent />
    </Suspense>
  );
}

Best Practices

Component Design

  1. Single Responsibility - Each component should have one clear purpose
  2. Composition over Inheritance - Build complex components from simpler ones
  3. Props Interface - Always define TypeScript interfaces for props
  4. Default Props - Provide sensible defaults for optional props
  5. Error Boundaries - Handle errors gracefully

Code Organization

  1. Logical Grouping - Place components in appropriate category folders
  2. Index Exports - Always export from folder index files
  3. Consistent Naming - Use PascalCase for components, camelCase for props
  4. File Naming - Use kebab-case for file names

Documentation

  1. Props Documentation - Document complex props with JSDoc
  2. Usage Examples - Provide clear usage examples
  3. Component Stories - Create Storybook stories for visual components
  4. README Updates - Keep component documentation current
This component system ensures consistency, reusability, and maintainability across the entire mobile application.