@bildit-platform/nextjs
@bildit-platform/nextjs
@bildit-platform/nextjs a library for integrating Bildit CMS with Next.js applications.
Installation
npm install @bildit-platform/nextjs
or
yarn add @bildit-platform/nextjs
Usage
Important: If you pass extraDependenciesConfig, The component that renders BilditProvider must be a Client Component. Ensure you include "use client" at the top of your file (or use a wrapper marked with "use client").
"use client"; // <-- required so extraDependenciesConfig works
import { BilditProvider, SlotPlaceholder } from '@bildit-platform/nextjs';
export default function MyPage({ banners }) {
return (
<BilditProvider banners={banners}>
<main>
<SlotPlaceholder slotId="hero-1" />
<SlotPlaceholder slotId="features-1" />
</main>
</BilditProvider>
);
}
API Reference
Components
BilditProvider
A context provider that processes and maps banners from Bildit CMS to their corresponding slots.
Note: Next.js doesn’t allow passing modules or functions from a Server Component into a Client Component. If you supply extraDependenciesConfig to BilditProvider, make sure the file starts with "use client" so it renders as a Client Component (or use a wrapper marked with "use client")
Props
| Prop | Type | Default | Required | Description |
|---|---|---|---|---|
| children | React.ReactNode |
– | Yes | The content to be rendered within the BilditProvider context. |
| banners | BannerType[] |
– | Yes | An array of banners fetched from Bildit CMS used to generate dynamic slots. |
| extraDependenciesConfig | Record<string, ExtraDependencyConfig> |
{} | No | A map of extra dependencies config, it should include module and the globalName |
| variantFilter | (variant: Variant, banner: BannerType) => boolean |
- | No | Experimental: Client-side variant filtering function. May cause flickering. |
Usage Example
import { BilditProvider } from '@bildit-platform/nextjs';
export default function App({ banners }) {
return (
<BilditProvider banners={banners}>
<YourAppComponents />
</BilditProvider>
);
}
With external modules
'use client';
import { BilditProvider } from '@bildit-platform/nextjs';
import * as Axios from 'axios';
import * as _ from 'lodash';
export default function App({ banners }) {
return (
<BilditProvider
banners={banners}
extraDependenciesConfig={{
axios: {module: Axios, globalName: 'axios'},
lodash: {module: _, globalName: '_'},
}}
>
<YourAppComponents />
</BilditProvider>
);
}
With client-side filtering (experimental)
'use client';
import { BilditProvider } from '@bildit-platform/nextjs';
export default function App({ banners }) {
return (
<BilditProvider
banners={banners}
variantFilter={(variant, banner) => {
// Filter by customer group
if (variant.customerGroups && variant.customerGroups.length > 0) {
return variant.customerGroups.includes('elite');
}
// Show variants with no customer groups defined
return true;
}}
>
<YourAppComponents />
</BilditProvider>
);
}
SlotPlaceholder
A component that renders dynamic content in a designated slot based on the provided slot identifier.
| Prop | Type | Default | Description |
|---|---|---|---|
| slotId | string |
– | Unique identifier for the content slot to be rendered dynamically. |
| className | string |
– | (Optional) Additional CSS classes to apply to the container element. |
| fallback | React.ReactNode |
– | (Optional) Fallback UI to render when no components are available. |
| forceFallback | boolean |
false |
Adds data-bildit-force-fallback="true" to the wrapper so admin tooling can skip the slot. |
Usage Example
import { SlotPlaceholder } from '@bildit-platform/nextjs';
import DefaultLogo from './DefaultLogo';
export default function Page() {
return (
<div>
<SlotPlaceholder slotId="hero-1" className="custom-class" />
<SlotPlaceholder
slotId="logo"
fallback={<DefaultLogo />}
forceFallback
/>
</div>
);
}
Hooks
useSlotComponent
A custom hook that retrieves a dynamic component mapped to a given slot identifier from the context.
Signature
const components = useSlotComponents(slotId: string): React.ReactNode[];
Parameters - slotId (string): The unique identifier for the desired content slot.
Returns - An array of React.ReactNode representing the dynamic components associated with the specified slot, or null if no components are found.
Usage
import { useSlotComponents } from '@bildit-platform/nextjs';
export default function CustomSlot({ slotId }) {
const components = useSlotComponents(slotId);
return <div>{components.map((component, index) => <div key={index}>{component ?? 'No content available'}</div>)}</div>;
}
Advanced Usage
Preview Date Feature
The preview date feature allows you to share website previews at a specific date/time without requiring any code changes in your application. This is useful for previewing scheduled content before it goes live.
How It Works
- URL Parameter Detection: Add
?bildit_preview_date=<ISO_DATE>to any URL - Automatic Middleware Processing: The middleware detects the parameter and converts it to an epoch timestamp
- Header Forwarding: The preview date is forwarded to server components via headers
- API Integration: The date is automatically sent to the Bildit API to fetch scheduled content
Setup
Option 1: Using the Pre-built Middleware (Recommended)
The easiest way to add preview date support is to use the pre-built createBilditMiddleware function:
// middleware.ts
import { createBilditMiddleware } from '@bildit-platform/nextjs';
export const middleware = createBilditMiddleware();
export const config = {
matcher: [
'/((?!api|_next/static|_next/image|favicon.ico).*)'
]
};
With optional callback for custom logging or analytics:
// middleware.ts
import { createBilditMiddleware } from '@bildit-platform/nextjs';
export const middleware = createBilditMiddleware({
onPreviewDate: (previewDate, request) => {
console.log('[Preview] Date detected:', previewDate);
// Add your custom logic here (analytics, logging, etc.)
}
});
export const config = {
matcher: [
'/((?!api|_next/static|_next/image|favicon.ico).*)'
]
};
Option 2: Enhancing Existing Middleware
If you already have middleware, add BILDIT preview date support by wrapping it:
// middleware.ts
import { enhanceMiddlewareWithBildit } from '@bildit-platform/nextjs';
import { myExistingMiddleware } from './my-middleware';
export const middleware = enhanceMiddlewareWithBildit(myExistingMiddleware);
export const config = {
matcher: [
'/((?!api|_next/static|_next/image|favicon.ico).*)'
]
};
This will automatically add preview date detection while preserving your existing middleware logic.
Server Component Usage
Retrieve the preview date in your server components:
// app/layout.tsx
import { getPreviewDateFromHeaders } from '@bildit-platform/nextjs';
import { RemoteConnector } from '@bildit-platform/nextjs-api';
import { headers } from 'next/headers';
export const dynamic = 'force-dynamic';
const bilditConnector = new RemoteConnector({
key: process.env.BILDIT_API_KEY || '',
baseURL: process.env.BILDIT_API_URL || ''
});
async function getInitialData() {
const headersList = await headers();
const previewDate = getPreviewDateFromHeaders(headersList);
const result = await bilditConnector.getWebBanners({
source: 'live',
location: '/',
date: previewDate, // Pass the preview date to the API
mode: 'csr',
tomorrow: true
});
return result.data;
}
Example URLs
# Preview content scheduled for February 15, 2026
https://example.com/?bildit_preview_date=2026-02-15T00:00:00.000Z
# Preview content at a specific time
https://example.com/products?bildit_preview_date=2026-02-15T14:30:00.000Z
# Works on any page
https://example.com/blog/post-1?bildit_preview_date=2026-03-01T00:00:00.000Z
API Reference
getPreviewDateFromUrl(url: string): number | undefined
Extracts and converts the preview date from a URL to an epoch timestamp.
Parameters:
url(string): The full URL to check for the preview date parameter
Returns:
number: Epoch timestamp in milliseconds, orundefinedif not found or invalid
Example:
const previewDate = getPreviewDateFromUrl(request.url);
// Returns: 1739577600000 (epoch timestamp)
getPreviewDateFromHeaders(headers: Headers): number | undefined
Extracts and converts the preview date from request headers to an epoch timestamp.
Parameters:
headers(Headers): Next.js request headers object
Returns:
number: Epoch timestamp in milliseconds, orundefinedif not found or invalid
Example:
import { headers } from 'next/headers';
const headersList = await headers();
const previewDate = getPreviewDateFromHeaders(headersList);
// Returns: 1739577600000 (epoch timestamp)
convertToEpoch(dateInput: string | number): number | undefined
Converts an ISO date string or epoch timestamp to epoch milliseconds.
Parameters:
dateInput(string | number): ISO date string (e.g., "2026-02-15T0000.000Z"), epoch string (e.g., "1739577600000"), or epoch number
Returns:
number: Epoch timestamp in milliseconds, orundefinedif invalid
Example:
const epoch1 = convertToEpoch('2026-02-15T00:00:00.000Z');
// Returns: 1739577600000
const epoch2 = convertToEpoch('1739577600000');
// Returns: 1739577600000
const epoch3 = convertToEpoch(1739577600000);
// Returns: 1739577600000
createPreviewHeaders(previewDate?: string | number): Record<string, string>
Creates headers object with preview date for API requests.
Parameters:
previewDate(string | number): ISO date string or epoch timestamp
Returns:
- Object with
X-Bildit-Preview-Dateheader set to epoch timestamp string, or empty object if invalid
Example:
const headers = createPreviewHeaders('2026-02-15T00:00:00.000Z');
// Returns: { 'X-Bildit-Preview-Date': '1739577600000' }
Notes:
- Preview dates are converted to epoch timestamps (milliseconds) automatically
- The middleware handles conversion from ISO dates to epoch format
- The Bildit API expects epoch timestamps for date filtering
- Preview dates work across all pages without code changes
Client-Side Filtering
Warning: Client-side filtering is experimental and may cause content flickering as banners are filtered after initial render. For better user experience, consider filtering banners on the server-side before passing them to BilditProvider.
The variantFilter prop allows you to filter banner variants on the client-side based on any criteria:
// Customer group filtering
<BilditProvider
banners={banners}
variantFilter={(variant) =>
!variant.customerGroups ||
variant.customerGroups.includes('elite')
}
/>
// Location-based filtering
<BilditProvider
banners={banners}
variantFilter={(variant) =>
variant.locations.includes('/') ||
variant.locations.includes('/home')
}
/>
// Category filtering
<BilditProvider
banners={banners}
variantFilter={(variant) =>
variant.categories.some(cat => ['promo', 'sale'].includes(cat))
}
/>
// Complex multi-criteria filtering
<BilditProvider
banners={banners}
variantFilter={(variant, banner) => {
const hasValidCustomerGroup = !variant.customerGroups ||
variant.customerGroups.includes(userCustomerGroup);
const hasValidLocation = variant.locations.includes(userLocation);
const isActivePromo = variant.categories.includes('active');
return hasValidCustomerGroup && hasValidLocation && isActivePromo;
}}
/>