Key Takeaways
- Don't default to React for a brochure site, article page, or a few isolated interactions. Start by asking what actually needs client-side state.
- Separate three costs: learning React's component model, maintaining the development toolchain, and delivering JavaScript to users.
- For content-heavy sites, compare rendering strategies and targeted interactivity—not just React against another UI library.
- React can still be the right choice for a small project when an experienced team, shared components, or concrete application requirements justify it.
Knowing when not to use React comes down to one question: does its component and state model solve enough of your project's problems to justify the added machinery? For a mostly static site with a little interaction, HTML, CSS, and targeted JavaScript are often a better starting point.
This isn't a blanket argument against React. React is a library for building user interfaces, and its component model can help organize interactive screens. The reasons to use React matter—but so do the requirements of the project you're actually shipping.
A Steep Learning Curve
The real adjustment is state ownership
React asks you to describe what the interface should look like for its current data, rather than manually update each affected element. State is the changing information a component remembers; props are inputs passed into a component.
One-way data flow means data passes from parent components to children. A child can call a callback supplied by its parent to request a change. When several components need the same state, you commonly move ownership to their closest shared parent.
For example, a product filter and a results list should agree on the selected category. In React, a shared parent can own that selection and pass it to both. The filtered results can be calculated from the products and selection instead of maintained as another independent state value.
This isn't fundamentally incompatible with MVC, a broader way of organizing an application. The practical learning cost is deciding where state belongs, avoiding duplicated state, and understanding how changes produce a new UI description—not memorizing an architectural slogan.
JSX is optional, and it doesn't dictate your CSS
JSX is a JavaScript syntax extension for describing UI with HTML-like markup. React commonly uses className where HTML uses class, but ordinary CSS files remain an option; JSX doesn't require a particular styling system.
You can also create React elements without JSX. Our React JSX introduction explains the syntax, but don't mistake learning that syntax for learning React's state model.
Tooling Overhead
Compare complete approaches, not isolated libraries
Choosing React doesn't finish your application architecture. You still need decisions about routing, data loading, forms, rendering, and deployment. React's documentation recommends starting new applications with a framework; frameworks such as Next.js integrate many of these concerns. A custom client-side setup remains an option when it fits your constraints.
That changes the comparison. Evaluate a React-based framework against your existing server-rendered stack or another complete approach—not bare React against an entire framework.
Frameworks may manage the build pipeline for you. For a custom client-side application, Vite is one option, not a universal successor to every other bundler. Babel isn't a React requirement; whether you need it depends on your chosen transforms and plugins.
A concrete client-side setup
For the React example below, use Vite's JavaScript React template. Install a supported Node.js release that satisfies the current Vite requirements, then run:
npm create vite@latest react-counter -- --template react
cd react-counter
npm install
npm run devThe template supplies the development setup and a src/main.jsx entry point that mounts App with createRoot. You don't separately configure Babel to run this example. For a longer walkthrough, see our React project quick start.
Node.js here is a development-tool dependency, not a requirement that visitors run Node.js. React can run in a browser without a Node-based build step, although typical local projects use Node.js and a package manager. Deployment requirements depend on whether you produce static assets or use server features.
The maintenance question is bigger than the initial commands: who owns dependency upgrades, build failures, plugin compatibility, and security review? A framework can reduce configuration work, but include its conventions and upgrade path in your decision.
Project Suitability: When Not to Use React
Start with the interaction model, not the number of pages. A single-screen editor with coordinated selections, unsaved changes, and complex validation is a reasonable React candidate. A large publication whose pages mostly contain text may be better served by a content-focused approach.
| Project requirement | Starting point to evaluate | What would justify more machinery? |
|---|---|---|
| A few pages and isolated interactions | HTML/CSS or server templates, with targeted JavaScript | Substantial shared client-side state or reusable application widgets |
| Articles, documentation, or marketing content | A static-site generator or content-focused framework | Interactive sections that need their own UI components |
| Coordinated editing, filtering, and multi-step workflows | A React-based application or another UI framework | A component model that simplifies the actual workflow |
| An existing React product and design system | The established stack, subject to delivery requirements | Reuse and team familiarity that outweigh introducing another stack |
Vue and Svelte are also options for component-based interfaces; neither should be treated as universally simpler or faster. Compare a representative feature and the complete deployment approach. If Vue is on your shortlist, our Vue versus React comparison provides another angle on that choice.
React vs vanilla JavaScript: a small interaction
A counter makes the distinction concrete. For the browser-native version, save these two files together and open index.html.
<!-- index.html -->
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Counter example</title>
<script src="./counter.js" defer></script>
</head>
<body>
<button id="counter" type="button">Count: 0</button>
</body>
</html>// counter.js
const button = document.getElementById('counter');
if (!button) throw new Error('Missing #counter button');
let count = 0;
button.addEventListener('click', () => {
count += 1;
button.textContent = `Count: ${count}`;
});The deferred script runs after HTML parsing. The click handler updates the count and replaces the button's text; no UI library is involved.
For the React version, replace src/App.jsx in the Vite project with:
import { useState } from 'react';
export default function App() {
const [count, setCount] = useState(0);
return (
<button
type="button"
onClick={() => setCount(current => current + 1)}
>
Count: {count}
</button>
);
}Here, useState stores the count, and its setter requests an update. The updater function calculates the next count from the pending state. React handles updating the rendered button.
Neither snippet proves a performance advantage. For one independent counter, I'd choose the native version. For an interface where many components must stay synchronized, I'd evaluate whether React makes the implementation easier to maintain.
Use progressive enhancement where it fits
Progressive enhancement means providing a usable baseline, then adding richer behavior where supported. For example, start with a working link or server-handled form before adding client-side conveniences. The counter demos aren't progressive enhancement: their counting behavior requires JavaScript in both versions.
Check native HTML before writing a widget. A <details> element with a <summary> provides an expandable disclosure without custom JavaScript. You don't need to introduce application state just to show an answer below a question.
Rendering Strategy Matters More Than the React Label
React doesn't require every page to be rendered entirely in the browser. React-based frameworks can support client rendering, server rendering, and static generation. Our client-side versus server-side rendering guide covers the underlying distinction.
- Client-side rendering: JavaScript builds the interface in the browser. For a client-only page, the initial content depends on that code running.
- Server-side rendering: HTML is generated on the server and delivered to the browser. Interactive React regions can then be hydrated.
- Static generation: HTML is generated ahead of requests, typically during a build. Decide how content updates trigger regeneration.
Hydration connects React's component logic to HTML already rendered by React on the server. That initial browser render must match the server output. Sending HTML first doesn't automatically remove the JavaScript or hydration work needed for interactive client components.
Server Components address a different boundary: their component code doesn't need to run in the browser. In Next.js, keeping interactive client boundaries narrow can reduce client JavaScript, while Client Components still need JavaScript for their behavior. Server Components and server-side rendering aren't interchangeable terms.
For content-first sites, an islands approach is another useful comparison: keep most of the page as HTML and hydrate selected interactive widgets. Astro supports React components this way and renders UI components to HTML and CSS without their client-side JavaScript by default, unless you opt into client behavior.
React coordinates rendering and DOM updates; it doesn't eliminate their cost. Code splitting can defer parts of an application, but evaluate what your actual routes ship rather than assigning React a universal bundle-size or speed figure.
A Decision Checklist Before You Commit
- Interaction: List the state that changes in the browser. Which parts must stay synchronized? Could independent widgets handle it?
- Content and SEO: Decide which text, links, and metadata must arrive in the initial HTML. Choose a rendering strategy deliberately.
- Accessibility: Include semantic controls, labels, keyboard navigation, and focus behavior in acceptance tests.
- Performance: Set budgets for initial JavaScript, visible content, and interaction responsiveness. Test production builds on representative devices and connections.
- Operations: Specify whether you need static hosting, a request-time server, or both. Include deployment, caching, and recovery work.
- Maintenance and team: Identify who understands the stack, who will upgrade it, and which existing components you can reuse.
- Future requirements: Count planned features with an owner and timeline—not a hypothetical dashboard that might appear someday.
For a borderline case, build one representative page or workflow in each shortlisted approach. Compare implementation effort, delivered assets, accessibility, and deployment work. Don't choose an architecture from a counter benchmark.
Common Mistakes
- Rejecting React solely because the site is small. An established team and component library can be good reasons to stay with it.
- Choosing another UI framework when you don't need one. First ask whether HTML and a small script solve the problem.
- Assuming server rendering removes client work. Inspect the interactive boundaries and delivered assets.
- Rewriting everything to add one feature. React supports adding components to an existing page or taking over only part of a site.
Conclusion
Use React when its model pays for itself in your interface and your team's workflow. When the requirements are mostly content and a few independent interactions, start smaller. Choose the approach with the lowest credible total ownership cost—not the most familiar logo or the shortest demo.
FAQ
When is React overkill?
For a page with only a few independent interactions and no substantial shared client-side state, I'd evaluate simpler options before choosing React. Start with HTML, CSS, and targeted JavaScript, then assess whether a component framework would solve a concrete maintenance problem.
Should I use React for a static or content-focused website?
Not by default. Evaluate a static-site generator, server templates, or a content-focused framework first; consider React when shared components, interactive sections, and team experience justify it.
Can I use React without JSX?
Yes. React's createElement API lets you describe elements in ordinary JavaScript without JSX. Removing JSX doesn't remove React's component model or runtime requirements for interactive client components.
Do React projects require Node.js and a build tool?
Not intrinsically: React can run in a browser without a Node-based build step. Typical local projects use Node.js for development tooling, while production runtime requirements depend on the rendering and deployment approach; see React's integration guidance.
What should I use instead of React for a small website?
Start with HTML and CSS, adding browser-native JavaScript only where needed. For a larger collection of content pages, evaluate server templates or a static-site generator; choose another UI framework only when its component model addresses your requirements.
Further Reading
- Creating a React App — framework choices and rendering options.
- Thinking in React — components, minimal state, and data flow.
- Build a React App from Scratch — what a custom setup leaves you to solve.
- Vite Getting Started — prerequisites, templates, and project commands.
- React hydrateRoot reference — hydration behavior and matching server output.
- Next.js Server and Client Components — rendering boundaries and client JavaScript.
- Astro islands architecture — selective interactivity on content pages.
- MDN progressive enhancement — building from a usable baseline.
