Throughout years of building robust platforms (Dashboards, internal CRMs, and E-commerces as a Full-Stack developer), the biggest difference between toy code and enterprise code is the level of maintainability.
React dominates frontend development, but it is notoriously unopinionated regarding structure. Today I want to share how I build the skeleton of a scalable SPA (Single Page Application).
1. Separation of Concerns using Custom Hooks
Components with thousands of lines of code, mixing fetch calls with HTML markup (<div/>), become unmanageable in the medium term.
In serious projects, my first rule is the separation of layers:
- The UI Component only renders styles and interfaces.
- Business Logic resides separately in Service Functions (Handlers, Utils).
- Custom Hooks act as orchestrators.
// Bad Example (mixed)
function Dashboard() {
const [data, setData] = useState(null)
useEffect(() => { fetch('/api/data').then(...) }, [])
return <div>{data?.name}</div>
}
// Good Example (orchestrated)
function Dashboard() {
const { data, isLoading, error } = useDashboardMetrics()
if (isLoading) return <Spinner />
return <MetricsGrid renderData={data} />
}
2. Strict Typing (End-to-End TypeScript)
“Half-hearted” typing is useless. Using any eliminates the fundamental advantage of the TypeScript ecosystem. I recommend automatically generating interfaces based on the backend (e.g., using tRPC or OpenAPI generators) ensuring that if the database changes a model, our frontend will react by throwing a compile-time error, not in the client’s terminal during a traffic spike.
3. State Management: When NOT to use Context
There are countless tools out there (Redux, Zustand, Context). But many make the mistake of stuffing server state into client state managers. Golden Rule:
- For asynchronous state (fetching, database cache): Use React Query or SWR.
- For ephemeral Interface state (open menu, dark theme): Zustand or simple Context.
Adopting these 3 basic principles of cleanliness, business/UI separation, and intelligent orchestration ensures that when the B2B team needs to scale your application from 5 to 50 developers, the architecture won’t collapse.