Skip to main content

Organizing Styles in React: Co-location and Global CSS

Organizing CSS in React requires a deliberate strategy to avoid naming conflicts and maintain scalability. Co-location—placing a component's styles alongside its code in the same folder—combined with a single global stylesheet for foundational rules, creates a maintainable architecture that grows smoothly from small projects to large applications. This approach ensures that component styles stay local, global styles remain predictable, and the project structure mirrors React's component-based philosophy.

Key Takeaways

  • Co-location places a component's CSS file in the same folder as its JSX file, making the component a self-contained, portable unit
  • Global styles (index.css) should contain only foundational rules (resets, fonts, CSS variables, base typography) imported once at the application root
  • Component-specific CSS files should import global CSS variables but define styles that apply only to that component
  • A consistent naming convention (CSS file name matches component name) and BEM-style class naming prevent conflicts in the global scope

Why Co-location Matters

The traditional approach of storing all CSS in a single /styles folder creates problems in component-based frameworks. When you delete a component, its styles often remain orphaned. Class name conflicts become harder to avoid as the application grows. Finding which styles apply to which component requires jumping between folders. Co-location solves these problems by treating each component as a self-contained unit: its JSX logic, styles, and tests live together, making the component easier to understand, maintain, modify, and even move to other projects.

The Problem with Centralized Style Folders

A /styles folder containing all CSS files scales poorly because:

  1. It's unclear which styles belong to which component
  2. Deleting a component leaves orphaned CSS behind
  3. The risk of global class name collisions increases with project size
  4. Developers must navigate between folders to understand a component completely

The Co-location Solution

By placing Button.css inside the Button component folder alongside Button.jsx, you create a modular unit where everything the button needs is in one place. This pattern scales cleanly: as you add components, each brings its own folder with its logic and styles. The component folder becomes the single source of truth for that feature.

Scalable Project Structure

A typical React project using co-location follows this pattern:

src/
├── assets/
│ ├── fonts/
│ └── images/

├── components/
│ ├── Button/
│ │ ├── Button.jsx
│ │ └── Button.css
│ │
│ ├── Alert/
│ │ ├── Alert.jsx
│ │ └── Alert.css
│ │
│ └── Card/
│ ├── Card.jsx
│ └── Card.css

├── index.css // Single global stylesheet
├── index.jsx // Application entry point
└── App.jsx // Main application component

Each component folder is self-contained, making it trivial to locate and modify component-specific styles. The global index.css remains small and focused on foundational rules.

Global Styles vs. Component-Specific Styles

The key to maintainable CSS is deciding what belongs in the global stylesheet versus what belongs in component files.

What Belongs in index.css (Global Styles)

Global styles should establish the foundational look and feel of the entire application. They should contain rules you rarely override.

Ideal candidates for global styles:

  • CSS Resets: Normalizing margin, padding, and box-sizing across browsers
  • Font Definitions: Setting font-family, line-height, and default font-size on body or html
  • CSS Custom Properties: Global color palettes, spacing units, typography scales (--primary-color, --spacing-unit)
  • Base Typography: Default styles for h1, h2, p, a, and other semantic HTML elements
  • Root-level Rules: Any styles that must apply globally, such as * { box-sizing: border-box; }

Example index.css:

:root {
--primary-color: #007bff;
--secondary-color: #6c757d;
--text-color: #333;
--background-color: #fff;
--spacing-unit: 8px;
}

* {
box-sizing: border-box;
margin: 0;
padding: 0;
}

html, body {
margin: 0;
padding: 0;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
sans-serif;
-webkit-font-smoothing: antialiased;
}

body {
background-color: var(--background-color);
color: var(--text-color);
line-height: 1.6;
font-size: 16px;
}

h1 {
font-size: 2rem;
margin-bottom: var(--spacing-unit);
}

h2 {
font-size: 1.5rem;
margin-bottom: calc(var(--spacing-unit) * 0.75);
}

p {
margin-bottom: var(--spacing-unit);
}

a {
color: var(--primary-color);
text-decoration: none;
}

a:hover {
text-decoration: underline;
}

Import index.css only once, in your application's entry point (src/main.jsx or src/index.js):

import './index.css';
import App from './App';

What Belongs in Component CSS Files

Component stylesheets contain rules that apply only to that component and its internal elements. They should use global CSS variables for consistency but define component-specific layout, sizing, and interaction styles.

Example Button.css:

.Button {
padding: calc(var(--spacing-unit) * 1.25) calc(var(--spacing-unit) * 2);
border: 2px solid var(--primary-color);
background-color: var(--primary-color);
color: white;
border-radius: 4px;
cursor: pointer;
font-size: 1rem;
font-weight: 500;
transition: all 0.2s ease;
}

.Button:hover {
background-color: #0056b3;
box-shadow: 0 2px 8px rgba(0, 86, 179, 0.3);
}

.Button:active {
transform: translateY(1px);
}

.Button--secondary {
background-color: var(--secondary-color);
border-color: var(--secondary-color);
}

.Button--secondary:hover {
background-color: #5a6268;
}

.Button:disabled {
opacity: 0.6;
cursor: not-allowed;
}

Example Button.jsx:

import React from 'react';
import './Button.css';

function Button({ children, variant = 'primary', disabled = false, ...props }) {
const className = `Button ${variant === 'secondary' ? 'Button--secondary' : ''}`;

return (
<button className={className} disabled={disabled} {...props}>
{children}
</button>
);
}

export default Button;

Each component file imports only its own stylesheet, keeping the dependency graph clear and allowing styles to be removed when the component is deleted.

Best Practices for Sustainable CSS

File Naming Conventions

Use consistent, predictable naming:

  • Match CSS filename to component filename (Button.jsxButton.css)
  • Use PascalCase for both files to align with React naming conventions
  • In the CSS, use component-based class naming: .Button, .Button__text, .Button--primary (BEM methodology)

Directory Structure Rules

  • One component, one folder
  • Co-locate JSX, CSS, and tests in that folder
  • Use a consistent structure across all components
  • Keep index.css at the root of src/

CSS Naming Strategy

Use BEM (Block, Element, Modifier) convention to minimize naming conflicts:

  • Block: The component name (.Button)
  • Element: A child element within the component (.Button__icon)
  • Modifier: A variant or state (.Button--secondary, .Button--disabled)

This convention makes class names self-documenting and significantly reduces the risk of collisions even though all classes remain in the global scope.

CSS Variable Usage

Leverage global CSS variables in all component stylesheets:

/* Good: uses global variables */
.Card {
padding: var(--spacing-unit);
border: 1px solid var(--border-color);
background-color: var(--background-color);
}

/* Avoid: hardcoded values */
.Card {
padding: 8px;
border: 1px solid #ddd;
background-color: #fff;
}

This approach allows designers to adjust the entire application's look by modifying index.css alone.

Refactoring an Existing Project

To refactor a project with scattered styles into the co-location pattern:

  1. Create a components/ folder in src/
  2. For each component currently in src/, create a subfolder: components/ComponentName/
  3. Move the component's JSX file into that folder, renamed to match the folder name
  4. Move any CSS currently associated with that component (or create new CSS) into the folder as ComponentName.css
  5. Update all imports to reflect the new paths
  6. Create src/index.css with only global styles and CSS variables
  7. Verify that src/main.jsx (or src/index.js) imports index.css exactly once

Frequently Asked Questions

What if multiple components share the same styles?

Extract shared styles into your global index.css using CSS classes or variables. If the shared styles are truly component-specific but reused, consider creating a shared component instead of duplicating CSS. For example, if Button and IconButton share baseline styles, make IconButton a variant that imports and extends Button.css.

Can I use SCSS or other CSS preprocessors with this structure?

Yes. Replace .css files with .scss files and follow the same co-location principle. Each component folder would contain ComponentName.jsx and ComponentName.scss. Ensure your build tool (Vite, Create React App, etc.) is configured to handle SCSS imports.

How do I handle component variants (primary, secondary, etc.)?

Use BEM modifier classes or CSS variables. In Button.css, define .Button--primary and .Button--secondary. In Button.jsx, conditionally apply the modifier class based on a variant prop. Alternatively, use CSS custom properties to vary appearance: --button-bg-color: var(--primary-color) by default, override to --secondary-color for secondary variant.

What about third-party component libraries like Material-UI?

Third-party component libraries typically provide their own styling systems (CSS-in-JS, CSS modules, styled-components, etc.). Import and use them as documented. Your project's co-location strategy applies to your components, not third-party ones. Keep third-party customizations in your global index.css or in a dedicated theme.css file.

Should I use CSS modules with co-location?

CSS modules are compatible with co-location but not required. CSS modules prevent global scope collisions entirely, whereas co-location with BEM conventions minimizes (but doesn't eliminate) that risk. Both approaches work well; choose based on your team's preference and the complexity of your component library.

Glossary

Co-location: The practice of placing related files—logic, styles, and tests—in the same directory. For React components, this means grouping Component.jsx, Component.css, and Component.test.js together.

Global Styles: CSS rules that apply across the entire application, such as resets, font definitions, and foundational typography.

Component Styles: CSS rules defined to apply only to a specific component and its internal elements, kept in the component's folder.

CSS Reset: A set of CSS rules that neutralizes browser default styling, ensuring consistent rendering across browsers (e.g., setting default margin and padding to zero).

CSS Custom Properties (CSS Variables): CSS variables defined at the root level (:root { --primary-color: #007bff; }) and referenced throughout stylesheets using var(--primary-color), enabling consistent theming and easy maintenance.

BEM (Block, Element, Modifier): A naming convention for CSS classes that organizes them into blocks (components), elements (child parts), and modifiers (variants), reducing naming conflicts and improving code clarity.

Further Reading