Modern Next.js apps grow fast — more pages, more components, more shared UI, more complexity. Without a clear component structure, imports become messy, files scatter across the repo, and onboarding new developers gets harder over time.
One simple pattern helps a lot: the Folder + index.ts component structure. This pattern keeps each component in its own directory and exposes a clean public API through a single index.ts file.
What is the Folder + index.ts pattern?
Instead of having a flat components directory with files like Footer.tsx, Header.tsx, and Button.tsx all at the same level, each component gets its own folder, plus an index.ts that re-exports what should be public.
components/
layout/
footer/
Footer.tsx
Footer.styles.ts
Footer.test.tsx
types.ts
hooks.ts
index.ts
With this setup, imports become:
// Before
import Footer from "@/components/layout/Footer";
// After
import Footer from "@/components/layout/footer";
When you import from a folder, bundlers like Vite, Webpack, or Next.js will automatically resolve the index.ts file in that folder, similar to how the web resolves index.html by default for a directory.
1. Clean, intuitive import paths
The first advantage is clean imports.
- You import from the directory, not a specific file.
- Paths read like folder navigation instead of file system plumbing.
- Refactoring the internal file names does not break your imports as long as the index.ts API stays stable.
For example, you can rename Footer.tsx to FooterRoot.tsx and update only index.ts, while all consumers still import from @/components/layout/footer.
// components/layout/footer/index.ts
export { default as Footer } from "./FooterRoot";
export type { FooterProps } from "./FooterRoot";
This decouples how the component is implemented from how it is consumed.
2. Colocation of everything a component needs
The Folder + index.ts pattern encourages colocation: everything related to a component lives next to it.
Typical files inside a component folder:
Footer.tsx– main UI componentFooter.styles.ts– styled-components, CSS-in-JS, or style utilsFooter.test.tsx– teststypes.ts– shared types for that componenthooks.ts– component-specific hooksindex.ts– public exports
This improves discoverability:
- New developers can open one folder and understand everything related to the component.
- You avoid “god” directories where styles, hooks, and tests are scattered across multiple top-level folders.
3. Extensibility as components grow
As your app matures, a simple component often turns into a small ecosystem of sub-components, hooks, and utilities. The Folder + index.ts pattern scales with that growth.
You might start with:
// index.ts
export { default as Footer } from "./Footer";
Later, you add more:
// index.ts
export { default as Footer } from "./Footer";
export { FooterSocial } from "./FooterSocial";
export { FooterNewsletter } from "./FooterNewsletter";
export * from "./types";
Consumers still import from the same folder:
import { Footer, FooterSocial } from "@/components/layout/footer";
You gain:
- Freedom to add new capabilities without touching every call site.
- A clear place (index.ts) to manage what is public vs. internal.
4. Barrel exports and public API control
The index.ts file is essentially a barrel file: it re-exports selected modules from within the folder. This lets you define a clean public API and hide implementation details.
Example:
// components/layout/footer/index.ts
export { default as Footer } from "./Footer";
export type { FooterProps } from "./Footer";
export { FooterSocial } from "./FooterSocial";
// Not exported: internal utilities, test helpers, etc.
Benefits:
- Consumers see a small, intentional surface area.
- Internals can be refactored freely as long as exports stay compatible.
- You keep imports consistent across the codebase, e.g. always from ”@/components/layout/footer” instead of mixing different file paths.
Barrel files can create circular dependencies if abused, so keep them scoped and focused at the component level and avoid giant, project-wide barrels.
5. Better tree shaking and bundle health
Properly structured exports make it easier for bundlers to apply tree shaking, removing unused code paths from the final bundle.
When each folder exposes:
- Named exports for sub-components, hooks, and utilities.
- Separate files for each concern.
…bundlers can include only what is actually imported. Combine this with good bundler configuration (like sideEffects: false for purely functional modules) to get smaller bundles and faster load times.
Example:
// package.json (for libraries or shared packages)
{
"sideEffects": false
}
6. Future-proofing your Next.js architecture
Next.js promotes co-located layouts, components, and route segments in the app directory, and a Folder + index.ts structure fits nicely with that philosophy.
As features evolve:
- You can split one large component into multiple smaller ones inside the same folder.
- You can introduce sub-components without changing import paths for existing consumers.
- You can add variants (e.g. FooterMinimal, FooterMarketing) and expose them as named exports.
All of this makes refactoring safer because external code depends only on the folder-level API, not on individual inner file names.
7. Consistency and onboarding
File structure is partly preference, but consistency is what really matters. Many React and Next.js codebases use some variation of:
- One folder per component.
- index.ts as the public API.
- Named files for concrete implementations (Footer.tsx, Button.tsx).
When the pattern is applied across the project:
- New developers can quickly infer where to find things.
- Code reviews are faster because everyone shares a mental model.
- IDE navigation becomes simpler since folder names describe the domain (e.g.
footer,header,sidebar).
8. TypeScript: exporting types from index.ts
In a TypeScript-based Next.js app, the Folder + index.ts pattern also improves type safety ergonomics.
Instead of importing types from deep paths, you can re-export them from the component’s index.ts:
// index.ts
export { default as Footer } from "./Footer";
export type { FooterProps } from "./Footer";
Consumers get a cleaner API:
import { Footer } from "@/components/layout/footer";
import type { FooterProps } from "@/components/layout/footer";
This keeps types aligned with their component and makes refactors less error-prone because you centralize the types that are meant to be public.
Practical guidelines for adopting this pattern
When introducing the Folder + index.ts structure in an existing Next.js project, these steps help:
-
Start with new components
Use the pattern for any new component first, so the codebase gradually moves toward the new structure.
-
Refactor high-traffic components
Move frequently used components (like layout pieces and shared UI) into folders with
index.tsto maximize DX gains early. -
Keep barrels focused
Create
index.tsper component folder, but avoid large root-level barrels that export your entire project. This can lead to circular dependencies and make refactoring more complex. -
Document the convention
Add a short “Component structure” section to your project’s README or contributing guide so all team members follow the same pattern.
-
Watch for circular dependencies
Be careful not to import the barrel from files it re-exports; prefer relative imports inside the folder and barrel imports outside it.
Example: Footer component using Folder + index.ts
Here is a minimal example tying it all together:
// components/layout/footer/Footer.tsx
import type { FooterProps } from "./types";
import { useFooterLinks } from "./hooks";
const Footer = ({ showSocial }: FooterProps) => {
const links = useFooterLinks();
// render JSX...
};
export default Footer;
// components/layout/footer/types.ts
export type FooterProps = {
showSocial?: boolean;
};
// components/layout/footer/hooks.ts
export const useFooterLinks = () => {
// custom logic...
return [];
};
// components/layout/footer/index.ts
export { default as Footer } from "./Footer";
export type { FooterProps } from "./types";
Usage:
import { Footer } from "@/components/layout/footer";
This small abstraction buys a lot: clean imports, clear boundaries, and a path that scales as your app and team grow.
If your Next.js project is starting to feel hard to navigate, adopting the Folder + index.ts pattern is a low-risk, high-impact improvement to your architecture and developer experience.