3 lutego 2026
Dlaczego warto używać plików index - Folder-as-Component
Jak uporządkować frontend w React/Vue/Nuxt dzięki wzorcowi Folder-as-Component i skalować projekt bez chaosu.
Dlaczego warto używać plików index - Folder-as-Component
Czy zdarzyło Ci się szukać jednego komponentu i trafiać na dziesiątki plików typu Button.vue, ButtonTypes.ts, ButtonStyles.scss, useButton.ts?
Na początku to działa, ale wraz ze wzrostem projektu robi się trudne w utrzymaniu.
W tym wpisie pokazuję podejście, które dobrze skaluje się w projektach komponentowych: Folder-as-Component.
Problem: chaos w nazewnictwie
Typowa, płaska struktura często wygląda tak:
components/
├── Button.vue
├── Button.types.ts
├── Button.styles.scss
├── Card.vue
├── Card.types.ts
├── Card.styles.scss
├── Navbar.vue
├── NavbarLinks.vue
├── NavbarDrawer.vue
└── ...Co z nią nie tak?
- Wszystko ląduje w jednym miejscu.
- Nazwy robią się długie i powtarzalne.
- Trudno ocenić, co należy do którego komponentu.
- Przy większej liczbie elementów rośnie koszt refaktorów.
- W IDE pojawia się „tab hell” - mnóstwo podobnych nazw.
Rozwiązanie: Folder-as-Component
Zamiast nazywać każdy plik nazwą komponentu, nazwij folder nazwą komponentu, a wewnątrz trzymaj stały układ:
components/
├── button/
│ ├── index.ts # eksport komponentu i typów
│ ├── index.vue # lub index.tsx
│ ├── _index.scss
│ └── types/
│ ├── index.ts
│ └── interfaces.ts
├── card/
│ ├── index.ts
│ ├── index.vue
│ └── _index.scss
└── navbar/
├── index.ts
├── index.vue
├── _index.scss
└── components/
├── index.ts
├── _index.scss
├── Drawer/
│ ├── index.ts
│ ├── index.vue
│ └── _index.scss
└── Links/
├── index.ts
├── index.vue
└── _index.scssJak to wygląda w praktyce
React: components/button/
index.ts:
export { Button } from './index.tsx'
export * from './types'index.tsx:
import type { ButtonProps } from '.'
import './index.scss'
export const Button = ({ label, icon, children, ...props }: ButtonProps) => {
return (
<button className="button" {...props}>
{icon && <Icon name={icon} />}
{label && <span>{label}</span>}
{children}
</button>
)
}Vue/Nuxt: components/button/
index.ts:
export { default as Button } from './index.vue'
export * from './types'index.vue:
<template>
<button class="button">
<Icon v-if="icon" :name="icon" />
<span v-if="label">{{ label }}</span>
<slot />
</button>
</template>
<script setup lang="ts">
import type { ButtonProps } from '.'
defineProps<ButtonProps>()
</script>
<style lang="scss">
@import 'index';
</style>Barrel exports: prostsze importy
Dla większych modułów tworzysz agregatory:
components/atoms/index.ts:
export * from './button'
export * from './badge'
export * from './icon'components/sections/index.ts:
export * from './hero'
export * from './faq'
export * from './footer'Dzięki temu importy stają się krótkie i przewidywalne:
import { Button } from '@/components/atoms'Zalety wzorca
- Lepsza enkapsulacja - wszystko związane z komponentem jest obok siebie.
- Lepsza skalowalność - struktura pozostaje czytelna przy 50+ i 100+ komponentach.
- Łatwiejszy refaktor - przenosisz cały folder, nie polujesz po plikach.
- Lepszy DX - łatwiejsza nawigacja po zakładkach i wyszukiwarce IDE.
- Spójność między zespołem - każdy wie, gdzie szukać komponentu, styli i typów.
- Niezależność od frameworka - działa podobnie w React, Vue, Nuxt, Next.
Konwencja plików
index.ts- publiczny eksport modułu (barrel export)index.vue/index.tsx- implementacja komponentu_index.scss- styl jako SCSS partialtypes/- kontrakty typów i interfejsy
To proste reguły, ale bardzo poprawiają czytelność projektu.
Kiedy warto wdrożyć
Najbardziej opłaca się, gdy:
- masz więcej niż kilkanaście komponentów,
- pracujesz w zespole,
- masz rozbudowane sekcje z subkomponentami (np.
navbar,dashboard,pricing), - chcesz łatwiej utrzymać spójność kodu.
Podsumowanie
Folder-as-Component nie jest „magicznym” rozwiązaniem, ale jest bardzo skuteczną konwencją organizacji kodu.
Porządkuje projekt, skraca czas nawigacji i upraszcza refaktory - szczególnie w aplikacjach Nuxt/Vue rozwijanych długoterminowo.