A frontend framework is a set of conventions and machinery for turning application state into a user interface and keeping the two in step. The tools available today agree on that goal and disagree on almost everything else, starting with where the work of keeping them in step actually happens.
Quick Overview
What you’ll learn:
- What a framework provides beyond a component syntax
- Which rendering model each major tool commits to, and what that commitment implies
- How the leading options differ in state handling, tooling and release discipline
- Why meta-frameworks changed the decision from “which library” to “which runtime”
- How to choose for a specific project instead of for a general reputation
- What the choice costs after launch, in upgrades, hiring and rewrites
Who this article is for:
- Engineers choosing a stack for a new application
- Technical leads inheriting a codebase built on a framework they did not pick
- Architects weighing rendering strategy against delivery constraints
Reading time: 19 minutes
What a Frontend Framework Actually Provides
The visible part of a framework is its component syntax, and it is the least important part. What a team is really adopting is a set of answers to questions every non-trivial interface has to answer anyway: how a piece of state gets from where it is stored to where it is displayed, what happens when it changes, how a page is split into units that can be reasoned about separately, and what the boundary looks like between code that runs in the browser and code that does not.
Underneath all of them sits the web platform itself — the document model, the styling engine, the language, and the browser APIs for storage, networking and rendering. Frameworks do not replace this layer; they wrap it, and every wrapper leaks. Engineers who understand the platform read framework behaviour as a set of choices about how to use it, while engineers who only know the wrapper read the same behaviour as magic that occasionally fails. This is why competence in the underlying platform survives every framework cycle, and why a team investing in modern web technologies is buying something that does not expire when the fashionable tool changes.
The second thing a framework provides is a set of defaults about project structure. Some tools take a position and enforce it; others leave the decision open and let each team invent its own. Neither is better in the abstract. An enforced structure is a cost for a small experienced team and a benefit for a large rotating one, and the reverse is equally true.
The third thing — and the one that surprises teams most often — is a maintenance contract. A framework is a dependency with its own release cadence, its own breaking changes, and its own idea of how long a version stays supported. That contract is public, it is checkable, and it belongs in the selection decision rather than in the post-mortem.
The Rendering Models That Separate These Tools
If you strip away syntax and branding, the substantive difference between the leading frontend tools is where the work of translating state into markup happens.
The first model does that work in the browser at runtime. The framework keeps an in-memory representation of the interface, compares it against the desired state after every change, and applies the minimum set of updates to the real document. This is the approach that made component-based interfaces mainstream, and its cost is that the comparison machinery must be shipped to every user and executed on every device, including the slow ones.
The second model does the work at compile time. Instead of shipping a diffing engine, the build step analyses the component and generates code that updates exactly the parts of the document that can change. The runtime is smaller because most of the decisions have already been made when the user arrives.
The third model asks a different question: how much of this page needs to be interactive at all? A content page is mostly static text with a few interactive regions. Rendering it fully as HTML and then hydrating only the interactive regions avoids paying for interactivity that nobody uses.
The fourth model moves rendering to the server by default and treats the browser as a place where the result is progressively enhanced. Markup arrives ready; the client layer improves the experience rather than creating it.
These four models are not rankings. They are trade-offs between bundle size, time to first render, interaction latency and the complexity of reasoning about where a given line of code runs. Almost every argument between frontend tools that appears to be about syntax is really an argument about which of these trade-offs the arguing parties consider acceptable.
React: Composition as the Default
React’s contribution was not the virtual document model, which predated it, but the normalisation of a one-directional flow: state lives above, is passed down, and changes travel back up through explicit handlers. That constraint is what makes large interfaces tractable, because it turns “why did this change?” from an investigation into a trace.
The library itself is deliberately narrow. It renders components and manages their lifecycle; it does not decide how to route between pages, how to fetch data, how to organise a project or how to store application-wide state. Those answers come from the ecosystem, and the ecosystem offers several of each. That is React’s central trade: maximum freedom to assemble a stack that fits, at the cost of having to make — and later maintain — every one of those decisions.
React’s release discipline is public and worth reading before adoption rather than after. The React Versioning Policy describes a semantic-versioning scheme in which breaking changes are reserved for major releases, new functionality arrives in minor releases, and deprecations are announced with warnings in a release before the behaviour is removed. For a team planning a multi-year codebase, that policy is a more useful input than any popularity signal, because it describes what upgrade work will look like.
Where React costs the most is exactly where it gives the most: state. Local component state is straightforward, and application-wide state is a design problem the library hands back to you. Teams that decide this deliberately do well; teams that let each feature invent its own approach end up maintaining several state systems in one product. A team that wants this settled before the codebase settles it for them will get more from structured work on state management across the major frameworks than from another tutorial about components.
Vue: Progressive Adoption and Built-In Reactivity
Vue occupies a middle position between a library and a full framework, and it does so on purpose. It can be added to a single page of an existing application and grown from there, or it can be adopted whole with an official router and an official state store. The migration path from the first to the second is a project decision rather than a rewrite, which is why Vue turns up so often in codebases that had to modernise gradually rather than all at once.
Its reactivity system is the technical distinction most worth understanding. As the Reactivity in Depth guide on vuejs.org describes, the current major version tracks state through JavaScript proxies: reading a property during rendering registers a dependency, and writing to it later schedules exactly the effects that depend on it. The developer does not call an update function, because the framework already knows which parts of the interface read that value.
The practical consequence is a smaller gap between “what the code says” and “what the framework does”. A value is changed the way any object property is changed, and the interface follows. The cost is that reactivity has boundaries — a value taken out of the reactive system stops being tracked — and those boundaries are the source of most genuine Vue bugs.
Vue’s template syntax is closer to standard markup than an expression-based alternative, which lowers the entry cost for developers who come from a document-first background and mildly annoys developers who prefer their views expressed as ordinary code. This is a preference, not a defect, and it is worth naming as one during selection rather than arguing about it later.
React and Vue, Head to Head
These two get compared constantly, usually on the wrong axis. On raw rendering performance they land close enough that the difference is dominated by how the application is written rather than by which of them is underneath. The substantive differences are elsewhere.
The first is where the answers come from. React supplies a rendering model and expects the team to select a router, a data-fetching approach and a state strategy. Vue supplies officially maintained answers to the same questions, which a team may replace but rarely needs to. This is the difference between assembling a stack and adopting one, and it predicts far more about a codebase’s long-term consistency than any benchmark.
The second is how views are expressed. React treats the view as ordinary code, which makes arbitrary logic in rendering natural and makes discipline the developer’s responsibility. Vue treats the view as a template with a constrained expression language, which makes some patterns awkward on purpose and keeps rendering logic thin. Teams that value freedom prefer the first; teams that value uniformity across many contributors prefer the second.
The third is the shape of the ecosystem. React’s is wider, so a prebuilt answer to an unusual requirement is more likely to exist and more likely to come in several competing versions of varying quality. Vue’s is narrower and more curated, so there is usually one answer and it is usually the official one.
None of these is a defect. They are the same trade — freedom against uniformity — appearing in the choice of stack, in the shape of the view layer and in the depth of the ecosystem, and a team that knows which side of it they want has effectively already chosen.
Angular: Convention, Tooling and a Published Support Window
Angular answers the questions React leaves open. Routing, forms, HTTP access, dependency injection, testing setup and project scaffolding all arrive in the box and all arrive with an opinion about how they should be used. For a team of two, this is overhead. For an organisation where several teams rotate across the same codebase over several years, it is the reason the codebase still reads consistently at the end of that period.
The framework’s use of typed code is not optional in practice, and this matters more than it sounds. Types on the boundaries between components are how large refactors stay survivable — the compiler finds the call sites, rather than a test suite finding some of them and production finding the rest.
Angular is also the clearest example of a maintenance contract stated in advance. The versioning and releases reference on angular.dev commits to a major release roughly every six months, with each major version receiving six months of active support followed by twelve months of long-term support during which only critical fixes and security patches land. That is a number a team can plan against: it tells you how often a scheduled upgrade appears on the roadmap and how much runway exists before a version stops receiving security work.
That published cadence is the honest answer to the question teams usually try to answer with popularity data. Whether a framework is fashionable tells you nothing about the shape of your maintenance year. Whether its maintainers publish a support window, and how long that window is, tells you a great deal.
Svelte: Moving the Work to Compile Time
Svelte’s premise is that the framework is a compiler, not a runtime library. Components are analysed during the build, and the output is code that performs targeted updates to the document. There is no general-purpose diffing engine shipped to the browser, because the specific updates each component can perform are already known.
The Svelte documentation presents this as the organising idea rather than as an optimisation, and the consequences run through the whole developer experience. Reactivity is expressed through ordinary assignment rather than through a dedicated API; markup, styles and logic sit together in one component file; transitions and animations are part of the framework rather than an add-on. The result is markedly less ceremony per component.
The trade is that more behaviour is decided by the compiler, which means debugging occasionally involves understanding what the compiler concluded. It also means the ecosystem is younger: fewer prebuilt component libraries, fewer engineers with production experience, and fewer worked examples for unusual requirements. For a product with ordinary requirements and a team willing to write its own components, none of that is disqualifying. For a product that needs a mature enterprise component suite next quarter, it is.
Svelte’s companion meta-framework adds routing, server rendering and build-time optimisation on top, which moves it from “a nicer way to write components” into the same category as the full-stack options discussed below.
Astro: Shipping Less JavaScript on Purpose
Astro starts from an observation about content sites that is easy to verify on any real project: most of the page does not need to be interactive, and shipping a framework runtime to make static text appear is pure cost.
The islands model documented on docs.astro.build renders the page to HTML at build time or on the server, and treats each interactive region as an isolated component that receives its own JavaScript only when it needs it. Regions that are not interactive receive none at all. Components from several different frameworks can coexist on one page, because each island is hydrated independently.
That makes Astro an unusually good fit for documentation, editorial sites, marketing pages and knowledge bases — anywhere the ratio of reading to clicking is high. It makes it a poor fit for an application whose entire surface is interactive, where the island boundary stops being a saving and starts being a constraint.
The content tooling reinforces the same positioning. First-class handling of Markdown-family formats and straightforward integration with external content sources mean the editorial workflow does not have to be built from scratch, which is usually the second-largest cost on this class of project after the design itself.
Meta-Frameworks: When the Question Becomes “Which Runtime”
The most consequential shift of the last few years is that the decision is rarely “which rendering library” any more. It is “which full-stack framework”, because the tools that matter now own the server as well as the browser.
The Next.js documentation makes the range explicit: a page can be rendered statically at build time, rendered on the server per request, or generated statically and revalidated in the background on a schedule. Those are not three products; they are three settings, chosen per route. A product catalogue and a personalised dashboard can live in one codebase with different rendering strategies, which is precisely the situation that used to force teams into two applications.
The Vue-side equivalent offers the same server-rendering capability with conventions drawn from Vue, and the same file-based routing model that turns directory structure into URL structure. Remix takes a more opinionated line, treating server rendering and web-standard form behaviour as the default path rather than an option, with nested routes that load their data in parallel and interfaces that keep working when client-side scripting fails.
The gain from all of this is real: better first render, better indexability, less bespoke plumbing. The cost is equally real and less discussed. Once framework code executes on a server, the application inherits a server’s threat surface — request handling, session state, data access, and the boundary between what the browser may ask for and what the server will do. Teams moving from a purely client-rendered application to a server-rendered one are taking on a set of concerns that the discipline of web application security exists to address, and they should take them on deliberately rather than discover them during an audit.
Commerce and Form-Heavy Products
Online retail is the clearest case where rendering strategy stops being an architectural preference and starts being a commercial constraint, which is why it is worth treating separately.
A product catalogue is content: it should be indexable, cacheable and fast on a first visit from a phone on a mobile network. Statically generated pages revalidated in the background fit that description exactly — the catalogue is served as prepared markup, and price or stock changes propagate without a full rebuild. A basket and a checkout are the opposite: personalised, stateful, and worthless if cached. In one codebase, on one framework, those opposing requirements need different rendering settings per route, and that capability is the actual reason meta-frameworks took over this category.
The second constraint is forms. Checkout is a sequence of forms with validation, error states, partial failure and payment redirects, and it is where abandonment happens. Frameworks that treat form submission as a first-class server-handled operation — rather than as an event handler that assembles a request by hand — remove a large class of bugs from the highest-value path in the product. Server-first handling also means the flow degrades rather than breaks when client scripting fails, which on a checkout page is the difference between a completed order and a lost one.
The third is integration. Most serious commerce frontends now talk to a separate commerce engine, a content source and a payments provider over their respective interfaces, which makes the frontend an integration surface as much as a rendering one. Teams underestimate this consistently, and the resulting cost is not a framework problem but an integration problem that arrives wearing a framework’s clothing.
Choosing for a Project, Not for a Résumé
Framework selection goes wrong in a predictable way: someone chooses the tool they most want to have on their CV, and the project inherits a set of trade-offs nobody examined. A defensible decision starts from properties of the project instead.
Shape of the interface. A content-dominant site and an application-dominant product have opposite requirements. The first should minimise shipped code and maximise cacheability; the second needs rich client state and will pay for it. A framework optimised for one is a liability for the other.
Size and turnover of the team. Enforced conventions cost velocity in a small stable team and buy consistency in a large rotating one. Match the framework’s opinionation to the team you will actually have once the product is in maintenance, not the team in the kickoff meeting.
Existing codebase. A framework that can be adopted page by page is worth a great deal when the alternative is a rewrite. Progressive adoption is a genuine technical property, not a marketing claim, and it is testable before commitment.
Rendering and indexability requirements. If the pages must be indexable and fast on first load, the rendering model is the decision and the component syntax is a detail. Choose the rendering model first.
Ecosystem depth for your specific needs. Not “how big is the community” but “does a maintained, accessible component exist for the three hard widgets in this product”. That question has a concrete answer, and answering it takes an afternoon.
Maintenance contract. How often do major versions land, how long is each supported, and what does the published migration guidance look like? A tool with a documented support window is easier to budget for than a tool with a livelier social-media presence.
Two things deliberately absent from that list are market-share rankings and salary surveys. Figures of that kind circulate constantly, rarely arrive with a stated sampling method or an accessible dataset, and change nothing about whether a given tool fits a given project. Where this article states a number, it states one published by the people who maintain the tool.
What the Choice Costs After Launch
The selection decision is cheap. Living with it is not, and the costs arrive in a familiar order.
Upgrades. Every framework has a cadence, and every cadence eventually produces a version that requires code changes. A team that treats upgrades as unplanned interruptions accumulates versions until an upgrade becomes a project; a team that puts the published cadence on the roadmap does small amounts of work repeatedly. The difference compounds.
Dependency drift. The framework is one dependency; the router, the state library, the component suite, the build tool and the test runner are others, each with its own maintainers and its own release rhythm. In an ecosystem where the framework supplies little, this collection is where the maintenance actually lives.
Hiring and onboarding. The relevant question is not how many developers know the tool globally but how quickly a competent engineer becomes productive in your codebase. A framework with strong conventions shortens that period; a codebase where every feature invented its own patterns lengthens it regardless of which framework is on the label.
Build and delivery. A modern frontend is compiled, bundled, split, optimised and deployed by automation, and that automation is a system with its own failure modes. Frameworks differ in how much of it they provide and how much the team assembles. Whatever the split, the pipeline that gets a change from a commit to production is part of the framework decision’s cost — which is why teams making this choice should understand what a working continuous integration and delivery practice requires of them before the first deployment rather than after the tenth.
Exit cost. Every framework choice is reversible in principle and expensive in practice. Business logic kept out of components migrates; business logic tangled into component lifecycles does not. The strongest hedge against a bad framework decision is not picking the right framework — it is keeping the parts that matter independent of whichever one you picked.
Rendering Strategy, Indexability and Measured Performance
Search indexing and perceived performance are frequently discussed as framework features. They are better understood as consequences of the rendering strategy, which is a decision the team makes and the framework merely permits.
A page rendered on the server or at build time arrives as markup. Its content is present in the initial response, its metadata is present in the initial response, and the browser can begin painting before any application code runs. A page rendered entirely in the browser arrives as an near-empty document plus instructions for building it, and everything downstream — indexing, first paint, and the behaviour of a client on a poor connection — depends on those instructions running successfully.
Google’s Web Vitals reference on web.dev gives the field a shared vocabulary and specific thresholds: Largest Contentful Paint at or below 2.5 seconds, Interaction to Next Paint at or below 200 milliseconds, and Cumulative Layout Shift at or below 0.1, each assessed at the 75th percentile of real page loads. Those numbers are useful precisely because they are published, stable and measured on real traffic rather than on a developer laptop.
The framework’s contribution to hitting them is mostly structural: how much code must execute before content appears, how much layout moves as late resources arrive, and how much work sits on the main thread when the user first tries to interact. A compiled or island-based approach starts closer to those thresholds; a heavy client-rendered application can still reach them with disciplined code splitting and careful asset handling. Neither outcome is determined by the choice alone.
The failure mode worth naming is measuring none of this. A team that has not instrumented these metrics on real traffic has opinions about its performance, not knowledge of it, and no framework choice repairs that.
The Platform Layer: Offline, Installability and Native-Adjacent Behaviour
Progressive web apps sit at the point where framework choice stops mattering and platform knowledge starts. Installability, offline behaviour, background synchronisation and push messaging are browser capabilities reached through service workers and a web application manifest. Frameworks differ in how much of that wiring they generate for you; none of them changes what the capabilities are.
The meta-frameworks generally provide service-worker generation and manifest handling as part of the build, with caching strategy left as a decision. Component-level frameworks typically expose the same capabilities through an official plugin. In every case the hard part is unchanged: deciding what a stale response is worth, what must be fresh, and what the application should do when the network is present but unreliable — which is a harder condition to handle correctly than being fully offline.
This is one of the clearest places where framework fluency and platform fluency diverge. An engineer who understands the caching model can implement it in any of these tools; an engineer who only knows the plugin will produce something that works in the demo and behaves unpredictably in the field. Teams building this class of application benefit from treating progressive web app capability as its own competence rather than as a checkbox inside a framework tutorial.
Where the Frontend Is Heading
Several directions are visible in current release notes rather than in prediction pieces, which makes them worth naming.
Selective and partial hydration is spreading beyond the tools that pioneered it. The underlying idea — decide per region whether interactivity is needed, and ship code accordingly — has proved general enough that mainstream frameworks are adopting variants of it.
The server is being reclaimed as a place where component code runs. Rendering components on the server and streaming the result, rather than shipping the components themselves, changes what a “frontend” codebase contains and blurs a boundary that had been stable for a decade.
Build tooling has consolidated. The era in which each project assembled a bespoke build configuration is ending, and the emerging defaults are fast enough that build performance has stopped being a differentiator worth arguing about.
Machine learning workloads in the browser have moved from demonstrations to production in narrow, well-chosen cases — inference on device where latency and data residency matter more than model size. The framework’s role here is unglamorous and important: keeping the interface responsive while asynchronous work proceeds, and rendering partial results without layout thrash.
None of these shifts invalidates the selection criteria above. They change which options score well on them.
Build Your Skills
Framework decisions get easier when the underlying model is clear — how state propagates, where rendering happens, and what the browser is being asked to do. Our trainers work through those mechanics on real code rather than on slides.
➡️ State Management in Vue.js, React and Angular Applications — EITT training
Frequently Asked Questions (FAQ)
Should a new project always pick the most popular framework?
No. Popularity is a proxy for ecosystem depth and hiring pool, and both of those can be checked directly for the specific requirements of the project. A tool that matches the rendering needs and the team’s structure will outperform a more widely used tool that does not, and the direct check takes less time than the argument about rankings.
Is a compiled framework always faster than a runtime one?
Not always, and the framing hides the real variable. A compiled approach starts with less runtime code, which helps most on constrained devices and initial loads. A well-engineered application built on a runtime framework, with disciplined code splitting and a sensible rendering strategy, routinely outperforms a careless application built on a compiled one. Architecture dominates the choice of tool.
When is a meta-framework the wrong answer?
When the application has no server-side requirements and no indexability requirements — an internal tool behind authentication, for example. In that situation the meta-framework adds a deployment target, a runtime and a threat surface in exchange for benefits the product does not need. The plain client-rendered option is the simpler and better answer.
How much does migrating between frameworks actually cost?
It depends almost entirely on how much business logic was written into components. Logic that lives in framework-independent modules moves with modest effort; logic embedded in component lifecycles, framework-specific state containers and template expressions has to be rewritten and re-tested. The cost is determined by decisions made during the build, not by the frameworks involved.
Do we still need to learn the underlying web platform?
Yes, and it is the highest-return investment on this list. Frameworks are abstractions over the document model, the styling engine, the language and the browser APIs, and every abstraction eventually requires you to understand what it abstracts. Platform knowledge transfers across every tool in this article; framework knowledge transfers to one.