react-size-thresholds
a component or hook measured as the wrong kind, judged against the base threshold instead of its own, or past its own
Agent checkadvisory by defaultreactcode
The argument
React - Function Size Limits
The base function size thresholds are defined in code:style-guide/references/patterns/functions.md. The overrides below apply to the file types they specify — when a file matches a classification here, use these thresholds instead of the base.
File Classification
.tsxfiles with a named/default export returning JSX → Component (use component thresholds).tsfiles exporting a function starting withuse→ Hook (use hook thresholds)- Everything else → Utility (50-line threshold applies)
Line Counting
Count from function signature to closing brace. Exclude imports, type declarations outside the function, and file-level comments.
Components (.tsx)
| Lines | Assessment |
|---|---|
| <100 | Almost always fine |
| 100–150 | Review — acceptable if mostly JSX composition with no inline logic |
| 150+ | Likely needs extraction |
| 200+ | Definitely needs extraction |
Hooks (.ts)
| Lines | Assessment |
|---|---|
| <80 | Fine |
| 80–120 | Review — look for extractable pure logic |
| 120+ | Likely needs utility extraction |
| 160+ | Definitely needs extraction |
Pure logic inside hooks should be extracted to utility functions. The hook itself should compose, not compute.
Default Actions — Components & Hooks
| Issue Type | Default Action | Review Level |
|---|---|---|
| Component >200 lines | Extract sub-components | Medium |
| Hook >160 lines | Extract pure logic to utilities | Medium |
| Inline styles / repeated className logic | Extract to shared class or component | Low |
The proof
fail
interface Issue {
id: string;
title: string;
state: string;
assignee: string | null;
}
interface Props {
issues: Issue[];
}
// Past the threshold with inline logic rather than JSX composition: the header,
// the filters and the row rendering are three sub-components waiting to be cut
// out of it.
export const IssuePanel = ({ issues }: Props) => {
const open = issues.filter((issue) => issue.state === 'open');
const closed = issues.filter((issue) => issue.state === 'closed');
const unassigned = open.filter((issue) => issue.assignee === null);
const byAssignee: Record<string, Issue[]> = {};
for (const issue of open) {
const key = issue.assignee ?? 'unassigned';
byAssignee[key] = [...(byAssignee[key] ?? []), issue];
}
return (
<section>
<header>
<h2>Issues</h2>
<p>
{open.length} open, {closed.length} closed, {unassigned.length} unassigned
</p>
</header>
{Object.entries(byAssignee).map(([assignee, assigned]) => (
<div key={assignee}>
<h3>{assignee}</h3>
<ul>
{assigned.map((issue) => (
<li key={issue.id}>
{issue.title} — {issue.state}
</li>
))}
</ul>
</div>
))}
</section>
);
};
pass
interface Issue {
id: string;
state: string;
amount: number;
}
export const buildIssueSummary = ({ issues }: { issues: Issue[] }): Record<string, number> => {
const totals: Record<string, number> = {};
for (const issue of issues) {
totals[issue.state] = (totals[issue.state] ?? 0) + issue.amount;
}
return totals;
};
Turn it down
Both lines go in your lightsout.config.json.
"standards-checks": { "react-size-thresholds": "advisory" }"standards-checks": { "react-size-thresholds": "off" }