
10 UX Design Patterns Every Developer Should Know (And When to Skip Them)

Most developers first encounter design patterns the same way: they google a problem, find an intimidating class diagram on Refactoring.Guru, and either copy it wholesale or close the tab forever. Neither is the right move. UX Design Patterns and their software engineering counterparts are worth understanding on your own terms, because knowing when not to use them is just as valuable as knowing what they do.
This guide walks through 10 real patterns, drawn from a fantastic breakdown by Fireship, covering what each one actually does, where it shows up in codebases you already use, and where it quietly adds complexity you did not ask for.
The Gang of Four and Why Patterns Exist
One of the most influential books in programming history is Design Patterns, written by four C++ engineers now known as the Gang of Four. Published in 1994, it catalogs 23 approaches to address recurring design problems that developers face across essentially every codebase.
The Gang of Four grouped those approaches into three buckets. Creational patterns cover how objects are created. Structural patterns cover how objects relate to each other. Behavioral patterns cover how objects communicate. That categorization has held up surprisingly well, even though the book predates JavaScript as a language by a year.
Here is the thing about design patterns that nobody says clearly enough: they are not algorithms. You cannot copy them off Stack Overflow and drop them in. You have to understand the problem they solve first, then decide whether that problem is actually yours. Used wrong, a pattern just adds boilerplate without adding clarity, and you end up with code that is harder to maintain than what you started with.
There is also a well-worn observation that goes something like: junior developers write simple code, senior developers write elaborate pattern-heavy architectures, and principal engineers circle back to simple code. That arc is real. The Sistine Chapel is stunning, but nobody wants to maintain it for a CRUD app.
Creational Patterns: How Objects Get Built
Singleton
The Singleton pattern restricts a class to a single instance. In TypeScript, the classic implementation uses a private constructor and a static getInstance() method:
class Settings {
private static instance: Settings;
private constructor() {}
static getInstance(): Settings {
if (!Settings.instance) {
Settings.instance = new Settings();
}
return Settings.instance;
}
}Every call to getInstance() returns the same object. That is the whole idea.
The problem is that in JavaScript, you mostly get this for free. Object literals are passed by reference. Global objects already share state. The pattern, in JavaScript specifically, is often pure overhead. Before reaching for Singleton, check whether a plain module-level export solves the problem with zero boilerplate.
Prototype
If you have done object-oriented programming, you know inheritance. The Prototype pattern is an alternative that skips class hierarchies entirely and clones from an existing object.
const zombie = {
eatBrains() {
return 'yum';
}
};
const zombie2 = Object.create(zombie, {
name: { value: 'Ash' }
});
console.log(zombie2.name); // 'Ash'
console.log(zombie2.eatBrains()); // 'yum'JavaScript supports prototypal inheritance natively, so this is less a pattern you apply and more a feature of the language you use correctly. Logging zombie2 shows only name, but calling eatBrains() still works because JavaScript walks the prototype chain upward until it finds the method.
Builder
The Builder pattern constructs an object step by step instead of dumping everything into a constructor at once. The key mechanic is method chaining: each method returns this, so you can chain calls fluently.
class HotDog {
constructor(bun) {
this.bun = bun;
}
addKetchup() { this.ketchup = true; return this; }
addMustard() { this.mustard = true; return this; }
addOnions() { this.onions = true; return this; }
}
const lunch = new HotDog('wheat')
.addKetchup()
.addMustard();jQuery made method chaining famous. It is genuinely readable for configuration-heavy object construction. That said, it has fallen somewhat out of style in favor of plain configuration objects, especially in TypeScript where spreading options is cleaner and easier to type.
Factory
A Factory function or method creates objects without exposing the new keyword directly to the caller. The use case that makes this obvious is cross-platform UI.
function buttonFactory(os) {
if (os === 'ios') return new IOSButton();
if (os === 'android') return new AndroidButton();
}Without the factory, every component that needs a button has to repeat that conditional check. The factory isolates that logic in one place. Small change, but it makes a real difference when the condition becomes more complex or when you add a third platform.

Structural Patterns: Facades, Proxies, and Simplification
Facade
A facade is a simplified API that sits in front of something more complicated. Facade API simplification is the whole point: hide the messy low-level details, expose only what callers need.
class House {
constructor() {
this.plumbing = new PlumbingSystem();
this.electrical = new ElectricalSystem();
}
turnOn() {
this.plumbing.setPressure(500);
this.electrical.setVoltage(120);
this.electrical.turnOn();
this.plumbing.turnOn();
}
}The person living in the house calls turnOn(). They do not care about voltage or pressure. Almost every JavaScript library is a facade in some form. jQuery is the textbook example: it wraps dozens of browser inconsistencies behind a consistent $() interface. Express.js does the same for Node's HTTP module.
Proxy
A proxy replaces a target object with a substitute that intercepts operations on it. The most instructive real-world example is Vue.js's reactivity system.
Vue needs to know when data changes so it can update the UI. Its solution is proxy object interception: it wraps your data object in a Proxy and intercepts get and set calls.
const handler = {
get(target, key) {
return Reflect.get(target, key);
},
set(target, key, value) {
console.log('Data changed, re-render the UI');
return Reflect.set(target, key, value);
}
};
const state = new Proxy({ count: 0 }, handler);
state.count = 1; // logs 'Data changed, re-render the UI'The Reflect API updates the underlying object; the proxy handles side effects. You interact with state exactly as you would with the original object. Vue.js's reactivity system is built on exactly this mechanic. Proxies are also useful when an object is expensive to duplicate in memory and you want a lightweight stand-in that defers the real work.
UX Design Patterns You Can Actually Use
Software patterns and UX patterns come from different traditions but share the same core motivation: give developers reusable answers to problems that have been solved before.
On the interface side, a curated patterns library (think Nielsen Norman Group, UI-Patterns.com, or your own internal design system) functions the same way the Gang of Four book does for code: it gives teams a shared vocabulary for solving interaction problems. "Use a progressive disclosure accordion here" is faster than re-explaining information hierarchy from scratch every time.
A few patterns that show up in nearly every product:
Progressive disclosure hides complexity until the user needs it. Accordions, "read more" links, and multi-step forms all do this. The goal is to reduce cognitive load at each decision point without hiding information permanently.
Interaction feedback loops tell the user something happened. A button that does nothing visible for 800ms feels broken. Loading spinners, inline success messages, and shake animations on failed validation all close the loop between action and confirmation.
Skeleton screens (a form of optimistic UI) are a variant on feedback. Rather than a blank space or a spinner, you show the shape of the content before it loads. This reduces perceived wait time more than a spinner does.
For UX design patterns for web and mobile apps, the separation between "web" and "mobile" has narrowed considerably. Bottom navigation, swipe gestures, and tap targets sized to at least 44x44px started on mobile and now appear routinely in responsive web products. The patterns share an underlying principle: match what the user's thumb or cursor expects, reduce decision points, and make the path to the next action obvious.
Design system consistency is what makes these patterns scale. One team using a "stepper" for a checkout flow and another using a wizard with different labels creates friction that users feel even if they cannot name it. A shared component library enforces the pattern so developers stop reinventing it.
👉 Also read: What Is UI Design and Why It Matters for Developers
Behavioral Patterns: How Objects Talk to Each Other
Iterator
The iterator pattern is how you traverse a collection. JavaScript already exposes this through for...of loops, arrays, and generators. But there is no built-in range function in JavaScript, which is a minor annoyance that the iterator pattern can actually fix.
function range(start, end, step = 1) {
return {
[Symbol.iterator]() {
return this;
},
next() {
if (start < end) {
const value = start;
start += step;
return { value, done: false };
}
return { value: undefined, done: true };
}
};
}
for (const n of range(0, 10, 2)) {
console.log(n); // 0, 2, 4, 6, 8
}Adding [Symbol.iterator] to the object lets it plug directly into for...of. The iterator controls what "next" means. That flexibility is the point.
Observer
The observer pattern is pull versus push. Iterators pull from a collection. The observer pattern pushes data to subscribers. One object broadcasts; many objects listen.
Firebase is a real-world example at scale. When data changes on the server, every client subscribed to that data path gets the update automatically. You did not ask for it; it arrived.
In code, RxJS formalizes this cleanly. Its Subject class tracks subscriptions and notifies all of them when a new value is emitted:
import { Subject } from 'rxjs';
const news = new Subject();
news.subscribe(v => console.log('Subscriber A:', v));
news.subscribe(v => console.log('Subscriber B:', v));
news.next('Breaking news!');
// Subscriber A: Breaking news!
// Subscriber B: Breaking news!Think of the observer as a loop that unfolds over time rather than over a collection. That mental model makes it easier to reason about asynchronous data streams.
Mediator
When multiple objects need to communicate with each other, the naive approach creates a many-to-many dependency mess. The mediator inserts itself between them and becomes the single coordination point.
Air traffic control is the analogy: planes do not talk to other planes, they talk to the tower. The tower handles coordination.
In Express.js, middleware is the mediator between an incoming HTTP request and the outgoing response. Each middleware function intercepts the request, does something (authentication, logging, body parsing), and passes it along. That separation of concerns means each middleware does one job and ignores the rest of the pipeline.
State
The state pattern handles objects that behave differently depending on their current mode. The naive version uses a switch statement. That works until it does not, usually when you hit six or more cases.
class Human {
constructor() {
this.mood = new HappyState(this);
}
think() {
this.mood.think();
}
changeMood(state) {
this.mood = state;
}
}
class HappyState {
think() { console.log('I love everything'); }
}
class SadState {
think() { console.log('Everything is terrible'); }
}This is the pattern behind finite state machines. Libraries like XState push the idea further: they model application state as an explicit graph with defined transitions, which makes behavior predictable and testable in a way that ad-hoc conditionals never are. When you need to manage something like a multi-step form, a media player, or an authentication flow, XState is worth the learning curve.
👉 Also read: Complete Web Design Workflow for 2026

When Patterns Make Things Worse, Not Better
The Singleton in JavaScript. The abstract factory when your app only has two object types. A full observer infrastructure when a callback would have worked. These are patterns applied without a genuine problem to solve.
Every pattern has a cost. More abstraction means more files, more indirection, and more for the next developer to decode. Code maintainability tradeoffs are real: a well-placed interface can make a codebase extensible, but a poorly placed one makes it impenetrable. The Gang of Four book itself acknowledges this. Refactoring.Guru, which documents all 23 patterns with excellent diagrams, is careful to list the downsides alongside the benefits. That balance is the right instinct.
The checklist before adding a pattern:
- Does the problem this pattern solves actually exist in your code right now?
- Would a simpler approach (a function, a module export, a plain object) do the same job?
- Will the next developer reading this code understand why the pattern is here?
If the answer to question one is "not yet," wait. Patterns exist to clean up recurring problems, not to preemptively complicate code that is not yet complicated.
A more useful way to build fluency with patterns is to spot them in libraries you already use: jQuery's method chaining is Builder, Vue.js's reactivity is Proxy, Express.js's middleware is Mediator, RxJS's subjects are Observer. You have been using these patterns the entire time.
Frequently Asked Questions
What is the difference between software design patterns and UX design patterns?
Software design patterns are solutions to recurring structural problems in code: how to create objects, how to connect them, how to manage behavior. UX design patterns solve recurring interaction problems: how to present complex choices, how to signal feedback, how to structure navigation. Both share the same philosophy (reuse proven solutions instead of reinventing them), but they operate at different layers. One lives in the codebase; the other lives in the interface.
Are there good interaction design patterns examples for beginners?
Start with the ones you see every day. Pagination, modal dialogs, breadcrumb navigation, empty states, and inline validation are all interaction design patterns that beginners encounter constantly as users before they encounter them as designers. Nielsen Norman Group has a research-backed library. For a more code-adjacent view, UI-Patterns.com organizes patterns by problem type, which makes it easier to browse by need rather than by name.
What are some bad interaction design examples I should avoid?
The worst ones are usually failures of feedback. A form that submits and shows nothing for three seconds, then silently resets. A button with no hover state. A modal with no obvious close path. Error messages that say "Something went wrong" with no further detail. These are bad not because they violate a rule but because they leave users without information when they need it most. Interaction feedback loops exist specifically to prevent this.
How do I know which design pattern to use?
Match the problem to the category. If you are creating complex objects with many optional configurations, consider Builder or Factory. If you need one instance shared across the app, Singleton (or just a module export). If you need to react to data changes across many components, Observer or a state management tool. The pattern name is not the answer; the category and the problem shape are. A UI design patterns library for developers can help map common interface problems to solutions the same way.
Is learning all 23 Gang of Four patterns necessary?
Honestly, no. The most commonly used ones (Singleton, Factory, Observer, Facade, and Proxy) cover the majority of situations most web developers encounter. Knowing the others makes code review easier because you can recognize what a colleague is attempting. But knowing all 23 and using them everywhere is how you end up with a Sistine Chapel that nobody wants to maintain.
Get CodeTips in your inbox
Free subscription for coding tutorials, best practices, and updates.
More from CodeTips

What is UI Design? The Honest Beginner's Guide to How It Works
Most people who use apps and websites every day have no idea how much thought goes into a single button. The color, the size, the spacing, the way it responds when you tap it that is UI design. Understanding what is UI design and how does it work is the first step toward either appreciating the craft or building a career in it.


Complete Web Design Workflow for 2026
Master the complete web design workflow for 2026 from discovery and wireframing to build, testing, and launch. Step-by-step guide for modern teams.
