A component should do one job, and you should be able to name it
The single-responsibility principle has a wonderfully cheap test for components: try to describe what the component does in one sentence. If you cannot do it without an “and,” it is doing too much. “It shows the user’s profile” is one responsibility. “It fetches the user and renders the profile and handles the edit form and manages the toast” is four, and the “and”s are telling you exactly where the split lines are. This test costs nothing, applies at any level, and answers the two hard questions of component design at once — when to split (when you need an “and”) and where (at each “and”).
The “and” is the split line
Watch the test find the seams. This component’s description needs three “and”s, and each one marks a responsibility that wants its own home:
// "fetches the user AND renders the profile AND owns the edit form" — three jobs
function UserProfile({ id }) {
const user = useSelector((s) => s.users.byId[id]); // job 1: get the data
const [editing, setEditing] = useState(false); // job 3: edit mode
useEffect(() => { dispatch(loadUser(id)); }, [id]); // job 1 again
return editing
? <form>{/* 30 lines of edit form — job 3 */}</form>
: <div>{/* profile markup — job 2 */}</div>;
}
Split at the “and”s and each piece gets a one-sentence description: a container that fetches, a presentational profile that renders, and an edit form that edits.
Each piece is nameable, testable, reusable
The payoff of one-job components is concrete. A component that does one thing is nameable (its name is the sentence), testable (you test one behaviour, not a tangle), and reusable (the profile renderer can appear anywhere, because it does not drag fetching and editing with it):
function UserProfileContainer({ id }) { // "fetches the user"
const user = useUser(id);
return <UserProfile user={user} />;
}
function UserProfile({ user }) { // "renders the profile" — pure, reusable
return <div>{user.name}</div>;
}
Three small components you can each hold in your head beat one you cannot.
Responsibility, not line count, is the metric
The nuance that keeps this from becoming dogma: the rule is about responsibility, not size. A 200-line component that does one genuinely complex thing (a rich date picker) can be fine; a 20-line one that fetches, transforms, and renders is doing three things and should split. So do not chase a line limit — chase the one-sentence test, and let it tell you both when (an “and” appeared) and where (at that “and”). Over-splitting has its own cost, too: a component so tiny it only forwards props adds indirection without earning it, so split when there is a real second responsibility, not on reflex. The whole discipline fits in a sentence about sentences: if you can’t name it in one without “and,” split it there. The atom-boundaries and presentational-vs-container exercises are this test applied — placing a component by its single job and pulling the extra jobs out.