Integrate Bolt.new with Kentico Xperience using the Delivery API for headless content retrieval and the Management API for content operations. Configure the Content Delivery API in Kentico Xperience, generate API keys in the Kentico admin interface, then fetch content items, structured data, and pages via Next.js API routes. Kentico requires an enterprise license — verify access before building. Use Ghost, Contentful, or Strapi for accessible free-tier headless CMS alternatives.
Build a Headless Kentico Xperience Frontend in Bolt.new
Kentico Xperience (formerly Kentico) is a mature enterprise Digital Experience Platform built on .NET with over 20 years of development and a large installed base among mid-market and enterprise organizations. For development teams working within an organization that uses Kentico as its CMS, building headless frontends that consume Kentico's Content Delivery API is a common requirement. The headless channel in Kentico Xperience provides structured access to any content type — articles, product information, landing pages, team members, events — through a RESTful API that can power any frontend technology.
Kentico Xperience's Content Delivery API delivers JSON responses for content items modeled in the Kentico CMS. Content editors work in the familiar Kentico admin interface to create and manage content, while the API feeds that content to Bolt-built React frontends, mobile apps, or any other consuming application. The API supports filtering by content type, language, URL slugs, and custom fields, enabling flexible content retrieval patterns for different frontend use cases.
For Bolt developers, the important context is licensing: Kentico Xperience requires an enterprise license starting from approximately $36,000 per year for production environments. There is no free developer tier, though Kentico offers trial licenses for evaluation. If you are working within a company that already has a Kentico license and you are building a new frontend for an existing Kentico-managed site, this integration guide provides the patterns for connecting Bolt to your organization's Kentico instance. For new projects without an existing Kentico investment, consider Ghost, Contentful, or Strapi as cost-effective alternatives with comparable headless CMS capabilities.
Integration method
Bolt generates Next.js API routes that call Kentico Xperience's Content Delivery API with API key authentication, keeping credentials server-side. Kentico's Delivery API is REST over HTTPS, so outbound calls for reading content items work in Bolt's WebContainer during development. The Management API (for creating and updating content) also uses REST with API key authentication and works in the WebContainer.
Prerequisites
- A Bolt.new account with a Next.js project
- A Kentico Xperience instance with an active enterprise license (trial license sufficient for development)
- The Content Delivery API configured in Kentico Xperience's Channel Management settings
- A Delivery API key generated in Kentico Xperience's API configuration
- The Kentico environment ID and delivery preview or live API URL for your Kentico instance
Step-by-step guide
Configure Kentico Xperience Headless Channel and Generate API Keys
Configure Kentico Xperience Headless Channel and Generate API Keys
Kentico Xperience's headless content delivery is organized around Channels — logical groupings of content and settings for different delivery endpoints. Before accessing the API, a headless channel must be configured for your Bolt frontend. Log in to Kentico Xperience's admin interface. Navigate to Channel Management in the top navigation. If you are setting up a new headless channel, click 'Create Channel' and select 'Headless channel' as the type. Give it a name that identifies your Bolt frontend (e.g., 'Bolt Frontend' or 'React App'). The channel creates a dedicated API endpoint scoped to the content types and languages you configure for that channel. With the channel created, navigate to its settings to find the Delivery API configuration. Kentico generates a base delivery API URL for your channel in the format: `https://your-project.kontent.ai/` or `https://deliver.kontent.ai/{environment-id}/`. Note your Environment ID — a GUID-format string that identifies your Kentico project environment. For API authentication: Kentico's Delivery API has two access modes. The Preview API (for drafts and unpublished content) requires an API key. The Production API (for published content only) can be configured to not require authentication, or can require an API key for additional security. Generate a Delivery API key from your channel's API Keys section — copy the key immediately after generation as it may not be shown again. For the Management API (content creation and updates), generate a separate Management API key from the API Keys section. Management API keys have broader permissions and must remain strictly server-side. Add to your Bolt .env file: `KENTICO_ENVIRONMENT_ID=your-guid-environment-id`, `KENTICO_DELIVERY_API_KEY=your-delivery-api-key`, and `KENTICO_MANAGEMENT_API_KEY=your-management-api-key`.
Add to .env: KENTICO_ENVIRONMENT_ID=your-environment-id-guid, KENTICO_DELIVERY_API_KEY=your-delivery-api-key, KENTICO_MANAGEMENT_API_KEY=your-management-api-key. Create lib/kentico.ts that exports a kenticoDeliveryFetch helper that calls https://deliver.kontent.ai/{KENTICO_ENVIRONMENT_ID}/{endpoint} with Authorization: Bearer {KENTICO_DELIVERY_API_KEY} header and returns typed JSON. Handle Kentico's error format where errors are returned in a response.message field. Export types for KenticoContentItem, KenticoSystemAttributes, and KenticoElements.
Paste this in Bolt.new chat
1// lib/kentico.ts2const KENTICO_BASE_URL = 'https://deliver.kontent.ai';34interface KenticoDeliveryFetchOptions {5 params?: Record<string, string>;6 preview?: boolean;7}89export interface KenticoSystemAttributes {10 id: string;11 name: string;12 codename: string;13 language: string;14 type: string;15 collection: string;16 sitemap_locations: string[];17 last_modified: string;18 workflow_step?: string;19 url_slug?: string;20}2122export interface KenticoContentItem<T = Record<string, unknown>> {23 system: KenticoSystemAttributes;24 elements: T;25}2627export interface KenticoDeliveryResponse<T = Record<string, unknown>> {28 items: KenticoContentItem<T>[];29 modular_content: Record<string, KenticoContentItem>;30 pagination: {31 skip: number;32 limit: number;33 count: number;34 next_page: string;35 };36}3738export async function kenticoDeliveryFetch<T = Record<string, unknown>>(39 endpoint: string,40 options: KenticoDeliveryFetchOptions = {}41): Promise<T> {42 const environmentId = process.env.KENTICO_ENVIRONMENT_ID;43 const apiKey = process.env.KENTICO_DELIVERY_API_KEY;4445 if (!environmentId) {46 throw new Error('KENTICO_ENVIRONMENT_ID is not configured');47 }4849 const baseUrl = options.preview50 ? `https://preview-deliver.kontent.ai/${environmentId}`51 : `${KENTICO_BASE_URL}/${environmentId}`;5253 const url = new URL(`${baseUrl}/${endpoint}`);54 if (options.params) {55 Object.entries(options.params).forEach(([k, v]) => url.searchParams.set(k, v));56 }5758 const headers: Record<string, string> = {59 'Content-Type': 'application/json',60 };6162 if (apiKey) {63 headers['Authorization'] = `Bearer ${apiKey}`;64 }6566 const response = await fetch(url.toString(), { headers });67 const data = await response.json() as { message?: string; request_id?: string } & T;6869 if (!response.ok) {70 throw new Error(71 `Kentico Delivery API error ${response.status}: ${data.message || response.statusText}`72 );73 }7475 return data;76}Pro tip: Kentico Xperience has two separate delivery endpoints: the Live API (https://deliver.kontent.ai) for published content and the Preview API (https://preview-deliver.kontent.ai) for drafts. The Preview API always requires an API key. The Live API may or may not require a key depending on your channel configuration. Use the Preview URL with a preview flag in your API helper for development workflows.
Expected result: The Kentico API helper is configured with the correct environment ID and API key. A test call to kenticoDeliveryFetch('items') should return your published content items. Verify the response includes items array and pagination data.
Fetch Content Items by Type
Fetch Content Items by Type
Kentico Xperience's Content Delivery API returns content items filtered by content type, language, and custom element values. Content types are defined in Kentico's Content Type Modeler — they represent the structure of different content (Article, Product, Team Member, Event) with typed elements (text, rich text, date-time, number, asset, linked items, taxonomy). The primary endpoint for content retrieval is `GET /{environmentId}/items` with query parameters. Key parameters: `system.type` (filter by content type codename — the lowercase, underscore-separated name like `article` or `product_page`), `system.language` (filter by language variant, e.g., `en-US`, `es-ES`), `elements.{element_codename}[contains]` (filter by element value), `depth` (how many levels of linked items to include, 0-5), `limit` and `skip` for pagination, and `order` for sorting. Kentico elements are returned as typed objects: text elements have a `value` string, rich text elements have a `value` string containing HTML, asset elements have an `images` array and `links` array, linked items have an `itemCodenames` array of codenames referencing items in `modular_content`, and taxonomy elements have a `taxonomyGroup` string and `value` array of terms. For rich text fields, Kentico inlines linked content (embedded items, linked images) using custom HTML attributes. The `@kontent-ai/delivery-sdk` npm package provides a React SDK for properly resolving these references. For simpler cases where the rich text does not contain inline linked items, the raw HTML in the `value` field can be rendered directly with `dangerouslySetInnerHTML`. A key distinction in Kentico's API: items are returned as flat objects with a `system` property (metadata) and `elements` property (content fields). The element codenames match what was configured in Kentico's Content Type Modeler — ask your Kentico administrator for the exact codenames for your content types if you are integrating with an existing Kentico setup.
Create a Next.js API route at app/api/kentico/items/route.ts that fetches content items from Kentico Delivery API using kenticoDeliveryFetch from lib/kentico.ts. Accept query params: type (content type codename), language (default 'en-US'), page (for skip calculation), limit (default 10), slug (optional, filter by url_slug element). Return items array with system and elements, and pagination info. Create a second route at app/api/kentico/items/[codename]/route.ts that fetches a single item by system codename.
Paste this in Bolt.new chat
1// app/api/kentico/items/route.ts2import { NextResponse } from 'next/server';3import { kenticoDeliveryFetch, KenticoDeliveryResponse } from '@/lib/kentico';45export async function GET(request: Request) {6 const { searchParams } = new URL(request.url);7 const type = searchParams.get('type');8 const language = searchParams.get('language') || 'en-US';9 const limit = parseInt(searchParams.get('limit') || '10', 10);10 const page = parseInt(searchParams.get('page') || '1', 10);11 const slug = searchParams.get('slug');12 const depth = searchParams.get('depth') || '1';1314 const skip = (page - 1) * limit;1516 const params: Record<string, string> = {17 'system.language': language,18 limit: limit.toString(),19 skip: skip.toString(),20 depth,21 };2223 if (type) {24 params['system.type'] = type;25 }2627 if (slug) {28 // Filter by URL slug element — adjust element codename for your content type29 params['elements.url_slug'] = slug;30 }3132 try {33 const data = await kenticoDeliveryFetch<KenticoDeliveryResponse>('items', { params });3435 return NextResponse.json({36 items: data.items,37 modularContent: data.modular_content,38 pagination: {39 total: data.pagination.count,40 limit: data.pagination.limit,41 skip: data.pagination.skip,42 hasNextPage: data.pagination.next_page !== '',43 currentPage: page,44 totalPages: Math.ceil(data.pagination.count / limit),45 },46 });47 } catch (err) {48 const message = err instanceof Error ? err.message : 'Failed to fetch Kentico content';49 return NextResponse.json({ error: message }, { status: 500 });50 }51}Pro tip: Kentico content type codenames follow a lowercase, underscore-separated convention (e.g., 'article', 'product_page', 'team_member'). If you are unsure of the codename for a content type, fetch all items without a type filter and inspect the system.type field in the response to find the correct codenames.
Expected result: The items API route returns Kentico content items with their elements and pagination data. The type filter correctly limits results to a specific content type. Test with /api/kentico/items?type=article to verify article content returns correctly.
Build Content Rendering Components
Build Content Rendering Components
Rendering Kentico content in React requires understanding the element type system. Each element in a Kentico content item has a typed structure depending on how it was configured in the Content Type Modeler. Building TypeScript interfaces that match your specific Kentico content types makes the frontend code more maintainable and catches element access errors at compile time. For text elements, access `elements.title.value` (a plain string). For rich text elements, `elements.body.value` contains HTML that can be rendered with `dangerouslySetInnerHTML`. For date-time elements, `elements.published_date.value` is an ISO 8601 string. For asset elements, `elements.feature_image.value[0].url` gives the first image URL, with `elements.feature_image.value[0].description` for alt text. For taxonomy elements, `elements.categories.value` is an array of term objects with `name` and `codename` properties. Linked items are the most complex element type. The `elements.related_articles.value` contains an array of item codenames. The actual linked item objects are in the `modular_content` object at the top level of the API response, keyed by codename. Your component needs to look up linked items from this separate object and render them recursively. For a production implementation with complex linked content, the `@kontent-ai/delivery-sdk` npm package provides automatic linked item resolution, typed responses, and inline rich text component mapping. For simpler cases with shallow content structures, the direct API approach with manual linked item lookup is sufficient. Langauge-specific content: Kentico stores each language variant as a separate item. When you request content with `system.language=es-ES`, you receive the Spanish translation with the same codename as the default language item. Build language switching by re-fetching the same item with a different language parameter.
Create TypeScript interfaces for Kentico Article content type with elements: title (text), slug (url_slug), body (rich_text), published_date (date_time), feature_image (asset), categories (taxonomy), author (linked_items). Build a React ArticleCard component that takes a KenticoContentItem<ArticleElements> and displays title, feature image (from elements.feature_image.value[0]), excerpt (first 200 chars from elements.body.value stripped of HTML tags), publish date formatted with Intl.DateTimeFormat, and category badges from elements.categories.value. Build an ArticleDetail component that renders the full elements.body.value as prose with dangerouslySetInnerHTML.
Paste this in Bolt.new chat
1// types/kentico-article.ts2export interface KenticoTextElement {3 type: 'text';4 name: string;5 value: string;6}78export interface KenticoRichTextElement {9 type: 'rich_text';10 name: string;11 value: string; // HTML string12 images: Record<string, { image_id: string; description: string; url: string; width: number; height: number }>;13 links: Record<string, { codename: string; type: string; url_slug: string }>;14 modular_content: string[];15}1617export interface KenticoAssetElement {18 type: 'asset';19 name: string;20 value: Array<{21 name: string;22 description: string | null;23 type: string;24 size: number;25 url: string;26 width: number | null;27 height: number | null;28 }>;29}3031export interface KenticoDateTimeElement {32 type: 'date_time';33 name: string;34 value: string | null; // ISO 860135}3637export interface KenticoTaxonomyElement {38 type: 'taxonomy';39 name: string;40 taxonomy_group: string;41 value: Array<{ name: string; codename: string }>;42}4344export interface KenticoLinkedItemsElement {45 type: 'modular_content';46 name: string;47 value: string[]; // array of item codenames48}4950export interface ArticleElements {51 title: KenticoTextElement;52 url_slug: KenticoTextElement;53 body: KenticoRichTextElement;54 published_date: KenticoDateTimeElement;55 feature_image: KenticoAssetElement;56 categories: KenticoTaxonomyElement;57 author?: KenticoLinkedItemsElement;58}5960// Utility to strip HTML tags for plain text excerpts:61export function stripHtml(html: string, maxLength = 200): string {62 const stripped = html.replace(/<[^>]+>/g, '').replace(/&[^;]+;/g, ' ').trim();63 return stripped.length > maxLength ? stripped.slice(0, maxLength) + '...' : stripped;64}Pro tip: Ask your Kentico Xperience administrator for the exact element codenames used in each content type — they must match exactly. Kentico codenames are lowercase with underscores (e.g., 'body_copy', 'feature_image', 'published_date'). A mismatch returns undefined rather than an error, making it easy to miss.
Expected result: TypeScript interfaces are defined for your Kentico content types. ArticleCard and ArticleDetail components correctly access typed Kentico element values. The feature image, categories, and rich text body all render correctly on the page.
Implement Kentico Management API for Content Operations
Implement Kentico Management API for Content Operations
Kentico Xperience's Management API enables creating, updating, and publishing content items programmatically. This is valuable for migrating content from other systems into Kentico, automating content creation from external data sources, or building simplified editorial workflows that write back to Kentico. The Management API uses REST with API key authentication. The base URL format is `https://manage.kontent.ai/v2/projects/{environmentId}/`. Unlike the Delivery API (which is read-only), the Management API requires a separate key with write permissions, generated from Kentico's API Keys section. Creating a content item requires two steps: first create the content item (which establishes its identity, type, and collection), then upsert the language variant (which sets the element values for a specific language). This two-step approach is specific to Kentico's content architecture — each content item can have multiple language variants, and elements are stored per-language-variant rather than on the item itself. Step 1: `POST /items` with body `{ name: 'Article title', type: { codename: 'article' }, collection: { codename: 'default' } }`. This returns the new item's `id`. Step 2: `PUT /items/{item_id}/variants/codename/{language_codename}` with body `{ elements: [{ element: { codename: 'title' }, value: 'Article title' }, { element: { codename: 'body' }, value: '<p>Content</p>' }] }`. This upserts the language variant — creating it if it does not exist, or updating it if it does. After creating and setting elements, change the workflow step to publish the content: `PUT /items/{item_id}/variants/codename/{language_codename}/workflow/{workflow_step_codename}`. The codename for the published state depends on your Kentico workflow configuration — typical values are 'published' or 'live'.
Create a Next.js API route at app/api/kentico/manage/articles/route.ts with POST handler that creates a new article in Kentico. Accept { name, body_html, published_date, categories }. Make two API calls: (1) POST to Kentico Management API /items to create the item shell, (2) PUT to /items/{id}/variants/codename/en-US to set element values. Use KENTICO_MANAGEMENT_API_KEY and KENTICO_ENVIRONMENT_ID from process.env. Return the new item id and a preview URL. Include error handling for duplicate item names.
Paste this in Bolt.new chat
1// app/api/kentico/manage/articles/route.ts2import { NextResponse } from 'next/server';34const KENTICO_MANAGE_BASE = 'https://manage.kontent.ai/v2/projects';56async function kenticoManageFetch<T>(7 endpoint: string,8 options: { method: string; body?: Record<string, unknown> }9): Promise<T> {10 const environmentId = process.env.KENTICO_ENVIRONMENT_ID;11 const apiKey = process.env.KENTICO_MANAGEMENT_API_KEY;1213 if (!environmentId || !apiKey) {14 throw new Error('KENTICO_ENVIRONMENT_ID and KENTICO_MANAGEMENT_API_KEY must be configured');15 }1617 const response = await fetch(18 `${KENTICO_MANAGE_BASE}/${environmentId}/${endpoint}`,19 {20 method: options.method,21 headers: {22 Authorization: `Bearer ${apiKey}`,23 'Content-Type': 'application/json',24 },25 body: options.body ? JSON.stringify(options.body) : undefined,26 }27 );2829 const data = await response.json() as { message?: string; validation_errors?: Array<{message: string}> } & T;3031 if (!response.ok) {32 const message = data.message ||33 data.validation_errors?.[0]?.message ||34 `Kentico Management API error: ${response.status}`;35 throw new Error(message);36 }3738 return data;39}4041export async function POST(request: Request) {42 const { name, body_html, published_date, categories } = await request.json() as {43 name: string;44 body_html?: string;45 published_date?: string;46 categories?: string[]; // taxonomy term codenames47 };4849 if (!name) {50 return NextResponse.json({ error: 'name is required' }, { status: 400 });51 }5253 try {54 // Step 1: Create the content item55 const item = await kenticoManageFetch<{ id: string; codename: string }>('items', {56 method: 'POST',57 body: {58 name,59 type: { codename: 'article' },60 collection: { codename: 'default' },61 },62 });6364 // Step 2: Set language variant elements65 const elements = [66 { element: { codename: 'title' }, value: name },67 ];6869 if (body_html) {70 elements.push({ element: { codename: 'body' }, value: body_html });71 }7273 if (published_date) {74 elements.push({ element: { codename: 'published_date' }, value: published_date });75 }7677 if (categories && categories.length > 0) {78 (elements as unknown[]).push({79 element: { codename: 'categories' },80 value: categories.map((c) => ({ codename: c })),81 });82 }8384 await kenticoManageFetch(`items/${item.id}/variants/codename/en-US`, {85 method: 'PUT',86 body: { elements },87 });8889 return NextResponse.json({90 id: item.id,91 codename: item.codename,92 previewUrl: `${process.env.KENTICO_DELIVERY_URL}/preview/${item.codename}`,93 message: `Article '${name}' created as draft in Kentico`,94 }, { status: 201 });95 } catch (err) {96 const message = err instanceof Error ? err.message : 'Failed to create Kentico article';97 return NextResponse.json({ error: message }, { status: 500 });98 }99}Pro tip: Kentico content items created via the Management API start in a draft workflow step. Publishing requires a separate API call to change the workflow step to 'published'. Build this as a two-step action in your UI — create the draft, review it in Kentico's preview, then explicitly publish — rather than auto-publishing on creation.
Expected result: The management API route creates draft article items in Kentico. Verify by checking Kentico's content list after calling the endpoint — the new draft article should appear with the correct title and content.
Deploy and Enable ISR for Published Content
Deploy and Enable ISR for Published Content
Kentico's Content Delivery API calls work in Bolt's WebContainer preview since they are outbound HTTPS requests to Kentico's cloud infrastructure. The Management API also works in the WebContainer — both are server-side API routes making outbound calls, unaffected by the WebContainer's incoming connection limitation. Deploy to Netlify via Settings → Applications → Connect Netlify or click Publish to deploy to Bolt Cloud. Add server-side environment variables: `KENTICO_ENVIRONMENT_ID`, `KENTICO_DELIVERY_API_KEY`, and `KENTICO_MANAGEMENT_API_KEY` — all without NEXT_PUBLIC_ prefix to keep them server-side. For production content delivery performance, implement Next.js ISR using Kentico's webhook notifications. Kentico Xperience supports webhooks that fire when content is published or updated. Configure a webhook in Kentico's admin (Settings → Webhooks) pointing to your deployed `/api/kentico/revalidate` endpoint. When editors publish content in Kentico, the webhook triggers ISR revalidation, serving fresh content without a full redeploy. Kentico's webhooks send a POST request with a payload identifying the changed content item by codename and type. Parse this payload in your revalidation route to determine which pages to revalidate — rather than revalidating all pages, revalidate only the specific article or product page that changed. This targeted revalidation keeps your cached pages fresh while minimizing unnecessary regeneration. For multi-language deployments, ensure each language has a corresponding route structure in Next.js (`/en/articles/[slug]`, `/es/articles/[slug]`) and that your revalidation logic covers all active language variants when content is updated.
Create an /api/kentico/revalidate route that accepts POST with a Kentico webhook payload. Validate the webhook secret from x-kc-signature header against KENTICO_WEBHOOK_SECRET env var. Parse the payload to extract affected item codenames and types. Call revalidatePath for each affected page URL. Return { revalidated: true, paths } on success. Also add KENTICO_ENVIRONMENT_ID to next.config.js remotePatterns for next/image to optimize Kentico-hosted images.
Paste this in Bolt.new chat
1# Production environment variables — Netlify or Bolt Cloud2# Server-side only — never add NEXT_PUBLIC_ prefix34KENTICO_ENVIRONMENT_ID=your-project-environment-guid5KENTICO_DELIVERY_API_KEY=your-delivery-api-key6KENTICO_MANAGEMENT_API_KEY=your-management-api-key7KENTICO_WEBHOOK_SECRET=your-webhook-verification-secret89# Kentico delivery base URLs (for reference, used in lib/kentico.ts):10# Live API: https://deliver.kontent.ai/{KENTICO_ENVIRONMENT_ID}/11# Preview API: https://preview-deliver.kontent.ai/{KENTICO_ENVIRONMENT_ID}/12# Management API: https://manage.kontent.ai/v2/projects/{KENTICO_ENVIRONMENT_ID}/Pro tip: Kentico's webhook signatures use HMAC-SHA256. Verify the x-kc-signature header against a HMAC-SHA256 hash of the request body using your webhook secret. This prevents unauthorized revalidation requests from invalidating your pages cache.
Expected result: The app is deployed and Kentico webhooks trigger ISR revalidation when content is published. Content changes in Kentico admin appear in the Bolt frontend within seconds via webhook-triggered page regeneration.
Common use cases
Headless Article and Blog Frontend
Build a custom article listing and detail view in Bolt that reads article content from Kentico Xperience's Content Delivery API. Display articles organized by category content type, with filtering by publication date, category, and tags. Render rich text content from Kentico's rich text fields with proper HTML output.
Build a headless blog frontend using Kentico Xperience Content Delivery API. Create a Next.js API route at /api/kentico/articles that fetches content items of type 'Article' from Kentico Delivery API at {KENTICO_DELIVERY_URL}/api/delivery/v2/items with x-kc-sdkid: cf/delivery@2.0 and x-kc-environment-id: {KENTICO_ENVIRONMENT_ID} headers. Accept query params for page, limit, and categorySlug. Build a React blog listing page and an /articles/[slug] detail page that renders the article's body_html field. Store KENTICO_DELIVERY_URL, KENTICO_ENVIRONMENT_ID, and KENTICO_DELIVERY_API_KEY in process.env.
Copy this prompt to try it in Bolt.new
Product and Marketing Content Hub
Create a product content hub in Bolt that retrieves product information, features, and marketing copy from Kentico Xperience. Display product pages with rich text descriptions, related products (from Kentico's linked items), specification tables, and customer testimonials all managed through Kentico's content editor interface.
Build a product content hub using Kentico CMS. Create API routes at /api/kentico/products (listing) and /api/kentico/products/[slug] (detail). Fetch content items of type 'Product' from Kentico Delivery API with linked items depth=2 to include related products and feature lists. Build a product listing page with category filtering and a product detail page showing name, description_html, features (linked items), specifications (structured field), and related_products. Use KENTICO_DELIVERY_URL and KENTICO_DELIVERY_API_KEY from process.env.
Copy this prompt to try it in Bolt.new
Multi-language Content Localization
Build a multi-language Bolt frontend that retrieves localized content from Kentico Xperience's multi-language content variants. Display content in the user's preferred language with automatic fallback to the default language variant when a translation is not available.
Build a multi-language page using Kentico localization. Create a Next.js API route at /api/kentico/page that accepts slug and language query params. Fetch content from Kentico Delivery API with lang={language} query param. If the requested language has no content (404), fall back to the default language (en-US). Build a page component that shows the content in the requested language with a language switcher dropdown. Support at least English, Spanish, and French. Use KENTICO_DELIVERY_URL, KENTICO_ENVIRONMENT_ID, KENTICO_DELIVERY_API_KEY from process.env.
Copy this prompt to try it in Bolt.new
Troubleshooting
Kentico Delivery API returns 401 Unauthorized or 403 Forbidden
Cause: The Delivery API key is either missing, incorrect, or not configured for the correct channel. Kentico's Live API may also require authentication based on channel security settings.
Solution: Verify the KENTICO_DELIVERY_API_KEY is set correctly and matches the key generated for your headless channel. In Kentico's channel settings, check whether the Live API requires authentication. If your channel is configured for unauthenticated public access, you can omit the API key for Live API calls. For Preview API calls, authentication is always required.
Content items return but elements are undefined or have unexpected structure
Cause: The element codenames used in your TypeScript interfaces do not match the actual codenames configured in Kentico's Content Type Modeler, or the depth parameter is too low to include linked items.
Solution: Fetch a single item without field filtering and inspect the raw elements object in the API response to find the exact codenames. Codenames are lowercase with underscores. Also check that the depth parameter is high enough to include linked item content — increase depth from 1 to 2 or 3 for content types with nested linked items.
1// Debug: log raw elements to find correct codenames2const rawData = await kenticoDeliveryFetch('items?limit=1&system.type=article');3console.log('Element codenames:', Object.keys(rawData.items[0].elements));Management API returns 404 for content type codename 'article'
Cause: The content type codename 'article' does not exist in this Kentico project, or the content type uses a different codename.
Solution: Get the correct content type codenames by calling the Management API types endpoint: GET /types. This lists all content types with their codenames. Ask your Kentico administrator for the exact codenames used in your project's content model.
1// List all content types to find correct codenames:2const types = await kenticoManageFetch('types', { method: 'GET' });3console.log('Content types:', types.types.map(t => ({ name: t.name, codename: t.codename })));Best practices
- Request only the element fields you need using the 'elements' query parameter to reduce response size — Kentico content items can have many elements, and a full response for a content-rich page can be large.
- Use the depth parameter judiciously — depth=0 returns no linked items, depth=1 includes one level of linked items, depth=5 is the maximum. Higher depth increases response size and latency significantly for deeply nested content.
- Implement ISR with Kentico webhooks for production content delivery — statically generated pages with webhook-triggered revalidation provide the best performance while keeping content fresh after editor updates.
- Store all Kentico API keys as server-side environment variables without NEXT_PUBLIC_ prefix — both Delivery API keys and Management API keys grant access to your entire Kentico project's content.
- Build TypeScript interfaces that match your Kentico content types exactly — using typed element access prevents runtime errors from mismatched codenames and makes the code self-documenting for team members unfamiliar with Kentico's element structure.
- Use the Preview API URL with authentication for development and editorial preview workflows — the Live API only returns published content, so your editors cannot review drafts through a Bolt preview without the Preview API key.
- Handle Kentico's two-step content creation (create item + upsert language variant) as a transaction in your Management API routes — if the language variant upsert fails, clean up the created item to avoid orphaned content.
- Verify content type codenames with your Kentico administrator before building API routes — codenames are determined when content types are created in Kentico and may not follow obvious naming conventions in older projects.
Alternatives
Ghost is open-source with a $9/month hosted plan and a cleaner REST API, making it the recommended choice for new projects that do not already have a Kentico investment.
WordPress offers a free open-source CMS with a large plugin ecosystem and a REST API, making it accessible for teams already using WordPress who need a headless frontend.
TYPO3 is a free open-source enterprise CMS with similar multi-site and multi-language capabilities to Kentico but with no licensing cost, making it a viable alternative for budget-conscious enterprise deployments.
Joomla provides free open-source content management with a REST API, suitable for organizations wanting enterprise-grade CMS features without the Kentico licensing cost.
Frequently asked questions
Does Bolt.new have a native Kentico Xperience integration?
No — Bolt.new does not have a native Kentico connector. Building the integration requires creating Next.js API routes that call Kentico's Content Delivery API. Bolt's AI can generate the initial API route structure and component code from a descriptive prompt, though you will need to adjust element codenames to match your specific Kentico content type configuration.
Can I test the Kentico integration in Bolt's WebContainer without deploying?
Yes — all outbound calls to Kentico's Delivery API and Management API work in Bolt's WebContainer since they are HTTPS requests to Kentico's cloud infrastructure. You can fetch content, display it in your React components, and test Management API content creation entirely in the WebContainer preview. Kentico webhook notifications require a deployed URL since they are incoming POST requests.
Do I need a Kentico license to develop and test this integration?
Yes — Kentico Xperience requires an enterprise license for production use, starting around $36,000 per year. However, Kentico offers trial licenses for evaluation and development that are fully functional. Contact Kentico sales to request a trial license if you are evaluating the integration. For new projects without existing Kentico infrastructure, the licensing cost makes Ghost, Contentful, or Strapi more practical alternatives.
What is the difference between Kentico Xperience and Kentico Kontent.ai?
Kentico offers two products: Kentico Xperience is the traditional .NET-based DXP with web forms pages, digital marketing, and headless API capabilities in one platform. Kentico Kontent.ai (now rebranded as Kontent.ai) is a pure headless CMS SaaS offering with a subscription model starting around $1,299/month. This guide covers Kentico Xperience. For a pure headless SaaS CMS from Kentico, use the Kontent.ai API which has a slightly different endpoint structure.
How do I handle Kentico's linked items in React components?
Kentico's linked items element returns an array of codename strings, while the actual item objects are in a separate 'modular_content' object at the response root level, keyed by codename. In your component, look up each linked item from modular_content using the codenames from the element value. For deeply nested linked items, set the depth parameter high enough to include all levels you need — depth=2 includes items linked within linked items.
Can I use the same API key for both Live and Preview content delivery?
No — Kentico uses separate API keys for Live (published content) and Preview (draft content). The Live API may be configured to not require a key at all for public content. The Preview API always requires an API key. Generate separate keys for each and store them as KENTICO_DELIVERY_API_KEY (live) and KENTICO_PREVIEW_API_KEY (preview) in your environment variables. Use the preview key only in authenticated editorial workflows, not in public-facing API routes.
Talk to an Expert
Our team has built 600+ apps. Get personalized help with your project.
Book a free consultation