Featured resource
Tech Upskilling Playbook 2025
Tech Upskilling Playbook

Build future-ready tech teams and hit key business milestones with seven proven plays from industry leaders.

Learn more
  • Labs icon Lab
  • Core Tech
Labs

Guided: Structure TypeScript Applications with Barrel Files and Module Re-exports

Transform chaotic imports into clean, maintainable code by building a production-ready React design system with TypeScript barrel files. In this hands-on lab, you'll experience the pain of managing 20 components with complex import paths, then systematically apply barrel patterns to achieve an 85% reduction in import statements. You'll master progressive barrel techniques from basic utility groupings to enterprise-grade main barrels, learning when each pattern optimizes developer experience versus performance. By the end, you'll have built a complete design system featuring buttons, inputs, cards, modals, design tokens, and utilities—all organized with professional-grade TypeScript patterns that scale to enterprise applications.

Labs

Path Info

Level
Clock icon Beginner
Duration
Clock icon 45m
Last updated
Clock icon Sep 04, 2025

Contact sales

By filling out this form and clicking submit, you acknowledge our privacy policy.

Table of Contents

  1. Challenge

    Introduction

    Welcome to the Guided: Structure TypeScript Applications with Barrel Files and Module Re-exports. This hands-on Code Lab teaches you how to organize a React design system using TypeScript barrel files.

    What You'll Work With

    A production-ready React design system with:

    • 20+ Components: Forms (Input, Select, Checkbox), Layout (Grid, Container), Feedback (Alert, Progress), Navigation (Tabs, Breadcrumb), and more
    • Design Tokens: Colors, spacing, typography, shadows
    • Utilities: Formatters, validators, theme helpers
    • Interactive Demo: Live showcase showing only 5 components initially (to reduce cognitive load)

    Important: All components are pre-built. You'll focus on organizing imports, not creating components. This keeps the lab focused and completable in 30-45 minutes.

    The Problem

    Your App.tsx has 20+ imports that reveal common issues:

    import { Badge } from './components/Badge/Badge';  // Repetitive paths
    import { Button } from './components/Button/Button';  // Deep nesting
    import { HomeIcon } from './components/icons/navigation/HomeIcon';  // Icon complexity
    import { formatCurrency, formatDate } from './utils/formatters';  // Multiple per file
    import { isValidEmail } from './utils/validators';  // Must know exact location
    

    This gets worse at scale. Production apps can have 60-80 imports per file.

    Why This Matters

    Import management is a daily friction that grows over time:

    • Developers waste time finding imports
    • New team members struggle with file structure
    • Refactoring breaks imports across files
    • Code reviews mix import changes with business logic

    Success Metrics

    You'll transform chaotic imports into organized, scalable patterns:

    // From this (20+ lines)
    import { Button } from './components/Button/Button';
    import { Input } from './components/Input/Input';
    import { Select } from './components/Select/Select';
    // ... many more
    
    // To this (organized by category)
    import { Button, Card, Modal } from './components/core';
    import { Input, Select, Checkbox } from './components/forms';
    import { Grid, Container } from './components/layout';
    
    // Or even this (single import)
    import { Button, Input, Grid } from './design-system';
    

    Your Learning Path

    • Introduction (here): Setup and understand the problem
    • Step 2: Experience import chaos with hands-on tasks
    • Step 3: Create your first barrel files
    • Step 4: Scale with component barrel files
    • Step 5: Implement Icon System Barrels
    • Step 6: Create unified design system architecture
    • Step 7: Optimize for production

    Getting Started

    Project Structure

    The design system files can be found under the /src directory in the filetree as the following:

    • src/components/ - React components
    • src/tokens/ - Design tokens
    • src/utils/ - Helper functions
    • src/types/ - TypeScript types
    • src/styles/ - CSS variables
    • src/App.tsx - Demo showcase

    Setup Commands

    npm run dev          # Start dev server
    npm run typecheck    # Verify TypeScript
    npm run build        # Production build
    

    After running the npm run dev in the terminal, the application will be available on {{localhost:3000}} for you to follow along.

    You can either click the link provided above, or refresh the Code Labs Web Browser tab to see the application running live.

    Npm Scripts

    To keep the focus on learning the barrel file pattern, a few npm scripts are provided in package.json to handle file reorganization for you.

    Later in the lab, you will also use a script to analyze bundle sizes.

    Solution

    info> A solution directory is provided at the root of the workspace where you can find the final code of each task to help you to get unblocked if needed.

    Continue to Step 2 to explore the current import structure.

  2. Challenge

    Experience the Import Chaos

    In this step, you'll explore the current import structure and feel the pain of managing multiple imports before learning how barrel files solve this problem.

    The Current State

    Open src/App.tsx in the editor. You'll immediately notice the import chaos: more than 20 import lines filling your entire screen.

    The Pain is Real

    • 20+ lines of imports before the actual code even begins
    • Repetitive patterns like ./components/[Name]/[Name] everywhere - Deep nesting that forces you to know the exact folder structure
    • Unused imports such as the formatNumber utility, added “just in case” but never called

    Task 1: Add Number Formatting

    In this task, you will identify an unused import in App.tsx. The formatNumber function is included in the file but not applied. You will update the Product Card to use formatNumber so the large number displays in a clean, formatted style in your browser.

    Understanding the Problem

    This simplified example has:

    • 19 components
    • 10+ utilities (but only using 3)
    • 4 token files (not even imported)
    • 22+ import lines

    Production design systems typically have:

    • 50+ components
    • 30+ utilities
    • 10+ token files
    • 60+ imports per file

    Each new feature requires:

    1. Finding the correct file location
    2. Updating import statements
    3. Remembering exact paths
    4. Fixing broken imports when files move

    You've experienced the import chaos firsthand. You see how:

    • Import statements multiply quickly
    • Useful utilities remain undiscovered
    • Path memorization becomes a burden
    • Refactoring risk increases ## What's Next

    In Step 3, you'll create barrel files for design tokens (colors, spacing, typography) and utility functions, seeing immediate benefits.

    Continue to Step 3: Create Your First Barrel Files.

  3. Challenge

    Create Your First Barrel Files

    You have seen how multiple imports create clutter in App.tsx. In this step, you will create a barrel file to centralize your utility exports. This will reduce the number of import statements and make your code easier to manage.

    Current Utils Structure

    The src/utils/ folder contains:

    • Formatting utilities: formatCurrency, formatDate, formatNumber in the formatters.ts file
    • Validation utilities: isValidEmail and others that are not yet used in App.tsx, in the validators.ts file

    Currently, these utilities are imported separately in App.tsx, which adds extra import lines.

    To solve this, you will create a new file in the project. Code Lab supports two ways to do this, and you can choose the method you prefer.

    info> You can create new files in two ways:

    • Terminal: Run touch src/utils/index.ts
    • Filetree: Open the utils directory → Click the horizontal ellipsis (...) → Select New file.
    • Alternatively, select utils → Press Tab → Press Enter → Select New file.

    Task 1: Create Your First Barrel File ## Task 2: Update App.tsx to Use the Barrel File

    Now that you have created the src/utils/index.ts file. Open App.tsx and make the following changes. ## Benefits You've Achieved:

    1. Reduced Import Lines: From 2 lines to 1 line (50% reduction)
    2. Single Source: All utilities come from ./utils instead of remembering specific files
    3. Simplified Paths: You don't need to know if formatNumber is in formatters or validators
    4. Easier Refactoring: Moving utilities between files won't break imports
    5. Better Discoverability: All utilities available from one import

    Next, you will further your barrel file creation experience by creating a barrel file to export all the TypeScript design tokens.

    Task 3: Create Design Token Barrels

    Design tokens are the foundation of any design system. They ensure consistency across all components and enable theming capabilities.

    What's Already Built:

    • src/tokens/ directory contains individual TypeScript token files: colors.ts, spacing.ts, typography.ts, shadows.ts
    • src/styles/tokens.css file with CSS variables - auto-generated from TypeScript tokens

    Your Task: Create a barrel file to export all the TypeScript design tokens, making them easy to import. ### Why Design Tokens Matter:

    • Consistency: All components use the same design values
    • Theming: Easily swap tokens for dark mode or brand variations
    • Maintainability: Update design values once, apply everywhere
    • Cross-Platform: Export as CSS, JS, or platform-specific formats
    • Import Flexibility: The barrel file enables flexible import patterns.
    // Import just colors
    import { colors } from './tokens/colors';
    
    // Import all tokens via barrel (after completing this task)
    import { colors, spacing, typography, shadows } from './tokens';
    
    // Import CSS variables for traditional styling (already available)
    import './styles/tokens.css';
    ``` ## What's Next
    
    Continue to Step 4: Scale with Component Barrel Files where you'll  create component barrel files and see how this pattern scales to eliminate all the repetitive component imports.
  4. Challenge

    Scale with Component Barrel Files

    You've simplified utility imports with barrel files. Now, you'll organize 20+ React components into logical barrels by category (forms, layout, feedback, navigation), implementing progressive disclosure patterns that power enterprise design systems.

    Component Library Structure

    In this step, you'll transform your flat component structure into organized categories. After running the npm scripts in Task 1, src/components/ will have the following structure:

    components/
    ├── core/
    │   ├── index.ts       # Generated barrel file with exports
    │   ├── Button/        # Button component (moved from root)
    │   ├── Card/          # Card component (moved from root)
    │   └── Modal/         # Modal component (moved from root)
    ├── forms/
    │   ├── index.ts       # Generated barrel file with exports
    │   ├── Input/         # Input component (moved from root)
    │   ├── Select/        # Select component (moved from root)
    │   ├── Checkbox/      # Checkbox component (moved from root)
    │   ├── Radio/         # Radio component (moved from root)
    │   └── Toggle/        # Toggle component (moved from root)
    ├── layout/
    │   ├── index.ts       # Generated barrel file with exports
    │   ├── Container/     # Container component (moved from root)
    │   ├── Grid/          # Grid component (moved from root)
    │   ├── Spacer/        # Spacer component (moved from root)
    │   └── Divider/       # Divider component (moved from root)
    ├── feedback/
    │   ├── index.ts       # Generated barrel file with exports
    │   ├── Badge/         # Badge component (moved from root)
    │   ├── Alert/         # Alert component (moved from root)
    │   ├── Progress/      # Progress component (moved from root)
    │   └── Spinner/       # Spinner component (moved from root)
    └── navigation/
        ├── index.ts       # Generated barrel file with exports
        ├── Tabs/          # Tabs component (moved from root)
        ├── Breadcrumb/    # Breadcrumb component (moved from root)
        └── Link/          # Link component (moved from root)
    

    Task 1: Create Component Category Directories and Barrel Files

    Run the provided npm scripts from package.json to set up these categories automatically. Each script creates the directory, moves the components, and generates a barrel files with the correct exports. info> If your application is running with npm run dev, it will appear broken after organizing the components into categories. This is expected and will be resolved once you complete Task 2 and update the imports in App.tsx. ## Task 2: Update App.tsx Imports

    Time to clean up those 19 component imports and use the new category barrels! Next, you will create the main components index.ts file that exports all category barrels, making it easy to import from any category.

    info> You can create new files in two ways:

    • Terminal: Run touch src/components/index.ts
    • Filetree : Open the components directory → Click the horizontal ellipsis (...) → Add New file

    Task 3: Create the Main Components Barrel

    Now that you’ve created the main components barrel, you can unify your imports even further.

    The provided refactor:unified script will replace component imports across the project to use the new top-level components/index.ts file.

    Task 4: Run Unified Refactor Script ### Achievement Unlocked

    • 57% reduction in import statements
    • Clean file header with organized imports
    • Scalable pattern for any number of components

    Real-World Impact

    In production apps with 50+ components:

    • Without barrels: 50+ import lines
    • With barrels: 1 import line
    • Time saved: Hours of import management
    • Easier refactoring: Move components without breaking imports

    What's Next

    In Step 5: Implement Icon System Barrels, you'll explore:

    • Organize the SVG icon component library with barrel files
    • Apply selective exports to keep the bundle lean
    • Use aliasing techniques to simplify imports
    • Build a scalable icon system that improves the developer experience

    Continue to Step 5: Implement Icon System Barrels.

  5. Challenge

    Implement Icon System Barrels

    You have mastered component organization with category barrels. Now it is time to build a production-ready icon system. You will apply selective exports, category organization, and aliasing strategies that mirror patterns used by popular libraries like Heroicons, Lucide, and Material Icons.

    Icon Systems at Scale

    Modern apps rely on 100+ icons. Without structure, imports become inconsistent and naming turns chaotic. In this step, you will build a professional icon system that stays semantic, tree-shaking friendly, and easy to maintain.

    Your first task is to set up selective exports to define the icon system’s public API.

    Task 1: Selective Exports - The Foundation

    The icon components are already provided. Your job is to create selective exports to define the public API.

    Pre-provided components:

    • src/components/icons/navigation/HomeIcon.tsx
    • src/components/icons/status/CheckIcon.tsx ### Complete File Structure After All Tasks:
      src/components/icons/
      ├── navigation/
      │   ├── HomeIcon.tsx        # (pre-provided)
      │   └── index.ts            # Task 5.1 + 5.3
      ├── status/
      │   ├── CheckIcon.tsx       # (pre-provided)
      │   └── index.ts            # Task 5.2 + 5.3
      └── index.ts                # Task 5.4
    

    This creates a flexible icon system where you can import the icons in many different ways:

    import { HomeIcon } from './components/icons/navigation' //import icon component from its directory
    import { DashboardIcon } from './components/icons/navigation'  //import icon component using its alias 
    import { HomeIcon, DashboardIcon, CheckIcon, SuccessIcon } from './components/icons'  //import icon components from barrel file
    

    Pro Tip: Aliases improve flexibility, but avoid overusing them. Too many names for the same icon can cause confusion. Focus on semantic meaning - SuccessIcon vs CheckIcon, rather than visual similarity.

    Professional Icon System Benefits

    Developer Experience:

    // Semantic clarity
    import { SuccessIcon, ErrorIcon } from './icons/status'; 
    

    Bundle Optimization:

    • Explicit re-exports improve tree-shaking
    • Aliases don't increase bundle size
    • Category imports let you load only what you need

    What's Next

    In Step 6: Create the Main Design System Barrel, you'll:

    • Combine components, icons, and utilities in a single entry point
    • Learn trade-offs between convenience and performance
    • Implement progressive disclosure patterns
    • Build the ultimate design system API

    Continue to Step 6: Create Unified Design System Architecture

  6. Challenge

    Create the Main Design System Barrel

    You've created barrels for components, icons, and utilities. Now you'll combine them into a single design system entry point - a pattern used by Material-UI, Chakra UI, and Ant Design.

    You'll master the trade-offs between convenience and performance, learning when to use different import strategies for optimal developer experience and bundle size.

    Why Import Strategy Matters

    Creating a main design system barrel gives developers convenience but comes with bundle size implications. In this step, you'll implement both approaches and see the trade-offs in action.

    Import Strategy Trade-offs

    Professional design systems support multiple import patterns. Here's how they perform in production:

    | Strategy | Use Case | Typical Bundle Impact | When to Use | |----------|----------|----------------------|-------------| | Convenience | Prototyping, MVPs | Loads most of the library | Early development | | Category | Balanced apps | Loads only used categories | Most production apps | | Direct | Performance-critical | Loads only what you import | High-traffic pages |

    Industry Note: Material-UI and Ant Design provide both convenience and optimized imports, letting teams choose based on their performance requirements.

    In Task 1, you'll create the convenience import strategy by building a main barrel file that aggregates all your existing category barrels. This single entry point lets developers import everything from one location - exactly how popular libraries like Material-UI work when you import from @mui/material.

    Task 1: Create the Unified Design System Entry Point

    Build the main barrel that combines all your existing category barrels into a single, convenient import source. See the difference? Components now use a single import source - ./design-system, while icons still use category imports -./components/icons. This mixed approach shows both strategies in action.

    Task 2: Demonstrate Import Strategy Trade-offs

    Update your app to showcase the difference between convenience imports and category imports.

    Pro Tip: Aliased exports let you provide domain-specific names - StatusBadge, ContentCard, FormInput, while keeping generic component names internally. This is how Material-UI provides both TextField and Input components.

    Import Strategy Decision Guide

    Industry Note

    Like Material-UI and Ant Design, your system now offers both convenience and optimized import paths. The right choice depends on the balance you need between developer speed and bundle performance.

    Choosing the Right Strategy

    Convenience Import (The Speed Choice):

    // ✅ Single source of truth
    // ⚠️ Imports entire design system
    import { Button, Modal, HomeIcon } from './design-system';
    

    Category Import (The Balanced Choice):

    // ✅ Good tree-shaking
    // ✅ Clear organization
    import { Button } from './components/core';
    import { HomeIcon } from './components/icons';
    

    Direct Import (The Performance Choice):

    // ✅ Minimal bundle
    // ⚠️ More verbose
    import Button from './components/core/Button/Button';
    

    What's Next

    ✅ Steps 1-5: Component organization and icon systems
    ✅ Step 6: Unified design system with strategic imports
    🎯 Step 7: Production optimization with explicit exports and performance monitoring

    Continue to Step 7: Optimize for Production.

  7. Challenge

    Optimize for Production

    In Step 6, you learned multiple import strategies - convenience, category, direct. Now you'll optimize for production with explicit exports, performance monitoring, and advanced barrel patterns.

    Current vs Optimized

    Current Pattern (Good for development):

    // Wildcard exports - convenient but less optimal for tree-shaking
    export * from './Button';
    export * from './Modal';
    

    Production Pattern (Optimal for tree-shaking):

    // Explicit exports - maximum tree-shaking effectiveness  
    export { Button } from './Button';
    export { Modal } from './Modal';
    

    The import experience stays exactly the same:

    import { Button, Modal } from './design-system/components/core';
    

    Production Optimization Impact

    | Pattern | Bundle Size | Tree-Shaking | Bundle Reduction | When to Use | |---------|-------------|--------------|------------------|-------------| | Wildcard | ~25KB | Good | Baseline | Development, small teams | | Explicit | ~15KB | Excellent | 30-40% smaller | Production, enterprise scale |

    Explicit exports can reduce bundle sizes significantly in large applications while maintaining the same developer experience.

    Task 1: Convert to Explicit Exports

    In this task, you will refactor your component barrel file to use explicit exports for better tree-shaking. ### Why Explicit Exports Work

    Bundlers like Vite can eliminate unused code more effectively when they know exactly what each module exports, rather than needing to analyze entire modules with wildcard exports.

    Tree-Shaking Pitfall: Be aware that the use of export * can prevent bundlers from tree-shaking unused code because they must assume potential side effects. Use explicit exports in production barrels.

    Side Effects Gotcha: If your modules include side effects - e.g., setting globals, then explicit exports alone may not guarantee tree-shaking. Consider adding "sideEffects": false in your package.json.

    Bonus Task: Analyze Bundle Performance

    See the real impact of your explicit exports with bundle analysis.

    1. Run bundle analysis:

      npm run bundle:analyze
      

      This builds your project and analyzes the bundle in the terminal (perfect for code labs).

    2. Optional: For detailed visual analysis, run:

      npm run build:analyze
      

      This generates dist/stats.html with an interactive bundle visualization.

    3. Compare the results before and after explicit exports. You should see:

      • Total bundle size and breakdown
      • Tree-shaking effectiveness percentage
      • Performance recommendations in your terminal
    4. Mini Performance Challenge: Open the generated analysis and answer:

      • What's the bundle size difference between wildcard and explicit exports?
      • Which import strategy shows the best tree-shaking percentage?
      • How much smaller is your main chunk compared to importing everything?
    Bonus Task: See the Performance Impact

    Compare bundle sizes before and after optimization to see real tree-shaking benefits.

    1. Test with wildcard exports (temporarily revert one file):

      // In src/components/core/index.ts - temporarily change back
      export * from './Button';
      export * from './Card';
      
    2. Run analysis: npm run bundle:analyze

    3. Revert to explicit exports and run analysis again:

      // Restore explicit exports
      export { Button, type ButtonProps } from './Button';
      export { Card, type CardProps } from './Card';
      
    4. Compare the bundle sizes - you should see the explicit exports version is smaller and more tree-shake friendly.

    ## Performance Monitoring in Practice

    Large teams use performance monitoring to:

    • Track Bundle Growth: Prevent gradual size increases
    • Guide Import Decisions: Help developers choose optimal patterns
    • Measure Tree-Shaking: Monitor optimization effectiveness

    Enterprise Usage Patterns

    Development Teams (5-50 developers):

    // Category imports for organized development
    import { Button } from './design-system/components/core';
    import { StatusBadge } from './design-system/components/display';
    

    Performance-Critical Applications:

    // Direct imports for maximum optimization
    import Button from './design-system/components/core/Button/Button';
    

    Prototyping and MVPs:

    // Convenience imports for speed
    import { Button, StatusBadge } from './design-system';
    ``` ### Note on Advanced Enterprise Patterns
    
    The current design system organization is category based and it is great for component libraries, but enterprise applications serving global audiences need advanced optimization patterns including feature-based organization and performance monitoring.
    
    
    

    / Category-Based Pattern /

    src/components/ ├── core/index.ts // Button, Modal, Card ├── display/index.ts // StatusBadge, ContentCard
    ├── forms/index.ts // FormInput

    
    
    

    / Feature-Based Pattern /

    src/features/ ├── authentication/index.ts // LoginForm, SignupModal, AuthButton ├── dashboard/index.ts // MetricCard, DashboardBadge, ChartModal ├── commerce/index.ts // ProductCard, CheckoutButton, PaymentForm

    
    Enterprise Apps Use Feature-Based patterns so teams can work independently on features without barrel conflicts, and dead feature code elimination is more effective.
    
    #### Performance Impact: 
    
    Feature-based barrels combined with explicit exports can achieve near-total elimination of unused exports in many large-scale applications.
    
     ## What You've Accomplished
    
    ## Industry Best Practices You've Mastered
    
    📦 Bundle Optimization: Explicit exports reduce bundle sizes by 30-40%  
    🔍 Performance Monitoring: Development-time guidance prevents regressions  
    ⚖️ Balanced Architecture: Multiple import/export strategies for different use cases  
    🏢 Enterprise Scale: Patterns that work from 2-200+ developers
    
    Congratulations! You've built a production-ready design system using the same optimization patterns as companies serving millions of users worldwide.
    
    You're ready to scale this architecture for real-world applications! 

Carolina Powers is a Software Engineer at Pluralsight with a background in teaching and a passion for learning. When she is not working, she enjoys spending time with her family, traveling, going to the beach, being active and teaching her son a new language.

What's a lab?

Hands-on Labs are real environments created by industry experts to help you learn. These environments help you gain knowledge and experience, practice without compromising your system, test without risk, destroy without fear, and let you learn from your mistakes. Hands-on Labs: practice your skills before delivering in the real world.

Provided environment for hands-on practice

We will provide the credentials and environment necessary for you to practice right within your browser.

Guided walkthrough

Follow along with the author’s guided walkthrough and build something new in your provided environment!

Did you know?

On average, you retain 75% more of your learning if you get time for practice.