Skip to the content.

A component should do one job, and you should be able to name it

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”).

A component with many 'and's splits into single-responsibility pieces Left: one component described with three 'and's. Right: it splits into three components, each described by one sentence with no 'and'. one "and"-heavy component fetches user ANDrenders profile ANDhandles edit form split at each "and" Container (fetches) Profile (renders) EditForm (edits)
Each "and" in the description is a seam. Split there and every resulting component passes the one-sentence test — one job, nameable.

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.