Univa Learning

Module 2 of 9 Client

Web Frontend Frameworks

Level Intermediate Estimated study time 120 minutes Prerequisites basic HTML/CSS/JS, one project built in any JS framework
Video lesson: coming soon

Why this matters for Univa

Every Univa client engagement starts with the same silent question: "what do we build the UI in?" Get this wrong and you pay for it in hiring friction, slow delivery, or a rebuild in 18 months. Get it right once and it becomes a default you never have to re-litigate on every new project.

Univa already defaults to React via Next.js (per docs/app-building-philosophy.md). This module is not about picking a new default. It is about understanding why that default holds up under scrutiny, so that when a client, a job description, or a competing agency mentions Vue or Angular, you can speak credibly about the trade-offs instead of just saying "we don't do that." It also matters because React is not only a web choice: it is the same component and state model that powers React Native. A client who starts with a Univa web app has a straight, low-friction path to a mobile app later, without a team or skill-set change.

This also matters for reading job descriptions and client requests-for-proposal correctly. A client who insists on Angular is usually a large enterprise with an existing engineering org and governance requirements. A client asking for Vue is often replatforming from a WordPress or jQuery site with a small team. Recognizing that signal early changes how you scope and price the work, and it protects Univa from underquoting a job that secretly needs Angular-grade discipline.

There is also a direct cost angle, which matters given Univa's freemium-tier-first posture. The framework choice interacts with the rendering strategy choice, and the rendering strategy choice is what actually drives hosting bills. A React/Next.js app that leans on SSG and ISR wherever possible can often live entirely on Vercel's or Cloudflare Pages's free tier, while a poorly-chosen SSR-everywhere setup, in any framework, can quietly turn a simple SME site into a recurring compute cost. Understanding rendering strategy deeply, not just framework syntax, is what keeps a Univa build cheap to run for years.

Core concepts

What a frontend framework actually does: it takes application state (data that changes over time, such as a cart total, a form value, a list of clients, or whether a modal is open) and keeps the DOM (what the browser actually renders) in sync with that state. This happens without you manually writing document.querySelector calls and mutating elements by hand. Every framework in this module solves that same underlying problem with a different philosophy about who tracks the state and how updates propagate to the screen.

React.js (Meta, 2013) is a library, not a full framework, for building UI out of composable components. Its core idea: describe the UI as a function of state (UI = f(state)), and let React figure out the minimal set of DOM changes needed. Historically this used a virtual DOM diff; modern React (with the React Compiler) increasingly optimizes this at build time instead, but the mental model for the developer stays the same: you never manually touch the DOM. React itself is deliberately unopinionated: it does not ship routing, data fetching, or a build tool. You either assemble those yourself from the ecosystem (React Router, TanStack Query, Vite) or, far more commonly today, adopt a meta-framework like Next.js that bundles those decisions for you. This unopinionated core is also why React Native exists and works so well: it takes the exact same component model and state-management mental model (hooks, props, one-way data flow) and points it at native mobile views instead of the DOM. Anyone who already knows React for web already knows roughly 80 percent of React Native; only the platform-specific view primitives and native modules are genuinely new.

Vue.js (Evan You, 2014) is a progressive framework: you can drop a single <script> tag into an existing HTML page and start using it incrementally, or scale all the way up to a full single-page application with Vue's official router (Vue Router) and state library (Pinia). Vue's signature feature is the single-file component (.vue files with template, script, and style all in one file), which keeps related code physically together and reads closer to plain HTML for newcomers. Vue's reactivity system is also more implicit than React's: you mutate a reactive variable directly (count.value++) and the template updates automatically, with no explicit setState call needed. This combination, gentler syntax plus automatic reactivity, is why Vue is consistently rated as one of the easiest frameworks to pick up for developers coming from plain HTML/CSS/JS rather than a heavily JS-centric background.

Angular (Google, 2016 full rewrite of the original AngularJS) is a complete, opinionated platform, not just a UI library. Out of the box it ships routing, reactive and template-driven forms, an HTTP client, a dependency injection system, and a full CLI for scaffolding and builds. It is built on TypeScript as a hard requirement, not an optional add-on the way TypeScript is for React and Vue. Angular enforces structure: components, services, and modules (or, in newer versions, standalone components) combine with dependency injection to make large codebases predictable and consistent across many different engineers touching the same repo over years. The cost is more boilerplate, more concepts to learn upfront (observables via RxJS, change detection zones, decorators), and a noticeably steeper initial learning curve than React or Vue.

Where meta-frameworks fit: none of the three frameworks above is typically used "bare" in production for a real app anymore. A meta-framework wraps the base framework and adds the missing pieces: routing, data fetching conventions, rendering strategy control, image and font optimization, and a deployment target. Next.js is React's dominant meta-framework and the one Univa defaults to. Nuxt plays the identical role for Vue: file-based routing, server-side rendering, static generation, and a plugin ecosystem, structured almost one-to-one with how Next.js is structured for React. SvelteKit deserves an honorable mention here even though Svelte itself is a different base framework from the three above. Svelte is a compiler that turns component code into vanilla JS at build time rather than shipping a runtime library to the browser, so SvelteKit apps tend to ship less JavaScript and load faster than equivalent React or Vue apps. Its ecosystem and hiring pool are smaller, which is exactly why it is a footnote here rather than a serious default candidate for client handoff work.

State management in practice: this is where the three frameworks diverge most in daily development. In React, local component state (useState) covers most needs; when state must be shared across distant parts of the tree, teams reach for React's built-in Context API for simple cases, or a third-party library such as Zustand or Redux for larger, more complex apps. React ships no official opinion here, which is flexible but means every team (and every job description) can look different. Vue ships Pinia as the official, blessed state library, so most Vue teams converge on the same pattern with far less debate. Angular leans on injectable services combined with RxJS observables for shared state, and larger Angular shops often add NgRx (a Redux-style library built for Angular) once the app's state graph gets complex enough to need strict, traceable state transitions. For Univa's typical SME project size, React's local state plus the Context API is almost always enough; a dedicated state library only becomes worth the setup cost once an app has many independent teams touching overlapping data.

A note on convergence: the three frameworks borrow ideas from each other constantly, which is worth knowing so old "framework war" talking points do not get repeated uncritically. Vue 3's Composition API was influenced by React Hooks, letting Vue developers organize logic by feature rather than by option type. Angular introduced Signals (from Angular 16 onward) as a fine-grained reactivity primitive, an idea popularized by newer frameworks like SolidJS and philosophically close to what Vue's reactivity system already did implicitly. React itself keeps absorbing server-rendering ideas that Vue and Angular's SSR/Universal modes explored years earlier. The practical takeaway: pick based on ecosystem, hiring, and team fit today, not on which framework currently "wins" on a specific technical feature, since most meaningful features migrate across all three within a couple of release cycles anyway.

Common pitfalls when picking a framework, worth naming so they do not sneak into a Univa decision unnoticed:

  • Choosing a framework because it is personally more fun to write in, rather than because it fits the client's team and timeline.
  • Adding a state management library (Redux, NgRx, Pinia stores for every feature) before an app actually has a cross-cutting state problem, which adds complexity with no corresponding benefit.
  • Assuming "more opinionated" always means "more enterprise-ready"; opinionation is a fit question, not a maturity ranking, since React quietly runs a large share of enterprise software too.
  • Underestimating Angular's ramp-up time when a project timeline assumes React-speed delivery but the team ends up building in Angular for the first time.
  • Picking SvelteKit or another niche framework for a client-facing build without warning the client that future in-house hires will be harder to find.

TypeScript across the three: all three frameworks work well with TypeScript today, but the relationship differs. Angular requires it: there is no realistic way to build a modern Angular app in plain JavaScript. React and Vue both treat TypeScript as optional but strongly recommended, and the wider ecosystem (Next.js, Nuxt, component libraries, AI SDKs) is written TypeScript-first regardless. Univa's own standard, per docs/app-building-philosophy.md, is TypeScript strict mode on every project, so this distinction rarely changes a real decision, but it is worth knowing that Angular's TypeScript requirement is a hard technical constraint, not a team preference.

Rendering strategies (the part that actually affects architecture and cost decisions):

  • CSR (Client-Side Rendering): the server sends a near-empty HTML shell plus a JavaScript bundle; the browser downloads the JS, runs it, and builds the entire page on the client. Fast and simple to develop against, but the user stares at a blank screen or a spinner until the JS loads and executes, and search engines historically struggled to index CSR-only pages correctly (modern crawlers have improved at this, but it remains a real risk). Classic create-react-app or plain Vue CLI single-page apps work this way by default.
  • SSR (Server-Side Rendering): the server runs the framework's rendering logic fresh on every single request and sends back fully-formed HTML. The user sees real content immediately, no blank screen, and it is reliably SEO-friendly, but every request costs server compute time, and response times are inherently slower than serving a pre-built static file.
  • SSG (Static Site Generation): the HTML is rendered exactly once, at build time, and the resulting files are served afterward as plain static assets straight from a CDN edge node. This is the fastest possible load time and the cheapest possible hosting (zero per-request compute), but the content is frozen until the next build and deploy, so it does not fit content that changes frequently.
  • ISR (Incremental Static Regeneration): Next.js's middle ground between SSG and SSR. Pages are statically generated exactly like SSG, but the framework can automatically regenerate individual pages in the background after a configured time interval (say, every 60 seconds) or on demand when triggered, without requiring a full site rebuild. The practical result: visitors almost always get static-speed serving, while the content itself never goes permanently stale.
  • Server Components (React Server Components, used by Next.js App Router): a newer rendering model where some components render only on the server and never ship any of their JavaScript to the browser at all, while other components are explicitly marked as "client components" and stay fully interactive in the browser as before. This shrinks the total JS the browser has to download and parse, and lets server components query a database or call a secret-holding API directly without exposing that logic to the client. It introduces a genuinely new mental split (deciding which files need the 'use client' directive at the top) that takes real practice to internalize.

A note on hydration: SSR, SSG, and ISR all send fully-formed HTML to the browser first, which is why the user sees content immediately. But that HTML is initially static and non-interactive; the framework's JavaScript still has to load in the background and attach event handlers to the existing markup, a process called hydration, before buttons, forms, and interactivity actually work. This is why a page can visually appear ready but not yet respond to a click for a brief moment, and why minimizing the JS needed for hydration (which is exactly what Server Components help with) directly improves perceived speed.

None of these five strategies is "the winner" in isolation. A single production Next.js app typically mixes several per page: SSG for the pricing page, ISR for a blog or catalog that updates hourly, SSR or server components for a personalized logged-in dashboard, and a small pocket of CSR for an in-page widget (a live chart, a drag-and-drop board) that genuinely only needs to run in the browser.

The landscape (comparison tables)

Framework comparison

DimensionReact.jsVue.jsAngular
TypeUI library (needs a router/meta-framework for a full app)Progressive frameworkFull platform
LanguageJS, TypeScript optionalJS, TypeScript optionalTypeScript required
Learning curveModerateGentleSteep
Reactivity modelExplicit (useState, hooks)Implicit (reactive refs, auto-tracked)RxJS observables + Zone.js change detection, plus newer Signals
Component syntaxJSX (JS + HTML-like markup mixed)Single-file components (template/script/style)Class or standalone components + templates
Official state libraryNone built in (Redux, Zustand, Jotai, etc. are third-party)Pinia (official)RxJS-based services, or NgRx (third-party)
Bundle size (baseline)Small to moderateSmallLarger
Best backed meta-frameworkNext.jsNuxtAngular's own CLI/SSR (Angular Universal)
Mobile crossoverReact Native (same component model)NativeScript-Vue, Capacitor (less first-class support)Ionic + Angular (hybrid webview, not the same native model)
Job market (global)LargestSmaller than React, notably strong in parts of Asia and EuropeStrong in enterprise, finance, and government
GovernanceMeta + open-source communityIndependent, community-led by Evan YouGoogle
Enforced structure out of the boxLow (you decide almost everything)MediumHigh (opinionated by default)
Typical team size fitAny size, scales with tooling choicesSmall to mid-size teamsMid to large teams, especially enterprise

Rendering strategy comparison

StrategyWhen HTML is builtServer cost per requestSEOBest for
CSRIn the browser, after JS loadsNone (static file host)Weak unless hydration/pre-rendering is handled carefullyInternal dashboards, logged-in-only apps
SSROn every requestHighStrongPersonalized pages that must also be SEO-visible (an uncommon combination)
SSGOnce, at build timeNoneStrongMarketing sites, documentation, pricing pages, portfolios
ISRAt build, then periodically refreshedLow (only on regeneration)StrongContent that updates occasionally: product catalogs, listing pages, blogs
Server ComponentsOn the server per request, with a smaller client-side JS payloadModerateStrongData-heavy pages where cutting client JS matters (admin panels, dashboards)

Meta-framework comparison

Meta-frameworkBase frameworkRendering strategies supportedEcosystem maturityUniva fit
Next.jsReactCSR, SSR, SSG, ISR, Server ComponentsVery highDefault choice
NuxtVueCSR, SSR, SSG, ISR (via modules)HighFallback if client mandates Vue
SvelteKitSvelteCSR, SSR, SSGGrowing, smallerNiche: small, solo-maintained static-heavy sites
Angular UniversalAngularSSR, SSG (limited/newer)High, enterprise-focusedOnly inside an existing Angular shop

Ecosystem and tooling snapshot

ConcernReact / Next.jsVue / NuxtAngular
StylingTailwind, CSS Modules, styled-components (all common)Tailwind, scoped <style> blocks (built in)Angular Material, component-scoped CSS
Component librariesshadcn/ui, Radix, MUI, ChakraVuetify, PrimeVue, Naive UIAngular Material, PrimeNG
TestingJest/Vitest + React Testing Library, PlaywrightVitest + Vue Test Utils, PlaywrightJasmine/Karma (default), Jest (common swap)
Build toolTurbopack/webpack (via Next.js), Vite (standalone React)Vite (default via Nuxt/Vue CLI)Angular CLI (esbuild-based)
AI SDK / LLM examples first-shipped forAlmost always React firstSometimes, usually a step behind ReactRarely first, community ports common
Hiring pool in MalaysiaLargestModeratePresent, concentrated in enterprise/bank IT

How to choose (decision rules)

Picking a framework:

  • If the client's in-house team already uses one of these, or a job description explicitly mandates one, use that one. Consistency with the existing stack beats personal preference every time; retraining a client's team onto a different framework is rarely worth the friction.
  • If it is a greenfield Univa build with no external constraints: default to React + Next.js. It has the largest hiring pool in Malaysia and globally, the richest ecosystem (component libraries, AI SDKs, and auth providers all ship React examples first, often exclusively), and the smoothest path to a future mobile app via React Native if the client's scope ever grows.
  • If the client is a large enterprise with a big in-house dev team (roughly 10 or more engineers), strict architectural governance, and a long product horizon (5 or more years): Angular's opinionation becomes an asset, not a tax. It forces consistency across many hands and many years, which is exactly when "there is one correct way to do this" pays for itself. This pattern shows up most often in banking, government, insurance, and large corporate internal tooling.
  • If the team is small, mixed-skill (backend developers picking up frontend for the first time), or the project is a fast internal tool or admin panel with a short timeline: Vue's gentle curve wins. Developers become productive in days rather than weeks, and Vue's single-file components keep markup, logic, and styling together in one readable file, which is friendly for less specialized teams and easier to hand off for future maintenance.
  • If the project is almost pure static content with minimal interactivity (a landing page, a documentation microsite, a one-off campaign page): consider SvelteKit as an honorable mention. It compiles away the framework at build time, shipping close to zero runtime JavaScript, so pages load extremely fast and light. Its smaller ecosystem and hiring pool mean it fits best for small, self-contained builds where Ahnaf remains the sole maintainer, not for client handoffs where the client's future in-house hires will need to maintain the codebase.
  • If the client explicitly cares about future in-house hiring and wants to avoid a niche framework: rule out SvelteKit and any framework outside the big three, regardless of technical merit. A framework nobody local can hire for is a long-term liability, even if it produces a faster app today.
  • If a project starts small but the client has stated plans to scale the engineering team significantly within a year or two: weigh Angular earlier than usual, since retrofitting Angular-style discipline onto an already-large React codebase is more painful than starting with structure.
  • If in doubt and there is no strong signal either way, default to React + Next.js. The downside risk of "overusing" React is low (it scales fine down to small projects); the downside risk of choosing Angular or a niche framework for a project that turns out small or short-lived is high.

Picking a rendering strategy (decided per page or route within Next.js, not once for the whole app):

  • If the page's content is identical for every visitor and rarely changes: use SSG.
  • If the content changes occasionally (daily or hourly) but does not need to be instantly fresh: use ISR.
  • If the content is personalized per logged-in user, or must be real-time-fresh on every single load: use SSR or server components.
  • If a specific piece of the page is purely client-side interactive with no SEO relevance (a chart, a filter widget, a modal, a drag-and-drop board): keep that one piece as a client component, nested inside an otherwise SSG or ISR page, rather than converting the whole page to CSR.
  • Default assumption for a new Univa client site: marketing pages use SSG, a blog or catalog uses ISR, an authenticated dashboard uses server components for the data-heavy shell with client components for the interactive bits inside it.

Univa playbook

  • Default stack: React + Next.js (App Router), TypeScript strict, Tailwind, exactly as set out in docs/app-building-philosophy.md. This is the answer for roughly 90 percent of client work: SME websites, internal tools, AI-powered apps.
  • When to deviate to Vue/Nuxt: almost never for a brand-new client build, but worth knowing cold if Univa inherits an existing Vue codebase from a client, or a client's in-house team is Vue-native and specifically wants to maintain the app themselves after handoff. Nuxt is Vue's direct Next.js equivalent (file-based routing, SSR/SSG/ISR support, a similar plugin model), so it is the natural landing spot if that situation arises.
  • When to deviate to Angular: only if the client is a large enterprise that mandates it contractually, or Univa is subcontracting a feature into an existing Angular codebase already in production. This is not a first choice for SME work: the framework is heavier and the learning curve steeper than the team sizes and delivery timelines Univa typically operates under justify.
  • Rendering default: lean on Next.js's flexibility rather than picking one strategy globally. Start every new page as SSG unless it clearly needs to be dynamic; convert to ISR the moment content needs to refresh periodically; reserve SSR or server components for genuinely personalized or data-heavy views. This keeps hosting costs near zero on Vercel's free tier or Cloudflare Pages, consistent with Univa's cost-conscious, freemium-tier-first posture.
  • State management default: start every project with plain React local state and the Context API only. Do not reach for Zustand, Redux, or any external state library until a concrete cross-cutting state problem actually appears; adding a state library upfront on an SME-scale app is unnecessary complexity that slows delivery without a corresponding benefit.
  • Talking to clients: if a client asks "why not Angular or Vue," the honest answer is: React and Next.js give the fastest delivery timeline, the largest Malaysian hiring pool if the client ever wants to bring development in-house, and a straight upgrade path to a mobile app later without a team change. That is a stronger, more concrete pitch than simply "it's what we know."
  • Red flag to watch for: a client insisting on a framework mismatched to their team size (for example, a two-person startup mandating Angular because "it's what a senior hire used at their last enterprise job") is a scoping risk. Flag it early, explain the trade-off in plain terms, and let the client make an informed call rather than silently building what they asked for.

Hands-on exercise

Take any small existing Univa project page (or spin up a throwaway one) and implement the same simple feature three ways in roughly one evening:

  1. Build it as a client component in the Next.js App Router ('use client' at the top, using useState) that fetches a list of 5 mock items and lets the user filter them by typing into a search box.
  2. Convert that same page so the initial list renders as a server component (no 'use client' directive, data fetched directly in the component), passing the interactive search box down as a small nested client component.
  3. Open your browser's dev tools Network tab for both versions and compare the size of the JavaScript actually shipped to the browser.
  4. Optionally, repeat step 1 in a fresh Vue single-file component (using Vue's ref and a v-model bound search input), just to feel the syntax difference firsthand, even without deploying it anywhere.

Stretch goal (if time allows): add a second field to the mock data (a category) and implement multi-field filtering in the React version using nothing but local state, to confirm for yourself that a state library really is unnecessary at this scale. Then imagine the same feature needing to sync across three unrelated components on the page, and note in one sentence when you would finally reach for Context or Zustand instead of prop-drilling the filter state down manually.

Write two or three sentences afterward: what changed in the bundle size between the client-only and server-component versions, and on a real client project, when would you reach for the server-component version versus keeping something as a plain client component.

Self-check

  1. Why is React described as a "library" while Angular is described as a "platform"?
  2. Name one concrete reason React knowledge transfers directly to React Native, while Vue knowledge does not transfer as directly to an equivalent mobile framework.
  3. A client wants a blog that updates once a day and must load instantly with good SEO. Which rendering strategy fits best, and why is plain SSR not the better choice here?
  4. A client's in-house team is 15 backend engineers with no frontend specialists, who will maintain the app themselves for years after handoff. Which framework tilts the choice, and why?
  5. What is the practical downside of choosing SSR for every single page on a site, even though it guarantees perfectly fresh content on every load?

Answers:

  1. React only solves the narrow "describe UI as a function of state" problem and leaves routing, data fetching, and build tooling to the wider ecosystem or a meta-framework; Angular ships all of those built in as one complete, opinionated system out of the box.
  2. React Native reuses React's exact component and state model (hooks, props, one-way data flow), just targeting native views instead of the DOM, so the mental model carries over almost one-to-one; Vue has no equivalent officially-backed native framework with the same first-class ecosystem support and adoption.
  3. ISR: it serves static-speed, SEO-friendly pages the vast majority of the time and only regenerates content in the background on a schedule, so it never pays the full per-request server cost that SSR would incur for content that only actually needs daily freshness.
  4. Angular, because its built-in structure (services, modules or standalone components, dependency injection) gives less-specialized frontend hands a single, enforced "correct" way to build features, which matters more than raw agility when many non-specialist engineers will maintain the codebase over years.
  5. Every single request pays a server compute cost and added latency compared to serving a pre-built static file, so the site scales worse and costs more under traffic spikes than SSG or ISR would for content that does not actually require per-request freshness.

Further reading