Full-project lesson · needs Node.js, npm and real project files, so it does not run in the in-page playground.
The most direct way to style a React app is the one you already know: write a CSS file and use class names. React only changes how you attach the classes (className instead of class).
Import the CSS file
In a Vite project you import a CSS file from JavaScript. Vite adds the styles to the page for you (and, in production, bundles them into a file).
Create src/components/Card.css:
.card {
border: 1px solid #d0d7de;
border-radius: 8px;
padding: 16px;
max-width: 320px;
}
.card__title {
margin: 0 0 8px;
color: #1a5276;
}
Create src/components/Card.jsx, which imports it:
import './Card.css'
function Card({ title, children }) {
return (
<section className="card">
<h2 className="card__title">{title}</h2>
{children}
</section>
)
}
export default Card
And use it in src/App.jsx:
import Card from './components/Card.jsx'
function App() {
return (
<Card title="Styled with CSS">
<p>This card gets its look from Card.css.</p>
</Card>
)
}
export default App
This is verified: the project was built with Vite and the title’s computed colour is the one in the CSS file.
Global scope: the important caveat
An imported .css file is not scoped to the component. Its rules go into one global stylesheet that applies to the whole page, like any other CSS. If another file also defines .card, they collide, and the later one wins. Three ways to stay safe:
- Use a naming convention. BEM (
.card__title,.card--featured) or a prefix per component keeps names unique. - Keep selectors flat and low in specificity. Prefer one class per rule over
div > ul li a. - Use CSS Modules, which make names unique automatically; see CSS Modules.
Global styles
Put resets, fonts, CSS variables and base element styles in one global file and import it once, in main.jsx. The Vite template does this with src/index.css:
import './index.css'
CSS custom properties are the best way to share design values (colours, spacing) across components:
:root {
--brand: #1a5276;
--radius: 8px;
}
.card { border-radius: var(--radius); }
Dynamic styles
For styles that depend on props or state, switch class names rather than writing CSS in JavaScript; see Conditional Classes. For values that must be computed (a progress bar width), use an inline style or a CSS variable set inline.
Where to put CSS files
Place a component’s CSS next to the component: Card.jsx and Card.css in the same folder. They are easy to find, and you delete them together.
Assets inside CSS
Relative url() paths inside imported CSS are resolved by Vite, which also fingerprints the files:
.hero { background-image: url('./hero.png'); }
Common mistakes
- Using
classinstead ofclassNamein JSX. - Forgetting to import the CSS file. The component renders unstyled and no error appears.
- Writing generic names such as
.titleor.button, which clash with other files. - Expecting component-scoped CSS from a plain
.cssimport. It is global.
Key points
import './File.css'adds the styles; apply them withclassName.- Plain CSS imports are global, so name carefully or use CSS Modules.
- Keep shared design values in CSS variables and keep CSS next to its component.