Integrate Joomla with Bolt.new by enabling the Web Services API plugin in Joomla 4+ and generating a user API token from Users → Your Profile → API Token. Build a Next.js API route in Bolt to proxy requests with Bearer token authentication, fetch articles and categories as JSON, and render them in React. Joomla's REST API is available since version 4.0 but has fewer community resources than WordPress.
Build a Headless Joomla Frontend with Bolt.new
Joomla is the third most popular PHP CMS globally, powering government websites, educational institutions, and community portals. Since Joomla 4.0, the platform includes a built-in Web Services API that exposes articles, categories, menus, users, contacts, and tags as JSON — no plugins required for the core content types. This makes it possible to use Joomla as a headless CMS backend while building a modern React frontend in Bolt.new, giving content editors the familiar Joomla administrative interface while the frontend gains full design control and modern JavaScript tooling.
Joomla's API uses Bearer token authentication rather than the Application Password system used by WordPress. You generate a token in the Joomla user profile (Users → Your Profile → scroll to the API Token tab) and include it as an Authorization Bearer header in every API request. The API is accessible at /api/index.php/v1/ — for example, /api/index.php/v1/content/articles returns a paginated list of published articles. For public-facing content on sites without authentication requirements, some Joomla installations allow anonymous API access, but enabling the API token for all requests is the safer default.
It is worth noting that Joomla's headless CMS ecosystem is smaller and less documented than WordPress's — you will find fewer tutorials, fewer starter templates, and a smaller community of developers who have done headless Joomla work. For new projects where you have freedom to choose the CMS, WordPress offers a more mature headless setup with more community resources. This guide is most valuable for teams that already have an existing Joomla installation they want to connect to a modern frontend, or organizations with a Joomla preference for their content workflow.
Integration method
Joomla 4+ includes a Web Services API plugin that exposes articles, categories, menus, users, and modules as JSON endpoints. In Bolt.new you build a Next.js API route that proxies requests to the Joomla API using Bearer token authentication, keeping credentials server-side. The API route fetches and normalizes Joomla content which React components consume to render the frontend independently of any Joomla template or theme.
Prerequisites
- A running Joomla 4.0+ installation with administrator access and the Web Services API plugin enabled
- A Joomla API token generated from Users → Your Profile → API Token tab in the Joomla backend
- Your Joomla site's base URL (e.g., https://yoursite.com) — the API is at /api/index.php/v1/
- At least a few published articles and categories in Joomla to test the integration
- A Bolt.new account with a new Next.js project open
Step-by-step guide
Enable the Joomla Web Services API Plugin and Generate an API Token
Enable the Joomla Web Services API Plugin and Generate an API Token
Joomla 4.0 and later includes the Web Services API as a built-in feature, but the API plugin is disabled by default in most Joomla installations. To enable it, log into the Joomla backend as an administrator and navigate to System → Plugins. Search for 'Web Services' in the plugin manager — you will find several plugins listed, including 'Web Services - Content', 'Web Services - Categories', 'Web Services - Contacts', and others. Enable all the Web Services plugins that correspond to content types you want to expose. The most important is 'Web Services - Content' for articles. Each plugin can be enabled individually by clicking its name and toggling the status to Enabled. After enabling the plugins, verify the API is responding by opening https://yoursite.com/api/index.php/v1/content/articles in a browser. If the Joomla installation requires authentication, you will see a 401 response rather than JSON — that is expected and correct. To generate an API token, go to Users → Manage Users, click your administrator username, then scroll down to the API Token tab. Click the Generate Token button. Copy the token string immediately and store it securely — it is only shown once and cannot be retrieved later if lost (you can generate a new one, but this invalidates all existing tokens for that user). Create a dedicated Joomla user account with only the permissions needed for API access rather than using your main administrator credentials.
1# .env — add to project root2JOOMLA_API_URL=https://yoursite.com3JOOMLA_API_TOKEN=your_joomla_api_token_here45# Verify API is enabled by opening in browser:6# https://yoursite.com/api/index.php/v1/content/articles7# With token: curl -H 'Authorization: Bearer YOUR_TOKEN' https://yoursite.com/api/index.php/v1/content/articlesPro tip: If the Joomla API returns a 404 instead of JSON or a 401, check that the 'Web Services - Content' plugin is enabled in System → Plugins. Also verify that Joomla's URL routing is using SEF URLs (System → Global Configuration → SEO → Use URL Rewriting should be set to Yes).
Expected result: The Joomla Web Services API plugins are enabled. Your .env file contains JOOMLA_API_URL and JOOMLA_API_TOKEN. Calling the API URL with the Bearer token in a browser or curl returns a JSON response with articles data.
Prompt Bolt to Generate the Joomla Articles API Route
Prompt Bolt to Generate the Joomla Articles API Route
With the Joomla API enabled and your credentials in .env, the next step is building the server-side proxy route in Bolt.new. A proxy API route is essential for this integration: Joomla installations rarely include CORS headers for browser requests from external domains, so direct client-side fetch calls would fail in the Bolt preview. Routing through a Next.js API route also keeps the JOOMLA_API_TOKEN credential out of client-side code, which is critical because API tokens provide authenticated access to your entire Joomla site. The Joomla Web Services API follows JSON:API specification, which means responses are structured with a 'data' array containing items, each with an 'id' and 'attributes' object. The article attributes include title, alias (used as the URL slug), introtext (teaser text), fulltext (full article body), catid, publish_up (publication date), and images (a JSON string of image URLs). Joomla's API also returns pagination metadata in a 'links' object with first, prev, next, and last links. Use the Bolt chat prompt below to generate the articles API route. After Bolt generates the code, verify that it reads both JOOMLA_API_URL and JOOMLA_API_TOKEN from process.env without any NEXT_PUBLIC_ prefix, and that the Authorization header uses the Bearer scheme rather than Basic auth (which Joomla does not support for API tokens).
Create a Joomla headless CMS integration in this Next.js project. Build an API route at app/api/joomla/articles/route.ts that fetches published articles from Joomla Web Services API. Read JOOMLA_API_URL and JOOMLA_API_TOKEN from process.env. Construct the request URL as JOOMLA_API_URL + '/api/index.php/v1/content/articles'. Add Authorization: Bearer JOOMLA_API_TOKEN header. Support optional query params: 'limit' (default 10), 'offset' (default 0), 'catid' to filter by category. Map Joomla's JSON:API response format (data[].attributes) to a clean object with id, title, slug (from alias), introtext, fulltext, catid, and publishDate fields. Return the normalized array and total count.
Paste this in Bolt.new chat
1// app/api/joomla/articles/route.ts2import { NextRequest, NextResponse } from 'next/server';34interface JoomlaArticleAttributes {5 title: string;6 alias: string;7 introtext: string;8 fulltext: string;9 catid: number;10 publish_up: string;11 images: string;12 state: number;13}1415interface JoomlaAPIItem {16 id: string;17 attributes: JoomlaArticleAttributes;18}1920interface JoomlaAPIResponse {21 data: JoomlaAPIItem[];22 meta?: { 'total-pages'?: number };23 links?: { self: string; next?: string };24}2526export async function GET(request: NextRequest) {27 const apiUrl = process.env.JOOMLA_API_URL;28 const apiToken = process.env.JOOMLA_API_TOKEN;2930 if (!apiUrl || !apiToken) {31 return NextResponse.json(32 { error: 'JOOMLA_API_URL and JOOMLA_API_TOKEN must be configured' },33 { status: 500 }34 );35 }3637 const { searchParams } = new URL(request.url);38 const limit = searchParams.get('limit') || '10';39 const offset = searchParams.get('offset') || '0';40 const catid = searchParams.get('catid');4142 const url = new URL(`${apiUrl}/api/index.php/v1/content/articles`);43 url.searchParams.set('page[limit]', limit);44 url.searchParams.set('page[offset]', offset);45 if (catid) url.searchParams.set('filter[category_id]', catid);4647 try {48 const response = await fetch(url.toString(), {49 headers: {50 'Authorization': `Bearer ${apiToken}`,51 'Accept': 'application/vnd.api+json',52 'Content-Type': 'application/json',53 },54 });5556 if (!response.ok) {57 return NextResponse.json(58 { error: `Joomla API error: ${response.status} ${response.statusText}` },59 { status: response.status }60 );61 }6263 const data: JoomlaAPIResponse = await response.json();6465 const articles = (data.data || []).map((item) => {66 const attr = item.attributes;67 let images: Record<string, string> = {};68 try {69 images = JSON.parse(attr.images || '{}');70 } catch {71 images = {};72 }7374 return {75 id: item.id,76 title: attr.title,77 slug: attr.alias,78 introtext: attr.introtext,79 fulltext: attr.fulltext,80 catid: attr.catid,81 publishDate: attr.publish_up,82 introImage: images.image_intro || null,83 fullImage: images.image_fulltext || null,84 };85 });8687 return NextResponse.json({88 articles,89 total: data.meta?.['total-pages'] || null,90 });91 } catch (error) {92 const message = error instanceof Error ? error.message : 'Unknown error';93 return NextResponse.json({ error: message }, { status: 500 });94 }95}Pro tip: Joomla's JSON:API response wraps all content in a 'data' array where each item has 'id' and 'attributes'. This is different from WordPress's REST API which returns a flat array of objects. Always access article fields through item.attributes rather than directly on the item.
Expected result: Calling /api/joomla/articles in the Bolt preview returns a JSON array of normalized Joomla articles with title, slug, introtext, catid, publishDate, and image URL fields.
Build the Article Listing and Single Article Pages
Build the Article Listing and Single Article Pages
With the articles API route working, build the React frontend pages that display Joomla content. A standard Joomla blog frontend needs at minimum a listing page showing article cards and a dynamic detail page rendering the full article content. Joomla article content is returned as raw HTML in the introtext and fulltext fields — render it using dangerouslySetInnerHTML, which is appropriate since the HTML comes from your own trusted Joomla installation. The listing page calls /api/joomla/articles and renders a grid of cards with the title, intro text, intro image, and publish date. For single article pages, build a separate API route that fetches a single article by its alias (slug) using Joomla's filter parameter: /api/index.php/v1/content/articles?filter[alias]=your-article-alias. Joomla also returns the fulltext field separately from introtext — combine them when rendering the full article view. For articles using Joomla's 'Read more' separator, introtext contains the content above the separator and fulltext the content below; concatenate them for the full article. Strip HTML tags from introtext before displaying it as a card excerpt — a simple regex replace handles this for most cases. Add navigation breadcrumbs using the catid to link back to the category filter view.
Build an article listing and detail page for this Joomla headless Next.js project. Create a listing page at app/blog/page.tsx that fetches from /api/joomla/articles with limit=12 and displays articles as cards in a responsive grid. Each card should show the intro image (if available), title, trimmed plain-text excerpt from introtext (strip HTML tags), and formatted publish date. Create a second API route at app/api/joomla/articles/[slug]/route.ts that fetches a single article by alias using filter[alias]=SLUG from JOOMLA_API_URL/api/index.php/v1/content/articles with Bearer auth. Create a detail page at app/blog/[slug]/page.tsx that renders the full article (introtext + fulltext) using dangerouslySetInnerHTML in a Tailwind prose container.
Paste this in Bolt.new chat
1// app/api/joomla/articles/[slug]/route.ts2import { NextResponse } from 'next/server';34export async function GET(5 _request: Request,6 { params }: { params: { slug: string } }7) {8 const apiUrl = process.env.JOOMLA_API_URL;9 const apiToken = process.env.JOOMLA_API_TOKEN;1011 if (!apiUrl || !apiToken) {12 return NextResponse.json(13 { error: 'Joomla API credentials not configured' },14 { status: 500 }15 );16 }1718 const url = new URL(`${apiUrl}/api/index.php/v1/content/articles`);19 url.searchParams.set('filter[alias]', params.slug);2021 try {22 const response = await fetch(url.toString(), {23 headers: {24 'Authorization': `Bearer ${apiToken}`,25 'Accept': 'application/vnd.api+json',26 },27 });2829 if (!response.ok) {30 return NextResponse.json(31 { error: `Joomla API error: ${response.status}` },32 { status: response.status }33 );34 }3536 const data = await response.json();37 const items = data.data || [];3839 if (items.length === 0) {40 return NextResponse.json({ error: 'Article not found' }, { status: 404 });41 }4243 const attr = items[0].attributes;44 let images: Record<string, string> = {};45 try { images = JSON.parse(attr.images || '{}'); } catch { images = {}; }4647 const article = {48 id: items[0].id,49 title: attr.title,50 slug: attr.alias,51 introtext: attr.introtext,52 fulltext: attr.fulltext,53 content: [attr.introtext, attr.fulltext].filter(Boolean).join('\n'),54 catid: attr.catid,55 publishDate: attr.publish_up,56 introImage: images.image_intro || null,57 fullImage: images.image_fulltext || null,58 };5960 return NextResponse.json({ article });61 } catch (error) {62 const message = error instanceof Error ? error.message : 'Unknown error';63 return NextResponse.json({ error: message }, { status: 500 });64 }65}Pro tip: Joomla's introtext field often contains HTML with Joomla-specific read-more tags like {readmore}. Strip these tags from the introtext before using it as a card excerpt. For full article pages, concatenate introtext and fulltext and render the combined HTML.
Expected result: The /blog listing page shows Joomla articles as cards with titles, excerpts, and images. Clicking a card navigates to /blog/[slug] which renders the full article content including both introtext and fulltext HTML.
Fetch Joomla Categories for Navigation and Filtering
Fetch Joomla Categories for Navigation and Filtering
Joomla's category system is a core organizational feature — articles are assigned to categories, and categories can be nested in parent-child hierarchies. The Web Services API exposes categories through the /api/index.php/v1/categories endpoint with a component query parameter to specify which component's categories to fetch. For articles, use component=com_content. The API returns category data including id, title, alias, parent_id, description, and level (depth in the hierarchy). You can use this data to build a category navigation sidebar, filter tabs above the article listing, or breadcrumb navigation on article detail pages. When fetching articles filtered by category, pass the category id using the filter[category_id] parameter. Note that Joomla's category alias (slug) is separate from the category ID — if you want URL-based category filtering (e.g., /blog/category/news), you will need to first fetch the category by alias to get its ID, then fetch articles using that ID. For most implementations, storing the category list in React state on first load and using the category ID for filtering is the most efficient approach. The category API also supports pagination with the same page[limit] and page[offset] parameters as the articles endpoint, which matters for sites with large numbers of categories.
Add category navigation to this Joomla headless Next.js project. Create an API route at app/api/joomla/categories/route.ts that fetches content categories from process.env.JOOMLA_API_URL + '/api/index.php/v1/categories?component=com_content' with Bearer auth. Return categories with id, title, alias, parentId, and level fields. Update the app/blog/page.tsx listing page to fetch categories on mount and display them as filter tabs above the article grid. Clicking a category tab should re-fetch /api/joomla/articles with the selected catid filter. Show 'All' as the first unfiltered tab.
Paste this in Bolt.new chat
1// app/api/joomla/categories/route.ts2import { NextResponse } from 'next/server';34export async function GET() {5 const apiUrl = process.env.JOOMLA_API_URL;6 const apiToken = process.env.JOOMLA_API_TOKEN;78 if (!apiUrl || !apiToken) {9 return NextResponse.json(10 { error: 'Joomla API credentials not configured' },11 { status: 500 }12 );13 }1415 const url = new URL(`${apiUrl}/api/index.php/v1/categories`);16 url.searchParams.set('component', 'com_content');17 url.searchParams.set('page[limit]', '50');1819 try {20 const response = await fetch(url.toString(), {21 headers: {22 'Authorization': `Bearer ${apiToken}`,23 'Accept': 'application/vnd.api+json',24 },25 });2627 if (!response.ok) {28 return NextResponse.json(29 { error: `Joomla Categories API error: ${response.status}` },30 { status: response.status }31 );32 }3334 const data = await response.json();35 const categories = (data.data || []).map((item: { id: string; attributes: { title: string; alias: string; parent_id: number; level: number; description: string } }) => ({36 id: item.id,37 title: item.attributes.title,38 alias: item.attributes.alias,39 parentId: item.attributes.parent_id,40 level: item.attributes.level,41 description: item.attributes.description,42 }));4344 return NextResponse.json({ categories });45 } catch (error) {46 const message = error instanceof Error ? error.message : 'Unknown error';47 return NextResponse.json({ error: message }, { status: 500 });48 }49}Pro tip: Joomla categories include system categories that you may not want to display (such as 'Uncategorised' with ID 2 and root categories with level 0 or 1). Filter the returned categories to only show those with level >= 2 and a meaningful title before rendering them as navigation items.
Expected result: The /api/joomla/categories endpoint returns Joomla content categories. The blog listing page displays category filter tabs, and selecting a category filters the article grid to show only articles in that category.
Deploy to Netlify and Configure Production Environment Variables
Deploy to Netlify and Configure Production Environment Variables
Your headless Joomla frontend works in Bolt's WebContainer preview because all Joomla API calls are outbound HTTP requests from the server-side Next.js API routes — outbound calls to external servers work correctly in WebContainer during development. When you are ready to share the site publicly, connect your Bolt project to Netlify through Settings → Applications → Netlify → Connect. Bolt will build the Next.js project and deploy it to a *.netlify.app URL automatically. After the first deployment, open your Netlify dashboard → Site Configuration → Environment Variables and add JOOMLA_API_URL (your full Joomla site URL) and JOOMLA_API_TOKEN. Trigger a redeploy from the Netlify dashboard for the environment variables to take effect — environment variables added after an initial deploy require a new build to be available in the application code. One critical WebContainer limitation to understand: incoming webhooks from Joomla extensions cannot reach the Bolt preview URL during development. Bolt's WebContainer runs inside a browser tab and does not expose a public-facing HTTP server, so any Joomla extension that sends HTTP callbacks (workflow notifications, content publishing hooks, or third-party integrations) requires your deployed Netlify URL. After deployment, configure any Joomla extension webhooks to point at your Netlify site URL. If you want content updates in Joomla to automatically refresh cached pages in your Next.js frontend without a full Netlify redeploy, add a revalidation endpoint that Joomla can call when articles are published — though Joomla has fewer webhook plugins than WordPress, making on-demand revalidation less commonly used in Joomla headless setups.
Add a cache revalidation endpoint to this headless Joomla Next.js project. Create an API route at app/api/joomla/revalidate/route.ts that accepts a POST request with a secret token in the x-revalidate-secret header matching process.env.JOOMLA_REVALIDATE_SECRET. Parse the request body for an optional 'alias' string (the article slug). Call revalidatePath for /blog and for the specific article path if alias is provided. Return 200 with revalidated: true on success.
Paste this in Bolt.new chat
1// app/api/joomla/revalidate/route.ts2import { NextResponse } from 'next/server';3import { revalidatePath } from 'next/cache';45export async function POST(request: Request) {6 const secret = request.headers.get('x-revalidate-secret');78 if (!secret || secret !== process.env.JOOMLA_REVALIDATE_SECRET) {9 return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });10 }1112 try {13 const body = await request.json();14 const alias = body?.alias as string | undefined;1516 revalidatePath('/blog');1718 if (alias) {19 revalidatePath(`/blog/${alias}`);20 }2122 return NextResponse.json({23 revalidated: true,24 paths: alias ? ['/blog', `/blog/${alias}`] : ['/blog'],25 });26 } catch {27 return NextResponse.json({ error: 'Revalidation failed' }, { status: 500 });28 }29}Pro tip: Add JOOMLA_REVALIDATE_SECRET to Netlify's environment variables and configure a Joomla webhook plugin (such as 'Webhooks for Joomla' from the JED directory) to POST to your deployed /api/joomla/revalidate endpoint when articles are published or updated.
Expected result: Your headless Joomla frontend is deployed on Netlify. Joomla articles and categories appear on the deployed site. The revalidation endpoint allows on-demand cache refresh when Joomla content is published.
Common use cases
Existing Joomla Site with Modern React Frontend
Replace a Joomla template theme with a custom React application while keeping all content, categories, and menus in Joomla. Content editors continue using the familiar Joomla backend, while the React frontend delivers improved performance, custom design, and modern component-based UI that Joomla templates cannot easily achieve.
Build a headless Joomla CMS integration in this Next.js project. Create an API route at app/api/joomla/articles/route.ts that fetches published articles from the Joomla Web Services API using process.env.JOOMLA_API_URL + '/api/index.php/v1/content/articles'. Add an Authorization header with Bearer process.env.JOOMLA_API_TOKEN. Support pagination with 'page[limit]' and 'page[offset]' query params. Return articles with id, title, alias (slug), introtext, fulltext, catid, and publish_up date fields.
Copy this prompt to try it in Bolt.new
Joomla Category Blog Page
Fetch Joomla categories and their articles to build a structured content hub with category filtering. Joomla's category system is hierarchical — categories can have parent and child relationships — which maps well to a React navigation sidebar and filtered article listing.
Create a category blog page for this Joomla headless Next.js project. Build an API route at app/api/joomla/categories/route.ts that fetches published categories from process.env.JOOMLA_API_URL + '/api/index.php/v1/categories?component=com_content' with Bearer auth. Build a second API route at app/api/joomla/articles/route.ts that accepts a 'catid' query param to filter by category. Create a blog page at app/blog/page.tsx showing categories as filter tabs and articles as cards in a responsive grid.
Copy this prompt to try it in Bolt.new
Joomla Contact Directory
Fetch Joomla contact records from the com_contact component API to build a staff directory or contact listing page. Joomla's Web Services API exposes the contact component endpoints, making it straightforward to pull contact names, roles, emails, and images from an existing Joomla directory into a React UI.
Build a contact directory page using Joomla's Web Services API. Create an API route at app/api/joomla/contacts/route.ts that fetches contacts from process.env.JOOMLA_API_URL + '/api/index.php/v1/contacts' with Bearer authorization header using process.env.JOOMLA_API_TOKEN. Return contact name, position, email_to, and image fields. Render contacts in a responsive card grid at app/team/page.tsx with name, position, and email visible on each card.
Copy this prompt to try it in Bolt.new
Troubleshooting
Joomla API returns 404 Not Found when calling /api/index.php/v1/content/articles
Cause: The Joomla Web Services API plugins are disabled, or Joomla's URL rewriting is not configured correctly for API routing.
Solution: Log into the Joomla backend and go to System → Plugins. Search for 'Web Services' and enable all relevant plugins, especially 'Web Services - Content'. Also verify that SEF URLs are enabled in Global Configuration → SEO → Use URL Rewriting. If the Joomla installation does not have a .htaccess file from the htaccess.txt sample, rename it to enable Apache mod_rewrite support.
Joomla API returns 401 Unauthorized despite correct API token
Cause: The API token is not associated with a user who has adequate permissions, or the token was regenerated after copying and the old token is now invalid.
Solution: Go to Users → Manage Users in the Joomla backend, click the user whose token you are using, and verify their user group has at least 'Manager' access level for content API endpoints. If you recently regenerated the token, update JOOMLA_API_TOKEN in your .env file with the new token value. Note that regenerating a token immediately invalidates the previous one.
1// Verify the token works with a direct curl test:2// curl -H 'Authorization: Bearer YOUR_TOKEN' \3// -H 'Accept: application/vnd.api+json' \4// https://yoursite.com/api/index.php/v1/content/articles5// Expected: JSON response with 'data' arrayCORS error when calling the Joomla API from a React component in the Bolt preview
Cause: Joomla does not add CORS headers for arbitrary origins by default, so direct browser-side fetch calls to your Joomla URL fail when the Bolt preview origin does not match.
Solution: Route all Joomla API calls through the Next.js API routes at /api/joomla/* rather than calling the Joomla URL directly from client React components. The API routes execute server-side and bypass browser CORS restrictions entirely.
1// Wrong — direct call from a React component2const res = await fetch('https://yoursite.com/api/index.php/v1/content/articles');34// Correct — call through your Next.js API route5const res = await fetch('/api/joomla/articles?limit=10');Joomla webhooks or content update callbacks do not reach the Bolt preview URL
Cause: Bolt's WebContainer runs in a browser tab and cannot accept incoming HTTP connections from external services — no public HTTP server exists in the WebContainer development environment.
Solution: Deploy to Netlify first to get a stable public URL, then configure any Joomla webhook extensions to point at your deployed /api/joomla/revalidate endpoint. The Bolt preview URL is only suitable for testing outbound API calls, not for receiving incoming requests from Joomla.
Best practices
- Never expose JOOMLA_API_TOKEN as a NEXT_PUBLIC_ variable — keep it server-side in API routes only, since the token provides authenticated access to your entire Joomla backend
- Create a dedicated Joomla user account with minimum necessary permissions for API access rather than using your main administrator token
- Always access Joomla JSON:API responses through item.attributes — the JSON:API format wraps all fields in attributes, not at the top level of each item
- Filter system categories (level 0 and 1, and 'Uncategorised') from category listings before rendering navigation
- Concatenate introtext and fulltext fields to render full Joomla articles — articles using the 'Read More' separator split content between these two fields
- Test all Joomla API calls in the Bolt WebContainer preview before deploying — outbound calls to your Joomla server work correctly in the development environment
- For new projects without an existing Joomla CMS, consider WordPress as the headless CMS alternative — WordPress has a larger headless community and a more mature REST API with more tutorials and starter kits
- Add error boundaries in React components that render Joomla HTML content, since introtext and fulltext may occasionally contain malformed HTML from the Joomla editor
Alternatives
WordPress has a built-in REST API requiring no setup, a vastly larger headless CMS community, and more tutorials and starter kits than Joomla — making it the better choice for most new headless CMS projects.
TYPO3 is another open-source PHP CMS with stronger enterprise multi-site and multi-language features than Joomla, though it requires installing the Headless extension and is most common in German-speaking markets.
Ghost is a modern open-source CMS purpose-built for publishing with a clean JSON Content API and built-in newsletter features, offering a much simpler headless setup than Joomla for content and blogging sites.
Kentico is a commercial enterprise CMS with a headless-first API and .NET backend, better suited to organizations already in the Microsoft ecosystem who need enterprise content governance beyond Joomla's scope.
Frequently asked questions
Does Bolt.new work with Joomla?
Yes. Joomla 4.0 and later includes a Web Services API that works with Bolt.new's Next.js API routes. You enable the Web Services plugins in the Joomla backend, generate an API token, and build a proxy API route in Bolt that calls the Joomla API with Bearer authentication. Outbound API calls to Joomla work correctly in Bolt's WebContainer preview.
Do I need any Joomla extensions to use the Web Services API?
No additional extensions are required — the Web Services API is built into Joomla 4.0+. However, the API plugins are disabled by default and must be enabled individually in System → Plugins. Enable 'Web Services - Content' for articles, 'Web Services - Categories' for categories, and any other component plugins matching your content types. For Joomla 3.x, the Web Services API is not available and you would need a third-party extension.
Can I use Bolt.new with Joomla 3.x?
Joomla 3.x does not include a built-in Web Services API. For Joomla 3.x you would need a third-party API extension such as 'API for Joomla' from the Joomla Extensions Directory. However, Joomla 3.x reached end of life in August 2023, so migrating to Joomla 4 or 5 is strongly recommended before building a headless frontend.
How is Joomla's API different from WordPress's REST API?
Joomla's API follows the JSON:API specification, which wraps all response data in a 'data' array where each item has an 'id' and an 'attributes' object. WordPress returns flat JSON objects directly. Joomla also uses Bearer token authentication, while WordPress can use Basic auth with Application Passwords. Joomla's API community and documentation are smaller than WordPress's, so you may find fewer ready-made examples and tools.
Can Joomla webhooks reach the Bolt.new development preview?
No. Bolt's WebContainer runs inside a browser tab and cannot accept incoming HTTP connections. Joomla extension webhooks, workflow notifications, and content publishing callbacks all require a deployed URL. Deploy to Netlify first, then configure Joomla extensions to use your deployed URL's revalidation endpoint.
Is Joomla a good choice for new projects using Bolt.new?
For new projects, WordPress or Ghost are typically better choices due to their larger headless CMS ecosystems and more documentation. Joomla is most valuable when connecting to an existing Joomla installation that your team already uses and maintains. The Joomla Web Services API works well with Bolt.new, but the smaller community means less support when you encounter integration issues.
Talk to an Expert
Our team has built 600+ apps. Get personalized help with your project.
Book a free consultation