The container line: draw it, and defend it with a check
Somewhere in every component tree there is a line. Above it, components may touch
the store, dispatch actions, and fetch data. Below it, components may only receive
props and emit events. That is the container line, and most architectural
health comes down to one question: is the line drawn clearly, and is it defended?
Teams that keep the line sharp end up with a portable UI layer and a testable state
layer. Teams that let it blur — a useSelector here, a fetch in an organism
there — end up with components that only work in one place and cannot be tested
without standing up the whole app.
Naming the line makes reviews decidable
The line’s value is that it turns a vague preference (“keep components clean”) into
a yes/no test any reviewer can apply: is this component above or below the line,
and does its code match? An organism with a useSelector is a line violation, no
debate required:
// BELOW the line — an organism must not read the store
function CommentList() {
const comments = useSelector((s) => s.comments.all); // ⚠ crosses the line
return <ul>{comments.map((c) => <Comment key={c.id} {...c} />)}</ul>;
}
// CORRECT — a container above the line reads; the organism takes props
function CommentListContainer() {
const comments = useSelector((s) => s.comments.all);
return <CommentList comments={comments} />; // ✓ line respected
}
Defend it with a check, not vigilance
A line defended only by review erodes, because review is intermittent and an AI generates line-crossing code faster than anyone reads it. So encode the line as a lint rule: nothing under your presentational directories may import the state layer.
// eslint: below-the-line directories may not import state
{
files: ["src/ui/**/*.{js,jsx}"], // organisms, molecules, atoms
rules: {
"no-restricted-imports": ["error", {
patterns: ["**/store", "**/state/*", "react-redux"],
}],
},
}
Now a useSelector in an atom fails the build with a message pointing at the line,
and the discipline holds without anyone having to remember it.
The line is where two skills meet
The container line is not a third concept alongside presentational/container and “organisms don’t fetch” — it is the same boundary named as a location. “Split by render-vs-decide” tells you which components go where; “organisms don’t fetch” tells you the fetch stays above; the container line is the drawable, checkable place all of that happens. Draw it explicitly (a directory convention, a naming rule), keep the store-aware code strictly above it, and enforce it with a check so volume cannot wear it down. Do that and your UI layer is a library you can lift into another app, and your state layer is a thing you can test without rendering a pixel. The presentational-vs-container exercise and the harness-state-shape check are the two halves of drawing and defending exactly this line.