
Deux Headless CMS
A hands-on exploration of building, connecting, and deploying a scalable Contentful–Next.js environment, combining design systems thinking with front-end engineering principles.
Deux Machina is a digital platform designed to centralize UX research, web development, and design resources. I set out to create an accessible, self-maintaining system where content editors could easily manage resources without developer input. My goal was to combine engineering precision with a designer’s attention to usability, using modern technologies—Next.js for performance and Contentful CMS for scalability.
1. Context & Objective
“I wanted to learn how to make the invisible architecture as intentional as the visible interface.”

I approached this project with a clear intent: to understand the architectural logic behind scalable design systems — from CMS setup to front-end integration and deployment. Rather than simply building an interface, my goal was to experience the complete product lifecycle as both a designer and engineer.
In practice, this meant architecting a small but fully functional system:
- Headless CMS (Contentful) for content management
- Next.js for static and dynamic rendering
- Vercel for automated build and deployment
My secondary goal was to identify where design and engineering intersect — understanding how data structure, API logic, and UI components influence the maintainability of a system.
.png)
2. Process & Technical Implementation
Step 1 — Fetching Data from Contentful
I began by setting up Contentful’s Content Delivery API through the createClient function.
A key learning here was securing environment variables to avoid exposure — I configured .env.local to store sensitive keys and established the foundation for safe API communication.
// Create a Contentful client and securely connect to the CMS
import { createClient } from "contentful";
export async function getStaticProps() {
const client = createClient({
space: process.env.CONTENTFUL_SPACE_ID, // stored securely
accessToken: process.env.CONTENTFUL_ACCESS_KEY, // environment variable
});
}
🧩 System integrity starts with clean configuration — this setup made every later step predictable and secure.
Step 2 — Building Dynamic Resource Components
To visualize CMS data, I created a ResourceCard component — modular, testable, and reusable. Initially, a missing null check caused render crashes; handling undefined data made the component fault-tolerant
export default function Resources({ resources }) {
return (
<div className="resource-list">
{resources.map(resource => (
<ResourceCard key={resource.sys.id} resource={resource} />
))}
</div>
);
}🧩 Small architectural safeguards — like conditional rendering — scale better than defensive patches later.

Step 3 — Dynamic Routing and Rich Content
Next, I implemented dynamic Static Site Generation (SSG) using getStaticPaths and getStaticProps. The learning curve was steep: forgetting to export getStaticPaths initially caused silent build failures. Fixing this taught me the precise mechanics of how Next.js maps slugs to pre-rendered routes.
export default function ResourceDetails({ resource }) {
const { featuredImage, title, description, tags, resourceUrl } = resource.fields;
return (
<div className="resource-details">
<img src={"https:" + featuredImage.fields.file.url} alt={title} />
<h1>{title}</h1>
<p>{tags.join(", ")}</p>
{documentToReactComponents(description)} {/* rich text renderer */}
<a href={resourceUrl} target="_blank" rel="noopener noreferrer">
View Resource
</a>
</div>
);
}🧩 “Dynamic systems don’t fail — they teach you the boundaries of your assumptions.”

Step 4 — Enabling Incremental Static Regeneration (ISR)
Finally, I enabled ISR by adding a revalidate parameter inside getStaticProps().
This allowed content updates to automatically rebuild without a full redeployment — turning a static site into a living, scalable system.
// Revalidates every 10 seconds to keep content fresh
export async function getStaticProps({ params }) {
const { items } = await client.getEntries({
content_type: "resource",
"fields.slug": params.slug,
});
return {
props: { resource: items[0] },
revalidate: 10, // ✅ Vercel auto-triggers rebuild every 10s
};
}🧩 Automation is the ultimate form of design efficiency — systems that update themselves scale effortlessly.

3. Reflection & Outcomes
“This project taught me that design maturity comes from understanding the systems behind the surface.”
The most valuable outcome of Deux Machina wasn’t just a working prototype — it was the shift in how I think about systems.
Every mistake — from export errors to data structure misalignment — forced me to refine my architectural logic and understand how design decisions ripple through technical infrastructure.
By the end, I had:
- Built a fully functional Contentful–Next.js–Vercel workflow
- Implemented modular and fault-tolerant React components
- Automated updates through incremental static regeneration
- Validated the workflow through a usability test with one participant
Gamifying Ecosia
Short heading goes here
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
Short heading goes here
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
