From Monolith to Modular: Refactoring a React Landing Page Using 'Thinking in React' Principles

8 min readDevelopment

A step-by-step case study of transforming a 266-line React component into a maintainable, modular architecture.

The Problem: When Components Grow Too Large

We inherited a React landing page with a single, massive component that violated fundamental React principles. The LandingPage function had ballooned into 266 lines of JSX with inline styles, hardcoded content, and multiple responsibilities in one file.

Before: The Monolith 😱

export default function LandingPage() {
  return (
    <main style={{ /* 20+ style properties */ }}>
      {/* 200+ lines of mixed JSX */}
      <div style={{ /* Hero section */ }}>
        <h1 style={{ /* Complex gradient styling */ }}>
          Chat With Your Files
        </h1>
        {/* More inline styled elements... */}
      </div>
      {/* Features, Contact, Footer all mixed together */}
    </main>
  );
}

This violated several React best practices:

  • Single Responsibility Principle: One component handled hero, features, contact, and footer.
  • Maintainability: 266 lines made the file hard to navigate.
  • Reusability: Inline styles and hardcoded content prevented reuse.
  • Testability: Impossible to test sections in isolation.

The Solution: Applying "Thinking in React"

We applied React's Thinking in React methodology to systematically refactor the monolith.

Phase 1: Break the UI into a Component Hierarchy

LandingPage (root container)
├── HeroSection (headline + CTAs)
├── FeaturesSection (feature grid)
├── ContactSection (contact info / lead capture)
└── Footer (branding + credits)

Each component now had a clear responsibility:

  • HeroSection – Introduce the product and drive conversions
  • FeaturesSection – Highlight key value propositions
  • ContactSection – Capture leads and provide contact info
  • Footer – Provide branding and credits

Phase 2: Extract Static Components

// HeroSection.tsx
export default function HeroSection() {
  return (
    <div className={css.heroSection}>
      <div className={css.heroContainer}>
        <h1 className={css.heroHeadline}>Chat With Your Files</h1>
        <p className={css.heroSubtitle}>Turn your document archive into a searchable assistant.</p>
        <div className={css.heroButtons}>
          <Link href="/demo" className={css.heroPrimaryBtn}>Try Live Demo →</Link>
          <Link href="/contact" className={css.heroSecondaryBtn}>Contact Us</Link>
        </div>
      </div>
    </div>
  );
}
// LandingPage.tsx
export default function LandingPage() {
  return (
    <main style={mainStyles}>
      <HeroSection />
      <FeaturesSection />
      <ContactSection />
      <Footer />
    </main>
  );
}

Phase 3: Separate Styling Concerns with CSS Modules

Before (inline styles):

<h1 style={{
  fontSize: 56,
  fontWeight: 700,
  background: "linear-gradient(135deg, #fff 0%, #a0a0a0 100%)",
  WebkitBackgroundClip: "text",
  WebkitTextFillColor: "transparent"
}}>

After (CSS Modules):

<h1 className={css.heroHeadline}>
/* HeroSection.module.css */
.heroHeadline {
  font-size: 56px;
  font-weight: 700;
  background: linear-gradient(135deg, #fff 0%, #a0a0a0 100%);
  -webkit-background-clip: text;
  -webkit-text-fill-color: transparent;
}

The Results: Measurable Improvements

| Metric | Before | After | Improvement | |--------|--------|-------|-------------| | Lines per component | 266 | 20–80 | 70% reduction | | Inline styles | 100% | 0% | Complete elimination | | Reusable components | 0 | 4 | ∞% increase | | CSS modularity | None | Full | Complete separation |

Developer Experience Benefits

Maintainability: Each section is edited independently.

Testability: Components can be tested in isolation.

describe('HeroSection', () => {
  it('renders call-to-action buttons', () => {
    render(<HeroSection />);
    expect(screen.getByText('Try Live Demo')).toBeInTheDocument();
  });
});

Collaboration: Multiple devs can work in parallel without conflicts.

Reusability: Components can be dropped into other pages or apps.

Why This Follows React Best Practices

  • Component Hierarchy: Clear parent–child relationships
  • Single Responsibility: One purpose per component
  • Static First: No premature state management
  • Separation of Concerns: CSS, logic, and markup are decoupled
  • Modern Patterns: Functional components, CSS Modules, clean imports

Lessons Learned

When to Apply This Pattern

  • Large components (>100 lines)
  • Multiple responsibilities in one file
  • Inline style sprawl
  • Difficult-to-maintain codebases

When Not to Over-Engineer

  • Static pages rarely need deep prop plumbing or early abstractions
  • Prefer simple, focused components over speculative complexity

Why CSS Modules Help

  • Scoped styling (no collisions)
  • Solid browser caching performance
  • Strong IDE support
  • Easier long-term maintenance

Future Improvements

  1. Add light state where needed (e.g., form validation, mobile menus)
  2. Make content configurable for A/B tests when appropriate
  3. Extract a small design system as patterns emerge
  4. Add TypeScript interfaces/props for type safety as variability grows

Conclusion

By applying Thinking in React, we transformed a 266-line monolithic component into a clean, modular architecture. The refactor improved maintainability, testability, and team productivity—without adding unnecessary complexity.

Key takeaway: Great React architecture starts with identifying the right component boundaries. Sometimes the simplest refactor—breaking things apart thoughtfully—is the most effective.


Tech Stack: React 18, Next.js, TypeScript, CSS Modules Scope: Landing page refactor Effort: ~2 hours Impact: Immediate improvement in maintainability and developer experience