Basic component setup
A component typically has the following structure:
- An Component (tsx or js(x)) file that defines the component function and its JSX return value.
- An interface or type definition for the component's props (optional but recommended for TypeScript).
- A CSS or styling file that defines the component's styles (optional, depending on your styling approach).
- A test file that contains unit tests for the component (optional but recommended).
- A story file for Storybook that demonstrates the component in isolation (optional but recommended for UI components).
- an index file (aka barrel file) that exports the component for easy imports (optional but common in larger projects).
There are a lot of visions "in the wild" about how to structure components, but the most important thing is to be consistent and clear about where each responsibility lives. The component file should focus on the component's logic and rendering, while styles, tests, and stories can live in their own files to keep things organized.
Personally, I like to keep the structure as explained and add each component in its own folder. This way, all related files (component, styles, tests, stories) are grouped together, making it easier to find and maintain them. A big plus is the focus on resuability of components. Once a solid component is created, it can be reused across different applications, which promotes consistency and reduces duplication.
…/components/atoms/Button
├── _Button.style.scss # styles for the Button component in SCSS format
├── _index.scss # barrel file for styles, importing all component styles
├── Button.interface.tsx # TypeScript interface for Button props
├── Button.mock.ts # mock data for testing or Storybook
├── Button.stories.tsx # Storybook stories for the Button component
├── Button.test.tsx # unit tests for the Button component
├── Button.tsx # the main component file defining the Button component
└── index.tsx # barrel file that exports the Button component for easy imports
Up one level
…/src/components/atoms
├── _index.scss # barrel file for styles, importing all atom styles
├── index.tsx # barrel file for atoms, exporting all atom components
├── Button
│ ├── _Button.style.scss
│ ├── _index.scss
│ ├── Button.interface.tsx
│ ├── Button.mock.ts
│ ├── Button.stories.tsx
│ ├── Button.test.tsx
│ ├── Button.tsx
│ └── index.tsx
├── Heading
│ ├── _Heading.style.scss
│ ├── _index.scss
│ ├── Heading.interface.tsx
│ ├── Heading.mock.ts
│ ├── Heading.stories.tsx
│ ├── Heading.test.tsx
│ ├── Heading.tsx
│ └── index.tsx
├── Icon
│ ├── _Icon.style.scss
│ ├── _index.scss
│ ├── Icon.interface.tsx
│ ├── Icon.mock.ts
│ ├── Icon.stories.tsx
│ ├── Icon.test.tsx
│ ├── Icon.tsx
│ └── index.tsx
... etc
Import example
import { Button, Heading, Icon } from 'components/atoms'
// or even
import { Button, Heading, Icon } from 'components'
Although I'd suggest to keep the imports as specific as possible to avoid confusion about where components are coming from. In larger projects, it's common to have a clear folder structure that indicates the type of component (e.g., atoms, molecules, organisms) to help with organization and readability. So, importing from components/atoms can be more explicit and helpful for understanding the component's role in the design system.