DEV Community

Sachin Maurya
Sachin Maurya

Posted on

The 10-Line Architecture That Scaled to 50+ Pages

"The best architecture is invisible to the developer using it" - This principle led us to create something that fundamentally changed how we build web applications.

Six months ago, our Next.js application at Kreate Technologies was drowning in technical debt. 50+ pages, 300+ components, and a team struggling to maintain consistency. Every new page took 2 hours to build. Every bug fix risked breaking three other features.

Today, creating a new page takes 18 minutes. Our bundle size dropped 65%. Performance scores hit 95+. Developer productivity increased 300%.

The secret? We treat pages as data structures, not code.

Table of Contents

The Problem: Component Soup {#the-problem}

Picture this typical Next.js page:

// homepage.tsx - The old way (200+ lines)
import HeroSection from '@/components/sections/HeroSection';
import ServicesSection from '@/components/sections/ServicesSection';
import TestimonialsSection from '@/components/sections/TestimonialsSection';
import ClientsSection from '@/components/sections/ClientsSection';
import FAQSection from '@/components/sections/FAQSection';
import CTASection from '@/components/sections/CTASection';
// ... 20 more imports

export default function HomePage() {
  const [heroData, setHeroData] = useState(null);
  const [servicesData, setServicesData] = useState(null);
  // ... 10 more state variables

  useEffect(() => {
    fetchHeroData().then(setHeroData);
    fetchServicesData().then(setServicesData);
    // ... 10 more API calls
  }, []);

  if (!heroData || !servicesData /* ... */) {
    return <PageLoader />;
  }

  return (
    <>
      <HeroSection 
        title={heroData.title}
        subtitle={heroData.subtitle}
        image={heroData.image}
        ctaText={heroData.ctaText}
        ctaLink={heroData.ctaLink}
      />
      <ServicesSection 
        services={servicesData}
        layout="grid"
        columns={3}
      />
      {/* ... 15 more sections */}
    </>
  );
}
Enter fullscreen mode Exit fullscreen mode

Problems everywhere:

  • 🔴 Code duplication across 50+ pages
  • 🔴 No lazy loading (450KB initial bundle)
  • 🔴 Prop drilling nightmare
  • 🔴 Error cascading (one failure = page crash)
  • 🔴 Inconsistent patterns
  • 🔴 2+ weeks for new developer onboarding

The Breakthrough Moment {#the-breakthrough}

During a code review, I noticed something shocking: 80% of our pages were just different arrangements of the same 30 components.

What if we could define pages as configurations instead of code?

// The same page - revolutionary approach (10 lines)
import { PageRenderer } from '@/components/common/PageRenderer';
import { HOMEPAGE_SECTIONS } from '@/config/sectionsConfig';

export default function HomePage() {
  return <PageRenderer sections={HOMEPAGE_SECTIONS} pagelink="home" />;
}
Enter fullscreen mode Exit fullscreen mode
// sectionsConfig.ts - The magic happens here
export const HOMEPAGE_SECTIONS: Section[] = [
  createSection('hero', () => import('./HeroSection'), { type: 'banner' }),
  createSection('services', () => import('./ServicesSection'), { type: 'grid', cols: 3 }),
  createSection('testimonials', () => import('./TestimonialsSection'), { type: 'carousel' }),
  createSection('clients', () => import('./ClientsSection'), { type: 'logos' }),
  createSection('faq', () => import('./FAQSection'), { type: 'accordion' }),
];
Enter fullscreen mode Exit fullscreen mode

This simple shift unleashed a cascade of innovations.

The Architecture Deep Dive {#the-architecture}

The Section Factory Pattern

interface Section {
  id: string;
  Component: React.LazyExoticComponent<React.ComponentType<any>>;
  skeleton: SkeletonConfig;
  defaultProps?: Record<string, unknown>;
}

interface SkeletonConfig {
  type: 'banner' | 'grid' | 'carousel' | 'list' | 'card';
  cols?: number;
  rows?: number;
  animated?: boolean;
}

const createSection = (
  id: string,
  importer: () => Promise<{ default: React.ComponentType<any> }>,
  skeleton: SkeletonConfig,
  defaultProps?: Record<string, unknown>
): Section => {
  return {
    id,
    Component: React.lazy(importer),
    skeleton,
    defaultProps
  };
};
Enter fullscreen mode Exit fullscreen mode

The Orchestrator: PageRenderer

The magic happens in our PageRenderer component:

export const PageRenderer: React.FC<PageRendererProps> = ({
  sections,
  globalProps = {},
  sectionProps = {},
  pagelink
}) => {
  const renderSection = useCallback((section: Section, index: number) => {
    const mergedProps = {
      ...globalProps,
      ...section.defaultProps,
      ...sectionProps[section.id],
      pagelink
    };

    return (
      <SectionErrorBoundary key={section.id} sectionName={section.id}>
        <LazySection
          Component={() => <section.Component {...mergedProps} />}
          skeleton={section.skeleton}
          sectionName={section.id}
        />
      </SectionErrorBoundary>
    );
  }, [globalProps, sectionProps, pagelink]);

  return (
    <div className="page-container">
      {sections.map(renderSection)}
    </div>
  );
};
Enter fullscreen mode Exit fullscreen mode

Intelligent Lazy Loading

Our LazySection component uses Intersection Observer for viewport-aware loading:

const LazySection: React.FC<LazySectionProps> = ({ 
  Component, 
  skeleton, 
  sectionName 
}) => {
  const [shouldRender, setShouldRender] = useState(false);
  const targetRef = useRef<HTMLDivElement>(null);

  useEffect(() => {
    if (!targetRef.current) return;

    const observer = new IntersectionObserver(
      ([entry]) => {
        if (entry.isIntersecting) {
          setShouldRender(true);
          performance.mark(`section-${sectionName}-start`);
        }
      },
      {
        threshold: 0.1,
        rootMargin: '50px' // Start loading 50px before visible
      }
    );

    observer.observe(targetRef.current);
    return () => observer.disconnect();
  }, [sectionName]);

  return (
    <div ref={targetRef} data-section={sectionName}>
      {shouldRender ? (
        <Suspense fallback={<SectionSkeleton config={skeleton} />}>
          <Component />
        </Suspense>
      ) : (
        <SectionSkeleton config={skeleton} />
      )}
    </div>
  );
};
Enter fullscreen mode Exit fullscreen mode

Content-Aware Skeleton System

Each section defines its own loading state:

const SectionSkeleton: React.FC<{ config: SkeletonConfig }> = ({ config }) => {
  switch (config.type) {
    case 'grid':
      const items = (config.cols || 3) * (config.rows || 2);
      return (
        <div className={`grid grid-cols-${config.cols} gap-4`}>
          {Array.from({ length: items }).map((_, i) => (
            <Skeleton key={i} className="h-32" delay={i * 50} />
          ))}
        </div>
      );

    case 'carousel':
      return (
        <div className="flex gap-4 overflow-hidden">
          {Array.from({ length: 3 }).map((_, i) => (
            <Skeleton key={i} className="flex-shrink-0 w-80 h-48" />
          ))}
        </div>
      );

    // ... more skeleton types
  }
};
Enter fullscreen mode Exit fullscreen mode

Real Implementation Examples {#implementation}

Our GenAI Document Analyzer Page

export const GENAI_SECTIONS: Section[] = [
  createSection(
    'hero',
    () => import('@/components/sections/genai/HeroSection'),
    { type: 'banner', height: '600px' }
  ),

  createSection(
    'processing-modes',
    () => import('@/components/sections/genai/AIProcessingModes'),
    { type: 'grid', cols: 2, rows: 4 }
  ),

  createSection(
    'industry-solutions',
    () => import('@/components/sections/genai/IndustrySolutions'),
    { type: 'carousel', items: 4 }
  ),

  createSection(
    'testimonials',
    () => import('@/components/sections/common/ClientTestimonials'),
    { type: 'carousel', autoPlay: true }
  )
];

// The entire page
export default function GenAIPage() {
  return (
    <PageRenderer 
      sections={GENAI_SECTIONS}
      pagelink="genai"
      globalProps={{ theme: 'dark' }}
    />
  );
}
Enter fullscreen mode Exit fullscreen mode

Dynamic Configuration with Feature Flags

const getPageSections = (page: string, user?: User): Section[] => {
  const baseSections = PAGE_CONFIGS[page] || [];

  // A/B testing through configuration
  if (user?.experimentGroup === 'B') {
    return [
      baseSections[1], // Move features first for group B
      baseSections[0],
      ...baseSections.slice(2)
    ];
  }

  // Feature flags
  if (featureFlags.newTestimonials) {
    const newSections = [...baseSections];
    const testimonialIndex = newSections.findIndex(s => s.id === 'testimonials');

    if (testimonialIndex !== -1) {
      newSections[testimonialIndex] = createSection(
        'testimonials-v2',
        () => import('./TestimonialsV2'),
        { type: 'masonry', cols: 3 }
      );
    }
  }

  return baseSections;
};
Enter fullscreen mode Exit fullscreen mode

Performance Impact {#performance}

The results speak for themselves:

Metric Before After Improvement
Bundle Size 450KB 157KB -65%
Time to Interactive 3.8s 2.3s -40%
First Contentful Paint 1.6s 0.8s -50%
Lighthouse Score 68 95+ +40%
Page Creation Time 2 hours 18 min -85%

Bundle Analysis Deep Dive

# Before - One massive chunk
main.js          450KB
vendors.js       1.2MB
Total Initial:   1.65MB

# After - Intelligent splitting  
main.js          157KB
framework.js     45KB
commons.js       89KB
sections/hero.js    23KB (loaded on demand)
sections/services.js 34KB (loaded on demand)
Total Initial:   291KB
Enter fullscreen mode Exit fullscreen mode

Real User Metrics

  • Bounce Rate: Decreased from 34% to 21%
  • Session Duration: Increased from 2.3min to 3.8min
  • Conversion Rate: Improved by 45%
  • Time to Interactive: 40% faster across all devices

Developer Experience Revolution {#dx-revolution}

Before vs After: New Developer Onboarding

Before (2 weeks):

  • Day 1-3: Understanding component architecture
  • Day 4-7: Learning data fetching patterns
  • Day 8-10: Understanding error handling
  • Day 11-14: First page creation

After (3 days):

  • Day 1: Configuration pattern explanation (2 hours)
  • Day 2: Practice with existing sections
  • Day 3: Create first page independently

Code Review Time Reduction

// Old way - 200+ line page file
// - Check imports
// - Verify data fetching
// - Validate error handling  
// - Review prop passing
// Average review time: 45 minutes

// New way - 10 line configuration
const SECTIONS = [
  createSection('hero', heroImport, heroSkeleton),
  createSection('features', featuresImport, featuresSkeleton)
];
// Average review time: 5 minutes
Enter fullscreen mode Exit fullscreen mode

Developer Happiness Metrics

We surveyed our development team:

  • "I enjoy working on pages": 23% → 89%
  • "I understand the codebase": 45% → 91%
  • "I can work independently": 34% → 87%
  • "Code reviews are helpful": 56% → 94%

Lessons Learned {#lessons}

1. Configuration > Code for Repetitive Patterns

When you find yourself copying code, ask: "Can this be configuration?"

2. Performance Should Be Automatic

Developers shouldn't think about:

  • Lazy loading (automatic with sections)
  • Code splitting (handled by dynamic imports)
  • Loading states (defined in skeleton config)

3. Error Boundaries at Every Level

// Section-level errors don't crash pages
<SectionErrorBoundary sectionName="testimonials">
  <TestimonialsSection />
</SectionErrorBoundary>
Enter fullscreen mode Exit fullscreen mode

4. Developer Experience Drives Product Quality

Happy developers → Better code → Better user experience

5. Migration Strategy: One Page at a Time

We migrated our entire application over 3 months:

  • Week 1-4: Build PageRenderer system
  • Week 5-8: Convert homepage + 2 key pages
  • Week 9-12: Migrate remaining pages
  • Zero downtime, minimal risk

Advanced Patterns

Context-Aware Data Fetching

// Components automatically get the right data
const TestimonialsSection = ({ pagelink }) => {
  // Automatically selects correct API based on pagelink
  const { data } = useTestimonials(pagelink);
  return <TestimonialCarousel data={data} />;
};

// Hook mapping
const useTestimonials = createHookMap({
  'genai': useGenAiTestimonials,
  'mobile-app': useMobileAppTestimonials,
  'default': useGeneralTestimonials
}, 'default');
Enter fullscreen mode Exit fullscreen mode

Multi-Level Error Recovery

// Automatic retry for transient errors
const SectionErrorBoundary = ({ children, sectionName }) => {
  const [retryCount, setRetryCount] = useState(0);

  const handleError = (error) => {
    const errorType = classifyError(error);

    if (errorType.autoRetry && retryCount < 3) {
      setTimeout(() => {
        setRetryCount(prev => prev + 1);
        // Component automatically retries
      }, errorType.retryDelay);
    }
  };

  return (
    <ErrorBoundary onError={handleError}>
      {children}
    </ErrorBoundary>
  );
};
Enter fullscreen mode Exit fullscreen mode

Future Innovations

AI-Powered Page Optimization

We're exploring ML models that optimize page configurations based on user behavior:

// Future: AI suggests optimal section arrangements
const optimizedSections = await optimizePageLayout({
  baseSections: HOMEPAGE_SECTIONS,
  userSegment: currentUser.segment,
  conversionGoal: 'signup',
  timeOfDay: new Date().getHours()
});
Enter fullscreen mode Exit fullscreen mode

Real-Time Configuration Updates

// Deploy new page layouts without code changes
const sections = await fetchSectionsFromCMS(pageName);
return <PageRenderer sections={sections} />;
Enter fullscreen mode Exit fullscreen mode

Conclusion

Configuration-driven architecture isn't just a pattern—it's a paradigm shift. By treating pages as data structures, we unlocked:

  • Unprecedented flexibility (A/B testing through config)
  • Automatic performance (lazy loading built-in)
  • Developer happiness (simple mental model)
  • Business agility (rapid iteration)

The best part? You can start small. Convert one page to this pattern, measure the impact, then expand.

Remember: The goal isn't to build software. It's to solve problems efficiently.


Resources

Connect with me:

What patterns have transformed your development experience? Share in the comments below!

Top comments (0)