We’ve all done it: browsing the latest summer collection during a snowstorm, eyeing a heavy windbreaker staged in a bright, sunny studio while you’re in the middle of a torrential downpour… For global brands, this creates a contextual disconnect, when the environment in the image doesn’t match the user’s reality, it feels generic and conversion rates can drop.
Traditionally, this means a lot of manual work for designers to localize hundreds of assets.
In this guide, you’ll build the “Global-Local” Engine. By combining Next.js 16, Contentful, and Cloudinary’s Generative AI, you’ll create a self-adapting storefront that:
- Detects user context (location and weather) at the Edge.
- Orchestrates visual strategies via Contentful.
- Generates personalized backgrounds in real time using Cloudinary AI.
The result is a storefront that feels like it was designed for the moment, no matter where the customer is in the world.
- Live Demo: seasonal-nomad.vercel.app
- Source Code: github.com/musebe/seasonal-nomad
Your engine moves away from static assets toward a composable commerce stack. Instead of serving a prerendered image, you’ll serve a dynamic instruction set.
-
Next.js 16. You’ll use the new
proxy.tsmodel to intercept requests at the network boundary. It detects the user’s environment and fetches real-time weather data before the HTML is even rendered, eliminating the “flash” of unstyled content. -
Contentful. This acts as your rules of engagement. Marketing teams can map weather codes (e.g.,
Rain,Snow) to descriptive AI prompts in the CMS, allowing them to update the site’s visual strategy without touching a single line of code. -
Cloudinary. Using the latest Cloudinary Contentful integration, the app pulls a master asset and applies the
replaceBackgroundtransformation. Generative AI renders a high-fidelity environment matching the user’s atmosphere while optimizing delivery viaf_autoandq_auto.
Before writing any code, structure your content using Contentful to ensure your Next.js frontend can query the right data at the right time.
-
Define the content models. First, create two distinct models:Product, which includes fields for Name, Slug, and a JSON object for the Cloudinary media, and Weather Prompt (or Weather Settings), which uses a “Condition” field (e.g., “Rain”) and an
aiPromptfield. - Populate the strategy. Create entries for common weather states: rain, clouds, and clear. For the “rain” entry, make sure to provide a descriptive prompt: “a moody rainy London street with puddles and soft street lamp reflections”.
- Generate API keys. To allow the Next.js app to talk to your structured content, navigate to Settings > API keys to generate a Space ID and a Content Delivery API (CDA) Access Token.
With the structure in place, you’ll integrate Cloudinary directly into the Contentful workflow, so you can manage your assets without leaving the CMS.
-
Configure the Cloudinary App. In Contentful, navigate to Apps > Marketplace to install the Cloudinary Sidebar App. Configure it with the cloud name
demo-article-projects, set the Starting folder toseasonal-nomadto isolate your assets, and enablef_autoandq_autofor global optimization. -
Link the JSON field. In the Product content model, set the Appearance of the
image(JSON Object) field to use the Cloudinary App. This ensures that when you select an image, Contentful stores a rich JSON object containing thepublic_idand metadata instead of a simple URL string. -
Upload the master asset. Upload the “Apex Nomad” jacket to the
seasonal-nomadfolder via the Contentful interface. The original image is a plain studio shot, so you can use generative AI to replace the background.
With your data sources (Contentful and Cloudinary) ready, you scaffolded the frontend using Next.js 16 and Turbopack. This version of Next.js is optimized for speed through its new “proxy” architecture and improved caching.
Start by scaffolding a modern Next.js 16 application. This command automatically sets up TypeScript, Tailwind CSS, and ESLint, giving us a production-ready starting point.
npx create-next-app@latest seasonal-nomad --typescript --tailwind --eslint
cd seasonal-nomad
Code language: CSS (css)
Once inside, install your core UI and media dependencies. Use shadcn/ui for accessible components and next-cloudinary to handle your AI transformations.
npm install next-cloudinary lucide-react
npx shadcn@latest init
npx shadcn@latest add select skeleton
Code language: CSS (css)
To securely connect your Next.js application to Contentful and Cloudinary, you’ll need to store your API credentials in an environment variable file. In the root directory of your project, create a new file named .env.local and add your keys:
# Contentful Handshake
CONTENTFUL_SPACE_ID="your_space_id"
CONTENTFUL_ACCESS_TOKEN="your_cda_token"
# Cloudinary Creative Engine
NEXT_PUBLIC_CLOUDINARY_CLOUD_NAME="demo-article-projects"
# Weather API
OPENWEATHER_API_KEY="your_openweather_key"
Code language: PHP (php)
We organized the project into a modular architecture to keep the fetching logic separated from the UI components.
To bridge the code with our CMS, first set up a dedicated client. This allows you to perform parallel data fetching, a key feature of Next.js 16 that lets you request the product and the weather prompt simultaneously to reduce load times.
Install the Contentful delivery SDK, which provides the necessary methods to query your structured content.
npm install contentful
Next, create a utility file, lib/contentful.ts, that will serve as your single point of contact for CMS data. By using the Space ID and Access Token from .env.local, you’ll establish a secure connection that only pulls “Market-Approved” assets based on the unique slugs defined in Step 1.
In Next.js 16, performance is king. Instead of fetching the product and then waiting to fetch the weather prompt (a “waterfall”), you’ll trigger them at the same time using Promise.all. This ensures the “Global-Local” engine has all the data it needs to start rendering the moment the user hits the page.
lib/contentful.ts:
import { createClient } from 'contentful';
const client = createClient({
space: process.env.CONTENTFUL_SPACE_ID!,
accessToken: process.env.CONTENTFUL_ACCESS_TOKEN!,
});
/**
* Fetches a single product by its unique slug
*/
export async function getProduct(slug: string) {
const entries = await client.getEntries({
content_type: 'product',
'fields.slug': slug
});
return entries.items[0];
}
/**
* Maps the weather condition to a specific AI prompt
*/
export async function getWeatherPrompt(condition: string) {
const entries = await client.getEntries({
content_type: 'weatherSettings',
'fields.condition': condition,
});
return entries.items[0];
}
Code language: JavaScript (javascript)
By structuring your client this way, you’ll create a reusable “fetcher” that can handle any product or weather state without further code changes.
View the full
lib/contentful.tson GitHub.
Next.js 16 is a more powerful proxy.ts model compared to traditional middleware. It acts as a high-performance interceptor that detects the user’s environment at the network boundary, ensuring that personalization happens before the page even begins to render.
The proxy serves as the “sensing layer” of our engine, and performs three critical tasks in a single pass:
-
It captures the user’s city via their IP address or a URL override from our
ContextSwitcherdropdown. - It communicates with the OpenWeatherMap API to retrieve current conditions (e.g., “rain”, “snow”, “clear”).
- Instead of passing raw data, the proxy “stamps” the request with custom headers like
x-weather-conditionandx-weather-temp.
By doing this at the edge, you’ll eliminate instances of when a page loads as one thing and then jumps to another once the browser’s JavaScript kicks in.
The core of this logic is the header, which provides the context that your server components will read to decide which AI prompt to fetch from Contentful.
// proxy.ts engine snippet
const weatherUrl = `https://api.openweathermap.org/data/2.5/weather?q=${city}&units=metric&appid=${process.env.OPENWEATHER_API_KEY}`;
const weatherRes = await fetch(weatherUrl);
const data = await weatherRes.json();
const condition = data.weather?.[0]?.main || 'Clear';
// Injecting the context into the request flow
response.headers.set('x-weather-condition', condition);
response.headers.set('x-current-city', city);
response.headers.set('x-weather-temp', Math.round(data.main?.temp || 0).toString());
Code language: JavaScript (javascript)
This setup ensures that by the time your main page.tsx starts executing, it already knows what the weather is like at the user’s location.
View the full proxy.ts on GitHub.
Because the next-cloudinary component uses React state to manage complex AI background swaps, you must establish a client boundary. This separation ensures that the interactive creative engine of Cloudinary remains isolated from the data-heavy server components, keeping the initial page load lightweight.
The HeroMedia component is the visual heart of the storefront. It takes the context delivered by the proxy and the product data from Contentful to perform the final AI transformation.
- “use client” directive. You’ll mark this component to execute in the browser, allowing the Cloudinary library to handle the dynamic generation of the background based on the user’s specific environment.
-
The Generative AI trigger. Utilize the
replaceBackgroundprop within theCldImagecomponent. This prop sends theaiPrompt(fetched from Contentful) to Cloudinary’s generative engine, which then swaps the original studio background for a contextually relevant one. -
Performance guardians. Resolve potential layout shifts and performance bottlenecks by implementing the
sizesprop. This allows the browser to request the most efficient image dimensions for the current device, while theprioritytag ensures the hero image is the first asset to load.
The core of the UI logic is the integration of the CldImage component. By passing the dynamic prompt directly into the transformation engine, you’ll achieve real-time personalization without manual asset management.
// HeroMedia.tsx engine snippet
<CldImage
src={imagePublicId}
fill
/* High-performance responsive media delivery */
sizes="(max-width: 768px) 100vw, 50vw"
alt={productName}
/* The Generative AI Trigger */
replaceBackground={aiPrompt}
className="object-cover transition-transform duration-700 group-hover:scale-105"
priority
/>
Code language: HTML, XML (xml)
View full HeroMedia.tsx on GitHub.
The final step is the assembly of Contentful and Cloudinary within the main app/page.tsx. This server component serves as the central hub, coordinating data from Contentful and the real-time context provided by our proxy.ts.
-
Parallel data fetching. You’ll utilize
Promise.allto fetch the product details and the weather-based AI prompt simultaneously. This avoids a “waterfall” effect, ensuring that the visual strategy and product data are ready at the same time. -
Reading context from headers. The page extracts the
x-weather-condition,x-weather-temp, andx-current-cityheaders. These values determine exactly which AI background Cloudinary will generate and which statistics will appear in the UI. -
Loading states with suspense. Wrap the
HeroMediacomponent in a React suspense boundary. This allows the textual content (like the product name and weather stats) to load instantly while the generative AI background replacement is being processed in the background.
The page logic bridges the gap between the raw environmental data from the proxy and the marketing strategy stored in Contentful.
TypeScript
// app/page.tsx engine snippet
const headerList = await headers();
const currentCondition = headerList.get('x-weather-condition') || 'Clear';
const temp = headerList.get('x-weather-temp') || '--';
// Parallel fetch from the Contentful "Brain"
const [product, weatherEntry] = await Promise.all([
getProduct('apex-nomad-windbreaker'),
getWeatherPrompt(currentCondition),
]);
// Extracting the AI prompt for Cloudinary
const aiPrompt = (weatherEntry?.fields.aiPrompt as string) || 'a high-tech showroom';
Code language: JavaScript (javascript)
View full app/page.tsx on GitHub.
When a user selects a city like Nairobi, the proxy detects “clouds,” the page will fetch the “cloudy” prompt from Contentful, and Cloudinary will render the jacket in a soft, overcast environment.
In a high-fidelity storefront, performance is just as critical as the visuals. To ensure the user’s experience feels premium and fast, you’ll implement two critical optimization layers: UI stability and build integrity.
Because Cloudinary’s replaceBackground generates a new image dynamically, there’s a brief processing window. Without a strategy, the page would “jump” as the image pops in, creating a jarring user experience. You’ll solve this by using shadcn/ui Skeletons wrapped in a React Suspense boundary. This gives the user immediate visual feedback and maintains layout stability while the generative AI works in the background.
One of the most common hurdles in Next.js 16 is static generation bailout. Since our ContextSwitcher uses the useSearchParams() hook to detect city changes, Next.js tries to opt the entire page into dynamic rendering.
The Fix: Suspense Boundary. Since you wrapped the search-dependent logic in a focused
Suspenseboundary within theNavbar.tsx, this isolated the dynamic behavior, allowing the rest of the site’s layout to remain statically optimized and lightning-fast on Vercel.
Your implementation uses a dedicated skeleton that mimics the exact aspect ratio of your product media, ensuring zero layout shift.
// HeroSkeleton.tsx engine snippet
export default function HeroSkeleton() {
return (
<div className="flex items-center justify-center h-full w-full bg-slate-50 p-12">
{/* Aspect-ratio matching prevents layout shift */}
<Skeleton className="w-full max-w-md aspect-[3/4] bg-slate-200" />
</div>
)
}
Code language: JavaScript (javascript)
View the full
HeroSkeleton.tsxon GitHub.
By combining Contentful, Cloudinary, and the “Edge” orchestration of Next.js 16, you’ve built a storefront that scales globally without taxing design resources. Instead of manual variations, generative AI handles the heavy lifting, ensuring every customer sees your products in an environment that actually matches the weather outside.
- Final Live Demo: seasonal-nomad.vercel.app
- Full Source Code: github.com/musebe/seasonal-nomad