Sitemap

Every Frontend Architecture Pattern

A simple guide from MVC to Micro Frontends

--

Press enter or click to view image in full size

When I started my career as a frontend developer I thought architecture was something only senior people talk about in long meetings. I used to think my job was to make the button blue and make the API call work. That is it.

Then one day I joined a project where a single React file had 3000 lines. Nobody knew where the state was coming from. A small change in the header was breaking the checkout page. That day I understood why architecture matters.

In this article I will explain every major frontend architecture pattern I have seen in real projects. I will keep it simple. I will tell you what the pattern is and when you should use it. I will also tell you when you should not use it because that part is equally important.

So grab your chai and let us start.

First what do we even mean by frontend architecture?

Frontend architecture is just the answer to three simple questions.

  1. Where does my data live?
  2. How does my data move from one place to another?
  3. How is my code divided into files and folders?

Every pattern you will read below is one way of answering these three questions. Some patterns focus on data. Some focus on UI. Some focus on how the page reaches the browser. Once you see it like this the whole topic becomes much less scary.

I have divided the patterns into four groups.

  • Data and state patterns like MVC, MVP, MVVM and Flux
  • UI and component patterns like Atomic Design and Container Presentational
  • Code organisation patterns like Feature Sliced Design, Clean Architecture and Monorepo
  • Rendering and delivery patterns like SSR, SSG, Islands, Server Components and Micro Frontends

Part 1. Data and State Patterns

1. MVC (Model View Controller)

This is the grandfather of all patterns. It came from desktop apps long before the web was a thing.

The idea is to split your app into three parts.

  • Model holds the data and business rules
  • View shows the data to the user
  • Controller listens to the user and tells the model what to change
Press enter or click to view image in full size

The user clicks something. The controller catches that click and updates the model. The model then tells the view that something changed. The view redraws itself.

Old frameworks like Backbone.js and early AngularJS followed this pattern. Even today many backend frameworks like Spring MVC follow it.

When to use it. Small apps and server rendered apps where the flow is simple.

Where it hurts. In big frontend apps many views talk to many models. The arrows start going everywhere and you get what we call spaghetti. Debugging becomes a nightmare because you cannot tell who changed what.

2. MVP (Model View Presenter)

MVP is a small twist on MVC. Here the view becomes very dumb. It does not talk to the model at all.

  • View only shows things and passes user events to the presenter
  • Presenter holds all the logic and updates the view directly
  • Model holds the data

The good thing about MVP is testing. Since the presenter has all the logic and no UI code you can test it very easily. This pattern was very popular in Android apps and in GWT based web apps.

When to use it. When you want logic that is fully separate from the UI so you can unit test it.

Where it hurts. You write a lot of extra code. Every view needs its own presenter and its own interface.

3. MVVM (Model View ViewModel)

MVVM is the pattern that made two way data binding famous.

Press enter or click to view image in full size
  • Model is your data coming from the server
  • View is the HTML template
  • ViewModel is a special object that holds the state of the view

The magic part is the binding. When the ViewModel changes the view updates on its own. When the user types in an input box the ViewModel updates on its own. You do not write any code to sync them.

Vue.js, Angular, Knockout and Svelte are all heavily inspired by this idea. If you have ever used v-model in Vue or ngModel in Angular then you have used MVVM.

When to use it. Form heavy apps like admin panels, dashboards and enterprise tools where a lot of inputs are directly linked to data.

Where it hurts. Two way binding is easy at first but in very large apps it becomes hard to track which change triggered which update. One small change can cause a chain reaction across the screen.

4. Flux and Redux (Unidirectional Data Flow)

Facebook faced exactly the problem I just mentioned. Their notification count was showing wrong numbers because too many things were updating too many other things. So they created Flux. Redux came later and made Flux simpler.

The rule is very strict. Data flows in only one direction.

Press enter or click to view image in full size
  1. The view dispatches an action. An action is just a plain object like { type: “ADD_TO_CART”, payload: item }.
  2. The reducer receives the action and the current state. It returns a brand new state.
  3. The store saves that new state.
  4. Every component that subscribed to the store gets the new data and re renders.

What about API calls? Reducers must be pure functions so they cannot call APIs. That work goes into middleware. If you have used Redux Thunk or Redux Saga then you know what I am talking about. Thunk is simple and good for most cases. Saga uses generator functions and is great when you have complex flows like retry logic, cancelling requests or running tasks in parallel.

When to use it. Medium to large apps where many screens share the same data. Think ecommerce cart, user session or a live trading dashboard.

Where it hurts. Boilerplate. For a simple app it feels like writing ten files to toggle one checkbox. Redux Toolkit has fixed a lot of this pain so if you are still writing old style Redux please give RTK a try.

Today we also have lighter options like Zustand, Jotai and Recoil. They follow the same idea of a central store but with much less code. And for server data most teams now use React Query or SWR instead of keeping everything in Redux.

Part 2. UI and Component Patterns

5. Component Based Architecture

This is the foundation of almost every modern framework. React, Vue, Angular, Svelte and Solid all follow it.

The idea is simple. You build your UI as a tree of small independent pieces. Each piece has its own markup, its own style and its own logic. You combine small pieces to make bigger pieces. Just like Lego.

A good component follows a few rules.

  • It does one job well
  • It gets its data through props or inputs
  • It does not reach out and touch other components directly
  • It can be reused in another screen without changes

Honestly every other pattern in this article sits on top of this one. If you get components right half of your architecture problems are already solved.

6. Atomic Design

Brad Frost gave us this idea and it is borrowed from chemistry. Your UI is built in five levels.

Press enter or click to view image in full size
  • Atoms are the smallest things. A button. An input. A label. An icon.
  • Molecules are small groups of atoms. A search box is an input plus a button.
  • Organisms are bigger sections. A header has a logo, a nav menu and a search box.
  • Templates are page layouts with empty slots.
  • Pages are templates filled with real content.

This pattern is the backbone of most design systems. If your company has a shared UI library in Storybook then most likely it is organised like this.

When to use it. When you are building a design system or when many teams share the same UI components.

Where it hurts. People waste a lot of time arguing whether something is a molecule or an organism. My advice is do not be too strict. Use the levels as a guide and not as a law.

7. Container and Presentational Components

Dan Abramov made this pattern popular in the early React days. The idea is to split every feature into two types of components.

Press enter or click to view image in full size
  • Container components know how things work. They fetch data, manage state and handle events.
  • Presentational components know how things look. They only receive props and render UI.

So you might have a UserListContainer that calls the API and a UserList that just shows the cards.

The best part is that your presentational components become super reusable and super easy to test.

A small update. After React Hooks came this pattern became less strict. Now we usually move the container logic into a custom hook like useUsers() and the component just calls that hook. The idea is the same. Only the shape has changed.

8. Compound Components and Render Props

These are smaller patterns but you will see them in every good component library.

Compound components let a parent share state with its children without passing props manually. Think of how a select box works in HTML.

<Tabs>
<Tabs.List>
<Tabs.Tab>Profile</Tabs.Tab>
<Tabs.Tab>Settings</Tabs.Tab>
</Tabs.List>
<Tabs.Panel>Profile content</Tabs.Panel>
<Tabs.Panel>Settings content</Tabs.Panel>
</Tabs>

The Tabs parent keeps track of the active tab and shares it through context. Radix UI and Headless UI use this pattern a lot.

Render props and headless components give you the logic and let you decide the UI. Libraries like TanStack Table and Downshift work this way. They handle sorting, filtering and keyboard support. You just write the markup.

Part 3. Code Organisation Patterns

9. Feature Sliced Design

Most of us start a project like this.

src/
components/
hooks/
services/
utils/

This is called organising by type. It works fine for small apps. But when your app grows the components folder has 200 files and you have no idea which file belongs to which feature.

Feature Sliced Design fixes this. It organises code by feature and by layer.

Press enter or click to view image in full size
  • app has global setup like providers, routing and styles
  • pages are full screens
  • widgets are big independent blocks on a page
  • features are user actions like add to cart or login
  • entities are business objects like user, product or order
  • shared has reusable stuff like UI kit, helpers and API config

The golden rule is that a layer can only import from layers below it. A feature can use an entity. An entity can never use a feature. This one rule alone stops so much circular dependency mess.

When to use it. Medium to large apps with multiple developers.

Where it hurts. There is a learning curve. New developers need some time to understand where to put things.

10. Clean Architecture (Layered Architecture)

This idea comes from the backend world and Uncle Bob. But it works beautifully in the frontend too, especially in apps with heavy business logic.

Press enter or click to view image in full size
  • Domain layer has pure business rules. No React. No API. Just plain TypeScript. For example a function that calculates the final price after discount and tax.
  • Application layer has use cases. These are your hooks or services that connect the UI with the domain.
  • Infrastructure layer talks to the outside world. API clients, local storage, analytics.
  • UI layer is your React or Vue components.

The main benefit is that your business rules do not depend on any framework. If tomorrow your company moves from React to something else your domain code stays exactly the same.

Get Himanshu Singour’s stories in your inbox

Join Medium for free to get updates from this writer.

When to use it. Banking apps, insurance apps, trading platforms or anything where the business logic is complex and important.

Where it hurts. For a simple marketing site this is total overkill. Do not build a five storey building for a tea stall.

11. Event Driven Architecture (Pub Sub)

Sometimes two parts of your app need to talk but they do not know each other. Here an event bus helps.

Press enter or click to view image in full size

One part publishes an event like “item added”. Any other part that cares about it can subscribe and react. The cart widget does not need to know that the header badge, the analytics tracker and the toast message exist.

You can build this with a tiny custom emitter, with the browser’s CustomEvent API or with libraries like mitt. It is also very common in micro frontends where different apps need to talk to each other.

When to use it. Loosely connected widgets, plugin systems and micro frontends.

Where it hurts. Too many events and you lose track of the flow. Please keep a list of all event names in one place. Your future self will thank you.

12. Monorepo Architecture

A monorepo is one Git repository that holds many apps and many shared packages.

Press enter or click to view image in full size

For example your customer website, your admin panel and your mobile app can all live in one repo. They share the same UI library, the same utils and the same ESLint and TypeScript config.

Tools like Nx, Turborepo and pnpm workspaces make this easy. They are smart enough to only build and test the parts that actually changed.

When to use it. When you have multiple apps that share a lot of code.

Where it hurts. CI setup and access control get tricky. A very big monorepo also needs good tooling or builds become slow.

Part 4. Rendering and Delivery Patterns

This part is about how your HTML actually reaches the user’s browser. This decides your page speed and your SEO.

13. SPA vs MPA

Multi Page Application (MPA) is the classic web. Every click loads a new HTML page from the server. Old PHP sites, JSP apps and most WordPress sites work like this.

Single Page Application (SPA) loads one HTML page once. After that JavaScript handles everything. Clicking a link just swaps the content without a full reload. Apps built with Create React App or plain Vite React are SPAs.

SPAs feel smooth like a mobile app. But the first load can be slow and search engines may struggle with them. That is why the patterns below exist.

14. CSR, SSR, SSG and ISR

Press enter or click to view image in full size

Client Side Rendering (CSR). The server sends an almost empty HTML file and a big JavaScript bundle. The browser runs the JS and builds the page. Good for dashboards behind login where SEO does not matter.

Server Side Rendering (SSR). The server builds the full HTML for every request and sends it. The user sees content quickly. Then JavaScript loads and makes it interactive. This step is called hydration. Good for pages that change often and need SEO like a product page or a news feed.

Static Site Generation (SSG). All HTML pages are built once at build time and served from a CDN. Super fast and super cheap. Good for blogs, docs and landing pages.

Incremental Static Regeneration (ISR). This is a mix of SSG and SSR. Pages are static but they rebuild in the background after a fixed time or when you trigger it. Good for an ecommerce site with thousands of products where prices change sometimes but not every second.

Next.js, Nuxt, Remix and SvelteKit let you use all of these in the same app. You can choose per page. This flexibility is the reason these meta frameworks became so popular.

15. Islands Architecture

Here is a question. Why should a blog page load JavaScript for the whole page when only the comment box needs it?

Islands Architecture answers this.

Press enter or click to view image in full size

The page is mostly static HTML. Only a few small parts are interactive. These parts are called islands. Each island loads its own JavaScript independently. Everything else is plain HTML with zero JavaScript.

Astro made this pattern famous. Fresh and Eleventy with plugins also support it.

When to use it. Content heavy sites like blogs, marketing pages, docs and news sites.

Where it hurts. Not a good fit for apps where almost everything is interactive like a Figma clone or a chat app.

16. React Server Components

This is the newest big pattern and it changed how we think about React.

Press enter or click to view image in full size

Now components are of two types.

  • Server Components run only on the server. They can read the database directly. Their code is never sent to the browser. So your bundle size becomes smaller.
  • Client Components run in the browser. You mark them with “use client” at the top. Only these can use useState, useEffect and event handlers.

Server components can render client components and pass props to them. So you keep the heavy data work on the server and send only small interactive pieces to the browser.

The Next.js App Router is built fully around this idea.

When to use it. New Next.js projects where performance and SEO matter.

Where it hurts. The mental model is new. Many developers get confused about what runs where. Some older libraries also do not work well inside server components yet.

17. Micro Frontends

Imagine a company like Flipkart or Amazon. Hundreds of developers work on one website. If everybody pushes code into one big frontend app then every release becomes a war.

Micro Frontends solve this by doing for the frontend what microservices did for the backend.

Press enter or click to view image in full size

The website is split into smaller apps owned by different teams. One team owns product pages. Another owns the cart. Another owns payments. Each team can deploy on its own without waiting for others. In theory each team can even use a different framework.

A shell app (also called host or container) loads these micro frontends and handles common stuff like routing, login and layout.

Popular ways to build them.

  • Module Federation in Webpack or Rspack. This is the most common choice today.
  • Single SPA as a framework to mount multiple apps together.
  • Web Components as a framework neutral wrapper.
  • iframes which are old school but still used for strong isolation.

When to use it. Large organisations with many independent teams.

Where it hurts. It adds a lot of complexity. Shared dependencies, version mismatch, consistent styling and performance all become harder. For a team of five developers please do not do this. A well structured monolith is better.

18. Backend for Frontend (BFF)

This one sits between the frontend and the backend but frontend teams usually own it.

Press enter or click to view image in full size

Different clients need different data. The mobile app wants a small response. The web app wants a richer one. Instead of making every client call five microservices and stitch the data you create a small server for each frontend. That server collects data from the services and returns exactly what the frontend needs.

You can build a BFF with Node.js, with Next.js API routes or with GraphQL.

When to use it. When you have many backend services and multiple types of clients.

Where it hurts. One more layer to build, deploy and monitor.

So which pattern should you choose?

This is the most important part so please read it slowly.

You never choose only one pattern. A real app is always a combination. Let me give you a real example from a typical ecommerce project.

  • Next.js with SSR and ISR for product pages
  • React Server Components for heavy listing pages
  • Feature Sliced Design for the folder structure
  • Atomic Design for the shared UI library
  • Redux Toolkit or Zustand for the cart and user session
  • React Query for server data
  • A BFF layer to combine product, price and stock services
  • A monorepo with Turborepo to share code with the admin panel

See how each pattern solves one specific problem?

Here is a quick cheat sheet I use.

  • Small personal project. Component based architecture with simple hooks. Nothing more.
  • Blog or marketing site. SSG or Islands with Astro.
  • Admin dashboard behind login. SPA with CSR, React Query and maybe Zustand.
  • Ecommerce or content platform. Next.js with SSR, ISR and Server Components.
  • Complex business app like banking. Add Clean Architecture and Feature Sliced Design.
  • Huge company with many teams. Monorepo first. Micro frontends only if teams really need independent deploys.

A few things I learned the hard way

Start simple. Do not add Redux, micro frontends and clean architecture on day one. Add a pattern only when you actually feel the pain it solves.

Consistency beats perfection. A team following an average pattern consistently will always do better than a team where everybody follows their own perfect pattern.

Write it down. Keep a small README or an ADR (Architecture Decision Record) that explains why you picked a pattern. Six months later nobody will remember.

Patterns are tools and not religion. Use what fits your problem. Leave what does not.

Final words

Frontend architecture is not about using fancy names in interviews. It is about making sure that six months from now you and your team can still change the code without fear.

If you understood the core ideas from this article you are already ahead of many developers. Next time you start a project just ask those three questions. Where does my data live? How does it move? How is my code divided? The right pattern will become clear on its own.

If this article helped you then please give it a clap and share it with your developer friends. And tell me in the comments which pattern you use in your current project. I read every comment.

Happy coding!

Before you go

If you are preparing for interviews or you just want to grow as an engineer then here are two platforms I built. I think both of them will help you.

1. AlgorithmXlr8.io

Press enter or click to view image in full size

AlgorithmXlr8.io is a one stop platform to master software engineering interviews in a systematic way.

  • System Design. High Level Design and Low Level Design built for interview context. There are also real scenario based system design questions that come up in almost eery interview.
  • DSA the systematic way. Pattern based learning like sliding window and two pointers. You also get the DSA-31 sheet for interviews and the CF-18 sheet for competitive programming.
  • Beyond DSA. Fix real production bugs in a codebase you have never seen. Live incidents and multi file repository problems right in your browser in Java, Python, Node.js or C++.

2. CodEyes.in

Press enter or click to view image in full size

CodEyes.in puts all your coding stats in one profile. Connect LeetCode, Codeforces, CodeChef, GitHub, Medium and Substack and see your full progress in one place.

  • One shareable profile link that you can put on your resume
  • Total questions solved, contests, active days and streaks across all platforms
  • A contest tracker so you never miss a contest
  • A leaderboard to compare yourself with other coders

Do check out both and tell me in the comments what you think. Your feedback really helps me make them better.

Written with love by Himanshu Singour

--

--

Himanshu Singour
Himanshu Singour

@AlgorithmXlr8.io | Simplify System Design and Data Structure & Algorithms.