DEV Community

Serif COLAKEL
Serif COLAKEL

Posted on

๐Ÿ”ฌ Atomic Design in React and React Native: Building Scalable UI Systems

Tags: React, React Native, Atomic Design, Component Architecture, Best Practices

Atomic Design offers a systematic approach to building maintainable UIs. Let's explore real-world implementations and professional patterns for React and React Native.


๏ฟฝ Modern Atomic Structure: Enhanced Folder Architecture

Scalable structure with TypeScript and cross-platform support:

src/
โ”œโ”€โ”€ components/
โ”‚   โ”œโ”€โ”€ atoms/
โ”‚   โ”‚   โ”œโ”€โ”€ Button/
โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ Button.tsx       # Web implementation
โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ Button.native.tsx # Mobile implementation
โ”‚   โ”‚   โ”‚   โ””โ”€โ”€ index.ts
โ”‚   โ”œโ”€โ”€ molecules/
โ”‚   โ”œโ”€โ”€ organisms/
โ”‚
โ”œโ”€โ”€ features/             # Optional feature-based grouping
โ”‚   โ””โ”€โ”€ cart/
โ”‚       โ”œโ”€โ”€ CartItem.tsx  # Feature-specific organism
โ”‚
โ”œโ”€โ”€ theme/
โ”‚   โ”œโ”€โ”€ colors.ts
โ”‚   โ””โ”€โ”€ spacing.ts
Enter fullscreen mode Exit fullscreen mode

Key improvement: Platform-specific files and feature-based grouping when needed.


๐Ÿงช Real-World Component Examples

This section showcases practical implementations of atomic design principles in React and React Native.

1. Smart Image Atom (Cross-Platform)

// components/atoms/Image/Image.tsx (Web)
import { useState } from "react";

type ImageProps = {
  src: string;
  fallback?: string;
  alt: string;
  className?: string;
};

export const Image = ({ src, fallback, alt, className }: ImageProps) => {
  const [source, setSource] = useState(src);

  const handleError = () => {
    if (fallback) setSource(fallback);
  };

  return (
    <img
      src={source}
      alt={alt}
      className={className}
      onError={handleError}
      loading="lazy"
    />
  );
};

// components/atoms/Image/Image.native.tsx (Mobile)
import { Image as RNImage } from "react-native";

export const Image = ({ src, fallback, style }) => (
  <RNImage
    source={{ uri: src }}
    defaultSource={fallback}
    style={style}
    accessibilityRole="image"
  />
);
Enter fullscreen mode Exit fullscreen mode

Features:

  • Lazy loading (web)
  • Error fallback
  • Accessibility roles
  • Platform-specific optimizations

2. Form Field Molecule with Validation

// components/molecules/FormField/FormField.tsx
import { useState, useEffect } from "react";
import { TextInput, Label, ErrorText } from "../atoms";

type FormFieldProps = {
  label: string;
  type?: "text" | "email" | "password";
  required?: boolean;
  validator?: (value: string) => string | null;
};

export const FormField = ({
  label,
  type = "text",
  validator,
}: FormFieldProps) => {
  const [value, setValue] = useState("");
  const [error, setError] = useState<string | null>(null);

  useEffect(() => {
    if (validator && value) {
      setError(validator(value));
    }
  }, [value, validator]);

  return (
    <div className="form-field">
      <Label htmlFor={label}>{label}</Label>
      <TextInput
        id={label}
        type={type}
        value={value}
        onChange={(e) => setValue(e.target.value)}
        aria-invalid={!!error}
      />
      {error && <ErrorText>{error}</ErrorText>}
    </div>
  );
};
Enter fullscreen mode Exit fullscreen mode

Features:

  • Validation system
  • Accessibility labels
  • Reusable across forms
  • Type safety with TypeScript

3. Product Card Organism

// components/organisms/ProductCard/ProductCard.tsx
import { Image, Button, PriceDisplay } from "../../atoms";

type Product = {
  id: string;
  name: string;
  price: number;
  imageUrl: string;
};

export const ProductCard = ({ product }: { product: Product }) => (
  <article className="product-card">
    <Image
      src={product.imageUrl}
      fallback="/product-fallback.jpg"
      alt={product.name}
    />
    <h3>{product.name}</h3>
    <PriceDisplay value={product.price} currency="USD" />
    <Button
      variant="primary"
      onClick={() => addToCart(product.id)}
      aria-label={`Add ${product.name} to cart`}
    >
      Add to Cart
    </Button>
  </article>
);
Enter fullscreen mode Exit fullscreen mode

Features:

  • Composite UI with multiple atoms
  • Type-safe product interface
  • Accessibility labels
  • Consistent styling system

๐Ÿš€ Advanced Patterns & Best Practices

1. State Management Boundaries

// Good practice - Keep atoms stateless
const Counter = ({ count, onIncrement }) => (
  <div>
    <span>{count}</span>
    <Button onClick={onIncrement}>+</Button>
  </div>
);

// Bad practice - Avoid state in atoms
const Counter = () => {
  const [count, setCount] = useState(0);
  return (...);
};
Enter fullscreen mode Exit fullscreen mode

2. Performance Optimization

// Memoize expensive components
import { memo } from "react";

const HeavyList = memo(({ items }) => (
  <ul>
    {items.map((item) => (
      <ListItem key={item.id} {...item} />
    ))}
  </ul>
));
Enter fullscreen mode Exit fullscreen mode

3. Cross-Platform Strategy

// components/atoms/Button/index.ts
export { default as Button } from "./Button";
// Automatically imports .native.tsx for React Native

// Shared props interface
interface ButtonProps {
  variant?: "primary" | "secondary";
  accessibilityLabel?: string;
  onPress: () => void;
}
Enter fullscreen mode Exit fullscreen mode

4. Accessibility First

// components/atoms/IconButton.tsx
export const IconButton = ({ icon, label, ...props }) => (
  <button {...props} aria-label={label} className="icon-button">
    <Icon name={icon} />
  </button>
);
Enter fullscreen mode Exit fullscreen mode

5. Theme Provider Pattern

// theme/ThemeProvider.tsx
import { ThemeProvider as StyledProvider } from "styled-components";

const theme = {
  colors: {
    primary: "#2196F3",
    error: "#FF5252",
  },
  spacing: (multiplier: number) => `${4 * multiplier}px`,
};

export const ThemeProvider = ({ children }) => (
  <StyledProvider theme={theme}>{children}</StyledProvider>
);
Enter fullscreen mode Exit fullscreen mode

๐Ÿ“ฑ React Native Specific Patterns

1. Safe Area Handling

// templates/MainLayout.native.tsx
import { SafeAreaView } from "react-native";

export const MainLayout = ({ children }) => (
  <SafeAreaView style={styles.container}>
    <Header />
    {children}
  </SafeAreaView>
);
Enter fullscreen mode Exit fullscreen mode

2. Touch Interaction Best Practice

// molecules/ListItem.native.tsx
import { Pressable, View } from "react-native";

export const ListItem = ({ title, onPress }) => (
  <Pressable
    onPress={onPress}
    android_ripple={{ color: "#ddd" }}
    style={({ pressed }) => [styles.item, pressed && styles.pressed]}
  >
    <Text>{title}</Text>
  </Pressable>
);
Enter fullscreen mode Exit fullscreen mode

๐Ÿง  Architectural Insights

  1. Cross-Platform Metrics
// theme/spacing.ts
export const spacing = {
  base: 8,
  get vertical() {
    return Platform.select({
      web: this.base * 2,
      default: this.base,
    });
  },
};
Enter fullscreen mode Exit fullscreen mode
  1. Feature-Sliced Structure
features/
  authentication/
    components/
      LoginForm.tsx
    hooks/
      useAuth.ts
Enter fullscreen mode Exit fullscreen mode
  1. Documentation Strategy
/**
 * @component FormField
 * @description Reusable form input with validation
 * @prop {string} label - Input label
 * @prop {function} validator - Validation function
 * @example
 * <FormField
 *   label="Email"
 *   validator={(v) => isValidEmail(v) ? null : 'Invalid email'}
 * />
 */
Enter fullscreen mode Exit fullscreen mode

๐Ÿ Conclusion

By implementing these real-world patterns:

โœ… Achieve true cross-platform consistency
โœ… Build maintainable component systems
โœ… Enhance team collaboration
โœ… Improve testability
โœ… Future-proof your architecture

Atomic Design is not just a patternโ€”it's a mindset that helps teams scale their UIs in a systematic and consistent way. Whether you're building for web or mobile, applying this approach in React and React Native can dramatically improve your component architecture.

Top comments (1)

Collapse
 
nevodavid profile image
Nevo David

insane depth here, i honestly wish i had this when i started building my first real project