The Builder Pattern is a creational design pattern used to construct complex objects step by step. It separates the construction of a complex object from its representation, so the same construction process can create different representations.
Whether you're crafting the perfect burger ๐ or dynamically composing UI layouts from CMS data, the Builder Pattern shines in scenarios where plain object creation logic becomes messy.
๐ง When to Use Builder Pattern
- When an object requires multiple steps to be created
- When an object has many optional parameters or configurations
- When creating complex objects with nested fields or layout trees
๐งฑ Structure of the Builder Pattern
- Product โ The complex object to be built
- Builder Interface โ Defines the steps for building the product
- ConcreteBuilder โ Implements the builder interface
- Director (optional) โ Orchestrates the building process
- Client โ Uses the builder to construct the object
๐ Real-World Analogy: Building a Burger
Letโs start with a simple example.
1. Product
class Burger:
def __init__(self):
self.bun = None
self.patty = None
self.cheese = False
self.lettuce = False
self.tomato = False
def __str__(self):
return f"Burger with {self.bun} bun, {self.patty} patty" + \
(", cheese" if self.cheese else "") + \
(", lettuce" if self.lettuce else "") + \
(", tomato" if self.tomato else "")
2. Builder
class BurgerBuilder:
def __init__(self):
self.burger = Burger()
def set_bun(self, bun):
self.burger.bun = bun
return self
def set_patty(self, patty):
self.burger.patty = patty
return self
def add_cheese(self):
self.burger.cheese = True
return self
def add_lettuce(self):
self.burger.lettuce = True
return self
def add_tomato(self):
self.burger.tomato = True
return self
def build(self):
return self.burger
3. Client Usage
builder = BurgerBuilder()
burger = (
builder.set_bun("Whole Wheat")
.set_patty("Veg")
.add_cheese()
.add_lettuce()
.build()
)
print(burger)
๐จ Output:
Burger with Whole Wheat bun, Veg patty, cheese, lettuce
๐ก Benefits of the Builder Pattern
- Cleaner and more maintainable code
- Easy to add/remove steps in building process
- Fluent API through method chaining
- Useful for testing and configurability
๐ง Advanced Use Case: Building Nested UI Layouts from CMS
Now letโs go beyond burgers and into frontend development.
Imagine you're using a CMS like Sanity that gives you a structured array of components โ but some components are layouts like HStack
or VStack
which themselves contain children. In this case, a flat .map()
wonโt work. You need a builder that can recursively build components based on their type.
Hereโs an example of such data:
[
{
"_type": "hstack",
"gap": 16,
"children": [
{ "_type": "image", "src": "a.jpg" },
{ "_type": "card", "title": "Card A", "content": "..." },
{
"_type": "vstack",
"gap": 8,
"children": [
{ "_type": "text", "content": "Inside VStack" },
{ "_type": "button", "label": "Click me", "link": "/click" }
]
}
]
}
]
๐จโ๐ป Step-by-Step Builder in React
1. Leaf Components
export const ImageBlock = ({ src }) => <img src={src} className="rounded" />;
export const TextBlock = ({ content }) => <p>{content}</p>;
export const ButtonBlock = ({ label, link }) => (
<a href={link} className="btn">{label}</a>
);
export const Card = ({ title, content }) => (
<div className="card">
<h3>{title}</h3>
<p>{content}</p>
</div>
);
2. Layout Components
export const HStack = ({ gap, children }) => (
<div style={{ display: 'flex', gap }}>{children}</div>
);
export const VStack = ({ gap, children }) => (
<div style={{ display: 'flex', flexDirection: 'column', gap }}>{children}</div>
);
3. ๐ง Recursive Section Builder
// SectionBuilder.tsx
import { HStack, VStack } from "./Layouts";
import { ImageBlock, TextBlock, ButtonBlock, Card } from "./Blocks";
const builders = {
image: (data) => <ImageBlock {...data} />,
text: (data) => <TextBlock {...data} />,
button: (data) => <ButtonBlock {...data} />,
card: (data) => <Card {...data} />,
hstack: (data, builder) => (
<HStack gap={data.gap}>
{data.children.map((child, i) => (
<div key={i}>{builder.build(child)}</div>
))}
</HStack>
),
vstack: (data, builder) => (
<VStack gap={data.gap}>
{data.children.map((child, i) => (
<div key={i}>{builder.build(child)}</div>
))}
</VStack>
),
};
export const SectionBuilder = {
build(data) {
const builder = builders[data._type];
if (!builder) return null;
return builder(data, SectionBuilder); // Recursive call
},
};
4. Rendering a Page
export const Page = ({ sections }) => {
return (
<main>
{sections.map((section, i) => (
<div key={i}>{SectionBuilder.build(section)}</div>
))}
</main>
);
};
โ Why This is a Builder Pattern
This is a Builder Pattern because:
- It separates the construction of a complex, recursive UI tree from its representation.
- Each block knows only how to render itself, not its siblings or parents.
- The
SectionBuilder
is the Director, orchestrating rendering through specific builders. - You can add new types (like grid, tab, carousel) without changing existing builder logic.
๐ฅ๏ธ Where is the Builder Pattern Used in Backend?
The Builder Pattern isn't just for frontend UI โ it's heavily used in backend development too. Some common places include:
- Query Builders: ORM tools like Prisma, Sequelize, and Hibernate let you construct complex database queries step-by-step.
-
HTTP Request Builders: Libraries like
OkHttp
andRestTemplateBuilder
help construct configurable requests. - Email Builders: Services like SendGrid or Nodemailer use builders to construct emails with optional subject, body, attachments, etc.
- Config Builders: Used in SDKs and APIs to initialize configuration objects with optional parameters.
- Document Builders: For generating structured JSON, XML, or even PDFs in steps.
These patterns improve readability, allow optional parameters, and separate object construction logic from representation โ the core goals of the builder pattern.
๐ก Extensions and Improvements
- Add
React.lazy()
andSuspense
for large component blocks - Use CMS-driven styles via Tailwind/CSS-in-JS props
- Add error boundaries for invalid CMS structures
- Serialize/deserialize preview versions for internal tooling
โจ Final Thoughts
Builder pattern is often taught with backend examples, but it's hugely powerful in frontend development, especially when dealing with dynamic, nested UI construction from content management systems.
Happy hacking! ๐
Top comments (0)