DEV Community

Cover image for πŸ› οΈ Don't Use React Imports Like This - Use the Wrapper Pattern Instead
The Vlpha
The Vlpha

Posted on

πŸ› οΈ Don't Use React Imports Like This - Use the Wrapper Pattern Instead

Ever opened a React component and been greeted by this beautiful mess?

import React from 'react';
import { Button } from './components/ui/Button';
import { Modal } from './components/ui/Modal';
import { Input } from './components/forms/Input';
import { TextArea } from './components/forms/TextArea';
import { Card } from './components/layout/Card';
import { Header } from './components/layout/Header';
import { Footer } from './components/layout/Footer';
import { LoadingSpinner } from './components/feedback/LoadingSpinner';
import { ErrorBoundary } from './components/utils/ErrorBoundary';
import { Avatar } from './components/display/Avatar';
import { Badge } from './components/display/Badge';
import { Tooltip } from './components/overlay/Tooltip';
// ... *screams internally* 😱
// At this point, I'm basically importing half the internet
Enter fullscreen mode Exit fullscreen mode

If you've been writing React for any amount of time, you've probably seen (or written) imports like this. I know I have, and it's painful every single time. πŸ˜…

The Real Problem with Direct Imports

Let's be honest about what's actually happening here:

1. Import Hell
Your component files become 30% imports, 70% actual logic. New developers spend more time figuring out import paths than understanding business logic.

2. Refactoring Nightmare
Want to move your Button component from ui/ to common/? Congratulations, you now have 47 files to update. Hope you didn't miss any!

3. Inconsistent Paths
Different developers import the same component differently:

// Developer A (the relative path warrior)
import { Button } from '../../../components/ui/Button';

// Developer B (the alias enthusiast)
import { Button } from '@/components/ui/Button';

// Developer C (the "it works on my machine" person)
import { Button } from './components/ui/Button';

// Developer D (the one who gave up)
import Button from '../../../../somewhere/maybe/Button';
Enter fullscreen mode Exit fullscreen mode

4. Mental Overhead
Every time you need a component, you're playing "guess the import path" instead of focusing on solving actual problems.

The Wrapper Pattern Solution

Here's the pattern that changed everything for me:

// Before: Import spaghetti 🍝
import { Button } from './components/ui/Button';
import { Modal } from './components/ui/Modal';
import { Input } from './components/forms/Input';

// After: Zen mode activated πŸ§˜β€β™‚οΈ
import { Button, Modal, Input } from './components';
Enter fullscreen mode Exit fullscreen mode

Step-by-Step Implementation:

Step 1: Create Your Index Files
Start with your main components directory:

// components/index.js - The hero we didn't know we needed
export { Button } from './ui/Button';
export { Modal } from './ui/Modal';
export { Card } from './ui/Card';
export { Input } from './forms/Input';
export { TextArea } from './forms/TextArea';
export { Header } from './layout/Header';
export { Footer } from './layout/Footer';
// One file to rule them all πŸ’
Enter fullscreen mode Exit fullscreen mode

Step 2: Create Subdirectory Indexes
For better organization, create index files in subdirectories:

// components/ui/index.js - The cool kids' table
export { Button } from './Button';
export { Modal } from './Modal';
export { Card } from './Card';
export { Badge } from './Badge';
export { Avatar } from './Avatar';

// components/forms/index.js - Where validation lives
export { Input } from './Input';
export { TextArea } from './TextArea';
export { Select } from './Select';
export { Checkbox } from './Checkbox';

// components/layout/index.js - Structure squad
export { Header } from './Header';
export { Footer } from './Footer';
export { Sidebar } from './Sidebar';
export { Container } from './Container';
Enter fullscreen mode Exit fullscreen mode

Step 3: Update Your Main Index

// components/index.js - The grand finale
export * from './ui';      // All the pretty things
export * from './forms';   // All the input magic  
export * from './layout';  // All the structure
export * from './utils';   // All the helper wizards
Enter fullscreen mode Exit fullscreen mode

Advanced Patterns

Conditional Exports

// components/index.js
export { Button } from './ui/Button';
export { Card } from './ui/Card';

// Only export the secret dev tools when we're feeling rebellious 😈
if (process.env.NODE_ENV === 'development') {
  export { DebugPanel } from './dev/DebugPanel';
  export { ComponentX } from './dev/ComponentX'; // The files we never talk about
}
Enter fullscreen mode Exit fullscreen mode

Named Exports with Aliases

// components/index.js - When you need to get creative with naming
export { Button } from './ui/Button';
export { Button as SuperButton } from './ui/PrimaryButton'; // It's SUPER! πŸ¦Έβ€β™‚οΈ
export { Modal as Dialog } from './ui/Modal'; // Same thing, different name
export { Modal as PopupThingy } from './ui/Modal'; // For when you're feeling casual
Enter fullscreen mode Exit fullscreen mode

TypeScript Support

// components/index.ts
export { Button, type ButtonProps } from './ui/Button';
export { Modal, type ModalProps } from './ui/Modal';
export { Input, type InputProps } from './forms/Input';

// Re-export types
export type { 
  ComponentSize, 
  ComponentVariant 
} from './types';
Enter fullscreen mode Exit fullscreen mode

Pro Tips and Best Practices

1. Keep It Organized

// βœ… Good: Organized like a boss
export * from './ui';        // All the shiny UI stuff
export * from './forms';     // Where user input goes to party
export * from './layout';    // The architects of the page
export * from './utils';     // The unsung heroes doing the dirty work

// ❌ Bad: Chaos incarnate
export { Button } from './ui/Button';
export { validateEmail } from './utils/validation';
export { Input } from './forms/Input';
export { formatDate } from './utils/date';
// *cries in maintainability* 😭
Enter fullscreen mode Exit fullscreen mode

2. Use Barrel Exports Wisely

// βœ… Good: Explicit and trustworthy
export { Button } from './Button';
export { Modal } from './Modal';

// ⚠️ Be careful with: The "trust me bro" approach
export * from './Button';
export * from './Modal';
// Can cause circular dependencies (and developer tears)
Enter fullscreen mode Exit fullscreen mode

3. Document Your Structure

// components/index.js
/**
 * Component Library Exports πŸ“š
 * 
 * UI Components: Button, Modal, Card, Badge, Avatar (the pretty ones)
 * Form Components: Input, TextArea, Select, Checkbox (the data collectors)
 * Layout Components: Header, Footer, Sidebar, Container (the structure crew)
 * Utility Components: ErrorBoundary, LoadingSpinner (the problem solvers)
 * 
 * Pro tip: If you can't find a component, it probably doesn't exist yet.
 * Or maybe it's hiding in the 'experimental' folder πŸ‘€
 */

export * from './ui';
export * from './forms';
export * from './layout';
export * from './utils';
Enter fullscreen mode Exit fullscreen mode

4. Handle Default Exports

// For default exports, you gotta be explicit (JavaScript quirks, am I right?)
export { default as HomePage } from './pages/HomePage';
export { default as AboutPage } from './pages/AboutPage';
export { default as ContactPage } from './pages/ContactPage';
export { default as NotFoundPage } from './pages/404'; // The page we never want to see

// Then use like a civilized developer:
import { HomePage, AboutPage, NotFoundPage } from './pages';
Enter fullscreen mode Exit fullscreen mode

Common Pitfalls to Avoid

1. Circular Dependencies

// ❌ This creates the dreaded circular dependency monster πŸ‘Ή
// components/CoffeeOrder.js
import { TeaOrder } from './'; // "I need tea to make coffee" - wait, what?

// components/TeaOrder.js  
import { CoffeeOrder } from './'; // "I need coffee to make tea" - this is getting weird

// components/index.js
export { CoffeeOrder } from './CoffeeOrder';
export { TeaOrder } from './TeaOrder';
// Result: JavaScript.exe has stopped working
Enter fullscreen mode Exit fullscreen mode

2. Over-Exporting

// ❌ Don't export your dirty laundry
export * from './internal/SecretSauce';
export * from './private/EmbarrassingComponent';
export * from './experimental/WhoKnowsIfThisWorks';

// βœ… Be selective about your public API (like a good bouncer)
export { Button } from './ui/Button';
export { Modal } from './ui/Modal';
// SecretSauce stays secret, as it should 🀫
Enter fullscreen mode Exit fullscreen mode

3. Deep Nesting

// ❌ Too deep - we're not drilling for oil here
import { Button } from './components/ui/interactive/buttons/primary/large/red/glowing';

// βœ… Keep it reasonable (your future self will thank you)
import { Button } from './components';
Enter fullscreen mode Exit fullscreen mode

-----

The wrapper pattern isn't just about cleaner importsβ€”it's about:

Maintainable code that scales with your team
Better developer experience for everyone
Consistent architecture across your application
Easier refactoring when requirements change

This simple pattern has transformed how I approach React architecture. It takes 5 minutes to implement but saves hours of frustration down the road.

Top comments (0)