If you've been building React apps with Vite, you've probably run into this question: Should I create an index.ts
file to export everything from a folder, or should I just import components directly? It's one of those decisions that seems small but can really impact your project's organization and developer experience.
Similar to knowing how to properly set up your Vite applications environment variables.
I've used both approaches extensively, and honestly, there's no universal "right" answer. But there are definitely situations where one approach shines over the other. Let me walk you through when to use each one.
What Are Index Files and Named Exports?
Before we dive into the when and why, let's make sure we're on the same page about what these approaches actually look like.
Index Files (Barrel Exports)
An index file acts like a "barrel" that re-exports everything from a folder. Instead of importing from individual files, you import from the folder itself.
This is why it's also hugely important to setup a good folder structure from the very beginning in your Vite applications.
// components/index.ts
export { Button } from './Button';
export { Modal } from './Modal';
export { Card } from './Card';
// In your component
import { Button, Modal } from '../components';
Named Exports (Direct Imports)
With named exports, you import directly from the file that contains what you need.
// In your component
import { Button } from '../components/Button';
import { Modal } from '../components/Modal';
Both work, but they have different trade-offs that make them better for different situations.
When Index Files Are Your Best Friend
Index files really shine in certain scenarios. Here's when I reach for them:
Component Libraries and Design Systems
If you're building a component library or design system, index files are almost essential. They create a clean, consistent API for consuming your components.
// components/ui/index.ts
export { Button } from './Button';
export { Input } from './Input';
export { Select } from './Select';
export { Checkbox } from './Checkbox';
// Clean imports elsewhere
import { Button, Input, Select } from './components/ui';
This approach makes your component library feel more like a published package, which is exactly what you want.
Feature Modules
When you're organizing code by features, index files help create clear boundaries and make imports cleaner.
// features/auth/index.ts
export { LoginForm } from './LoginForm';
export { SignupForm } from './SignupForm';
export { useAuth } from './hooks/useAuth';
export { authSlice } from './store/authSlice';
// Much cleaner than multiple imports
import { LoginForm, useAuth, authSlice } from './features/auth';
Utilities and Helpers
For folders full of utility functions, index files prevent import statement bloat.
// utils/index.ts
export { formatDate } from './formatDate';
export { generateId } from './generateId';
export { debounce } from './debounce';
// One import line instead of three
import { formatDate, generateId, debounce } from './utils';
Hooks Directory
Custom hooks often work together, and index files make them easy to consume as a group.
// hooks/index.ts
export { useAuth } from './useAuth';
export { useLocalStorage } from './useLocalStorage';
export { useDebounce } from './useDebounce';
// Clean hook imports
import { useAuth, useLocalStorage } from './hooks';
When Named Exports Are the Way to Go
But index files aren't always the answer. Sometimes direct imports are cleaner and more maintainable:
Large Component Folders
If you have a huge components
folder with dozens of components, an index file becomes unwieldy. You end up with a massive file that's hard to maintain.
// This gets messy fast
export { Button } from './Button';
export { Modal } from './Modal';
export { Card } from './Card';
export { Table } from './Table';
export { Form } from './Form';
// ... 50 more components
In these cases, direct imports are cleaner:
import { Button } from './components/Button';
import { Modal } from './components/Modal';
One-Off Components
If a component is only used in one place and isn't part of a larger system, skip the index file. The overhead isn't worth it.
Index files can sometimes hurt tree-shaking and create larger bundles if not handled carefully. For performance-critical applications, direct imports give you more control.
Development Speed
When you're rapidly prototyping or in the early stages of a project, direct imports can be faster. You don't need to maintain index files as you add and remove components.
The Developer Experience Factor
Both approaches impact your daily development experience differently:
Index Files Pros:
- Cleaner import statements
- Easier to refactor (change internal file names without updating imports)
- Better IntelliSense in many editors
- Feels more "professional" for shared code
Named Exports Pros:
- Explicit about what you're importing
- Better for debugging (stack traces show actual file names)
- No maintenance overhead
- Easier to understand for new team members
Best Practices I've Learned
After using both approaches in various projects, here are some patterns that work well:
Use Index Files for Public APIs
If other teams or parts of your app will consume your code, index files create a cleaner interface.
Skip Index Files for Internal Implementation
Within a feature or component, direct imports are often cleaner and easier to follow.
Keep Index Files Small
If your index file has more than 10-15 exports, consider breaking it up or using direct imports instead.
Use TypeScript for Better DX
TypeScript makes both approaches better, but it especially helps with index files by providing better autocomplete and error checking.
// types/index.ts
export type { User } from './User';
export type { Product } from './Product';
export type { Order } from './Order';
Vite handles both approaches well, but some bundlers have quirks. Test your approach to make sure tree-shaking works as expected.
Common Mistakes to Avoid
Don't create index files everywhere just because you can. They add complexity and maintenance overhead.
Don't mix approaches randomly within the same project. Pick a pattern and stick with it for consistency.
Don't forget to update index files when you add or remove exports. This is a common source of build errors.
Don't create circular dependencies through index files. It's easier to do than you might think.
My Recommendation
Here's the approach I use in most Vite projects:
- Use index files for: UI component libraries, feature modules, utilities, and hooks
- Use direct imports for: Large component folders, one-off components, and internal implementation details
- Always use TypeScript to make either approach more maintainable
- Be consistent within each part of your application
The key is matching the approach to your specific use case. Don't just pick one and use it everywhere – think about what makes sense for each part of your project.
The Bottom Line
Both index files and named exports have their place in Vite projects. Index files are great for creating clean APIs and organizing related code, while named exports are perfect for simplicity and explicit imports.
The best approach depends on your project size, team preferences, and how the code will be consumed. Start with what feels natural for your project, and don't be afraid to mix approaches where it makes sense.
Remember, you can always refactor later. The important thing is to stay consistent within each part of your application and choose the approach that makes your code easier to understand and maintain.
Happy coding!