# Joomla

- Tool: FlutterFlow
- Difficulty: Intermediate
- Time required: 45 minutes
- Last updated: July 2026

## TL;DR

Connect FlutterFlow to Joomla using a FlutterFlow API Calls group targeting Joomla 4's built-in Web Services REST API at /api/index.php/v1/. Enable the Web Services plugins in your Joomla admin, generate a Bearer API token from the Users panel, and create API Calls in FlutterFlow to read articles and categories into a mobile content feed. Joomla 3 has no native REST API — version 4 or higher is required.

## Turn Your Joomla CMS Into a Headless Backend for a FlutterFlow Mobile App

Joomla powers millions of websites and manages large libraries of articles, categories, and media. With Joomla 4's built-in Web Services REST API, you can use that existing content as the backend for a FlutterFlow mobile app without duplicating or migrating any data. A news app, a member portal, an event listing, or an internal knowledge base can all pull content directly from a Joomla installation that your team already manages through the Joomla admin panel.

The key requirement is Joomla 4 or higher. Joomla 3 ships without a native REST API, and adding a third-party REST extension is complex and poorly maintained. If your site runs Joomla 3, the right first step is migrating to Joomla 4 (Joomla provides an official migration pathway), then returning to this guide. On Joomla 4, the Web Services API is included but disabled by default — you activate it in the Plugin Manager in two clicks, generate a token for a user account, and your REST API is live at /api/index.php/v1/.

Joomla's REST responses follow the JSON:API specification, which wraps data in a structure like { data: [{ type: 'articles', id: '1', attributes: { title: '...', introtext: '...' } }] }. This is different from flat REST APIs, so FlutterFlow JSON Paths need to reference $.data[*].attributes.title rather than $.title. Once you understand that wrapper structure, reading Joomla articles into a FlutterFlow ListView is straightforward — and Joomla is free open-source software with no API usage fees, only your hosting costs.

## Before you start

- A Joomla 4 website (self-hosted or hosted) with administrator access — Joomla 3 does not have a native REST API
- The Web Services - Content and Web Services - Users API Authentication plugins enabled in Joomla Plugin Manager
- A Joomla user API token generated from Users > Manage > (select user) > Joomla API Token
- Your Joomla site URL accessible over HTTPS (the API requires HTTPS in most hosting configurations)
- A FlutterFlow project (Starter plan or higher for API Calls)

## Step-by-step guide

### 1. Enable the Joomla 4 Web Services API plugins

Log in to your Joomla administrator panel at yoursite.com/administrator. Navigate to System in the top menu, then click Plugin Manager under the Manage section. You need to enable two specific plugins before the REST API works. In the search bar, type Web Services and press Enter. You will see a list of plugins with names starting with 'Web Services -'. The most important ones are 'Web Services - Content' (for articles), 'Web Services - Users' (for user data), and 'API Authentication - Token' (required for token-based auth). Click each plugin's status icon (a red X or grey circle) to enable it — the icon turns green when active.

If you do not see 'API Authentication - Token' in the list, go back and search for 'API Authentication' separately. This plugin is essential — without it, the API returns a 401 Unauthorized error for all authenticated requests even if the token is correct.

After enabling the plugins, test that the API is live by visiting https://yoursite.com/api/index.php/v1/content/articles in your browser (replace yoursite.com with your real domain). You should see a JSON response — either a list of articles or an authentication error. An authentication error means the API endpoint is active (good); a 404 error means either the API path is wrong or your server's URL rewriting configuration is blocking the /api/ path. Some shared hosting setups rewrite or block unconventional URL patterns — if you get a 404, check with your host that the /api/ path passes through without redirection.

**Expected result:** Visiting https://yoursite.com/api/index.php/v1/content/articles in a browser returns a JSON response (either article data or a 401 authentication error, both confirm the API is active).

### 2. Generate a Joomla user API token

In the Joomla administrator panel, go to Users in the top menu and click Manage. Find the user account you want to use for API access — ideally create a dedicated user for this purpose rather than using your admin account. Click that user's name to open their edit form.

Scroll down in the user edit form until you see the 'Joomla API Token' tab or section in the left sidebar (it appears as a sub-tab within the user editor). Click it. If you have never generated a token for this user, the field will be empty. Click 'Generate New Token' (the exact button label may vary by Joomla version). A long alphanumeric token string appears. Copy it immediately and store it securely — Joomla shows it once and does not redisplay it after you save.

If the token section does not appear in the user editor, the 'API Authentication - Token' plugin is not enabled — go back to the Plugin Manager and enable it, then return to this step. Once you have the token, do NOT paste it directly into FlutterFlow — you will pass it to a backend proxy or treat it as a secret variable. The token grants the same access level as the user account it belongs to, so a token for an administrator user can create, edit, and delete content through the API. Use a user with the minimum required permissions (typically Public or Registered for read-only content access).

**Expected result:** You have a Joomla API token (a long alphanumeric string) copied to a secure location, and the user it belongs to has appropriate access levels for the content your app will read.

### 3. Create the FlutterFlow API Calls group for your Joomla site

In FlutterFlow, click API Calls in the left navigation panel. Click + Add and choose Create API Group. Name the group something clear, such as JoomlaContent. Set the Base URL to https://yoursite.com/api/index.php/v1 — replace yoursite.com with your actual Joomla domain and do NOT include a trailing slash. The v1 path segment is part of the base URL, not the individual call path.

At the API Group level, go to the Headers tab and add the Authorization header that all Joomla API calls require. Set the header name to Authorization. For the value, you want to send Bearer {token} — but rather than hardcoding the token, create a variable: in the Variables tab of the API Group, add a variable called joomlaToken of type String. Then set the Authorization header value to Bearer {{ joomlaToken }}. This keeps the token out of the static header and lets the proxy step pass it in dynamically.

Now add an API Call inside the group: click + Add API Call and name it GetArticles. Set the HTTP Method to GET and the path to /content/articles. Switch to the Variables tab of this call and add optional query parameters: a filter_category_id (Integer) to filter by category, a page_limit (Integer, default 10) to control how many articles load at once, and a page_offset (Integer, default 0) for pagination. Reference them in the URL as query parameters using the Query Params section with {{ variableName }} syntax.

Open the Response & Test tab. Paste a sample Joomla API response (from your browser test of the live endpoint). Joomla returns JSON:API format — the structure is { 'links': {...}, 'data': [ { 'type': 'articles', 'id': '1', 'attributes': { 'title': '...', 'introtext': '...', 'catid': 8, 'created': '2024-01-01T12:00:00+00:00' } } ] }. Click Generate JSON Paths and enable paths for $.data[*].attributes.title, $.data[*].attributes.introtext, $.data[*].id, and $.data[*].attributes.created. Rename them to readable variable names: articleTitle, articleExcerpt, articleId, articleDate.

```
{
  "group_name": "JoomlaContent",
  "base_url": "https://yoursite.com/api/index.php/v1",
  "group_headers": {
    "Authorization": "Bearer {{ joomlaToken }}",
    "Content-Type": "application/json"
  },
  "calls": [
    {
      "name": "GetArticles",
      "method": "GET",
      "path": "/content/articles",
      "query_params": {
        "filter[category_id]": "{{ categoryId }}",
        "page[limit]": "{{ pageLimit }}",
        "page[offset]": "{{ pageOffset }}"
      }
    },
    {
      "name": "GetCategories",
      "method": "GET",
      "path": "/categories",
      "query_params": {
        "filter[extension]": "com_content"
      }
    }
  ]
}
```

**Expected result:** The API Calls panel shows the JoomlaContent group with a GetArticles call. The Response & Test tab shows JSON Paths for article title, excerpt, ID, and date.

### 4. Parse the JSON:API response and bind to a ListView

Joomla's JSON:API response structure is the trickiest part of this integration. Every item is nested under a data array, and every field is inside an attributes object. This means the JSON Paths FlutterFlow's auto-generator suggests need careful verification. The correct path for article title is $.data[*].attributes.title — not $.title, not $.data[*].title. If you use the wrong path, the response variable will bind as null and your ListView will appear empty even when the API returns data.

Once your JSON Paths are correctly set and the Test tab shows real article titles populating the response variables, switch to your screen in FlutterFlow. Place a ListView widget on the page. Open the ListView's properties and set its Data Source to a Backend Query. Select your JoomlaContent API Group, then GetArticles call. Expand the variable fields: set joomlaToken to your token value (temporarily for testing; you will move this to the proxy step), set pageLimit to 10, and leave pageOffset at 0 for the initial load.

Inside the ListView, add a Card widget with a Column containing two Text widgets. Select the first Text widget and click the orange variable chip next to its text field. Choose Backend Query Result > articleTitle. Select the second Text widget and bind it to articleExcerpt. The ListView will repeat this card template for each article in the API response array.

For the article detail screen: add a new screen with a Text widget for the full article body. When the user taps a card in the ListView, navigate to the detail screen and pass the articleId as a page parameter. On the detail screen, create a one-time backend query to /content/articles/{id} using the passed ID, then bind $.data.attributes.text (the full article body) to the Text widget.

```
// Joomla JSON:API response structure example
// GET /api/index.php/v1/content/articles
{
  "links": { "self": "https://yoursite.com/api/index.php/v1/content/articles" },
  "data": [
    {
      "type": "articles",
      "id": "42",
      "attributes": {
        "title": "Welcome to Our Community",
        "alias": "welcome-to-our-community",
        "introtext": "<p>This is the article excerpt...</p>",
        "catid": 8,
        "created": "2024-03-15T10:30:00+00:00",
        "state": 1
      }
    }
  ]
}

// Correct FlutterFlow JSON Paths:
// $.data[*].attributes.title      → article title (list)
// $.data[*].attributes.introtext  → excerpt (list)
// $.data[*].id                    → article ID (list)
// $.data[*].attributes.created    → date string (list)
// $.data[*].attributes.catid      → category ID (list)
```

**Expected result:** The ListView on your screen shows real Joomla article titles and excerpts populating from the API. Tapping a card navigates to a detail screen with the full article.

### 5. Secure write operations with a backend proxy

Reading public Joomla articles on the client side is generally acceptable — the content is public, and the API token with read-only permissions has limited damage potential. However, any write operation (POST to create an article, PATCH to update content, DELETE to remove an item) uses the same token. If that token ships in your compiled Flutter app, anyone who installs the app can extract it and write content to your Joomla site.

For write operations, the correct architecture is a Firebase Cloud Function or Supabase Edge Function that accepts parameters from your FlutterFlow app, validates them, adds the Authorization: Bearer {token} header from the server environment, and forwards the request to your Joomla site. Your FlutterFlow app calls the proxy function URL instead of Joomla directly.

In Firebase: store the Joomla token using firebase functions:config:set joomla.token=yourtoken. In the Cloud Function, read it via functions.config().joomla.token and construct the request with axios or node-fetch. In Supabase: store the token in Edge Functions > Secrets as JOOMLA_API_TOKEN and read it with Deno.env.get('JOOMLA_API_TOKEN').

For the read-only GET calls, you have two options: keep them client-direct (acceptable for public content with a limited-permission user token) or route them through the proxy too (better security, adds latency). If your Joomla content is public and the API user has no write permissions, the client-direct read pattern is a reasonable tradeoff for a simpler setup. Make the decision based on your token's permission level.

```
// Supabase Edge Function proxy for Joomla writes (index.ts)
import { serve } from 'https://deno.land/std@0.177.0/http/server.ts';

serve(async (req) => {
  const corsHeaders = {
    'Access-Control-Allow-Origin': '*',
    'Access-Control-Allow-Headers': 'authorization, content-type'
  };

  if (req.method === 'OPTIONS') {
    return new Response('ok', { headers: corsHeaders });
  }

  const joomlaToken = Deno.env.get('JOOMLA_API_TOKEN');
  const joomlaBaseUrl = Deno.env.get('JOOMLA_BASE_URL');

  const body = await req.json();
  const { endpoint, method, payload } = body;

  // Allowlist write endpoints
  const allowedEndpoints = ['/content/articles', '/content/articles/'];
  const isAllowed = allowedEndpoints.some(e => endpoint.startsWith(e));
  if (!isAllowed) {
    return new Response(JSON.stringify({ error: 'Endpoint not permitted' }), {
      status: 403,
      headers: { ...corsHeaders, 'Content-Type': 'application/json' }
    });
  }

  const response = await fetch(`${joomlaBaseUrl}/api/index.php/v1${endpoint}`, {
    method: method || 'GET',
    headers: {
      'Authorization': `Bearer ${joomlaToken}`,
      'Content-Type': 'application/json'
    },
    body: payload ? JSON.stringify(payload) : undefined
  });

  const data = await response.json();
  return new Response(JSON.stringify(data), {
    headers: { ...corsHeaders, 'Content-Type': 'application/json' }
  });
});
```

**Expected result:** The Supabase Edge Function or Firebase Cloud Function is deployed and returns Joomla content when called from Postman with a POST body specifying the endpoint and payload. The Joomla API token does not appear in any FlutterFlow-side code.

## Best practices

- Always verify you are running Joomla 4 or higher before attempting this integration — Joomla 3 has no native REST API and requires a third-party extension that is poorly maintained.
- Create a dedicated Joomla API user with the minimum access level required (Registered for public content, Author for content creation) rather than using your administrator account's token.
- Use JSON:API-correct paths in FlutterFlow ($.data[*].attributes.fieldName) for all Joomla API responses — flat paths always return null with Joomla's response format.
- Route any POST, PATCH, or DELETE operations through a Firebase Cloud Function or Supabase Edge Function proxy to keep the Joomla API token off the client device.
- Test the Joomla API URL directly in a browser before configuring FlutterFlow to confirm the endpoint is reachable, plugins are enabled, and URL rewriting is not blocking the /api/ path.
- Add pagination support using Joomla's page[limit] and page[offset] query parameters from the start — unparameterized article lists will eventually grow too large for a single API response.
- Handle the case where introtext contains HTML: use a flutter_html Custom Widget for rich content display or strip tags in a Dart Custom Action for plain-text excerpts.
- Monitor your Joomla server logs for API call volumes — Joomla has no built-in API rate limiting, but your hosting provider's server may throttle high-frequency requests.

## Use cases

### Mobile news feed for a community or association website

A FlutterFlow app that surfaces articles from a Joomla-powered association website as a mobile news feed. Members install the app, see a ListView of the latest articles from their chosen category, tap to read the full introtext, and share items. The content is authored entirely in the Joomla admin panel by existing editorial staff — no new backend is needed.

Prompt example:

```
Build a news feed screen showing the 10 latest articles from a specific Joomla category. Each list item should display the article title, publication date, and a short excerpt. Tapping an item opens a detail screen with the full article text.
```

### Internal knowledge base app for a company intranet built on Joomla

A FlutterFlow mobile app that gives employees access to internal Joomla documentation, policies, and procedure articles from their phones. A search bar queries the /content/articles endpoint with a keyword filter. Articles are organized by Joomla categories displayed as tabs at the top of the screen.

Prompt example:

```
Create a searchable knowledge base screen with a text input at the top. When the user types a search term, call the Joomla API to find matching articles and display results in a list with title and category label.
```

### Event listing app driven by Joomla article categories

A FlutterFlow app that reads Joomla articles tagged under an Events category to display a filterable list of upcoming events. Custom fields in Joomla articles store the event date and location. The app reads those fields from the JSON:API attributes object and displays them in event cards sorted by date.

Prompt example:

```
Build an events screen that lists articles from the Joomla 'Events' category. Each card should show the event title, a short description from the introtext field, and an event date from a custom field. Sort cards by newest first.
```

## Troubleshooting

### 401 Unauthorized on all API calls even with the correct token

Cause: The 'API Authentication - Token' plugin in Joomla is either not enabled or was disabled. Without this plugin active, Joomla does not recognize Bearer token authentication and rejects all API requests with a 401.

Solution: Log in to the Joomla administrator panel and go to System > Plugin Manager. Search for 'API Authentication' and confirm the 'API Authentication - Token' plugin is enabled (green status icon). If it was disabled, enable it, save, and retry your FlutterFlow API call. Also confirm the Authorization header in FlutterFlow includes the word Bearer followed by a space before the token — Bearer yourtoken, not just yourtoken.

### API returns data in the Test tab but response variables are null when bound to widgets

Cause: JSON Paths are using flat field names (like $.title or $.name) instead of the JSON:API nested structure ($.data[*].attributes.title). Joomla's REST API wraps all fields under a data array of objects each containing an attributes object.

Solution: Open the Response & Test tab of your API Call, paste a fresh JSON response from the Joomla API, and click Generate JSON Paths. Verify that all enabled paths start with $.data[*].attributes. for list calls or $.data.attributes. for single-item calls. Delete any paths that use flat field names and recreate them with the correct nested structure. After saving, retest and confirm the response variables show real values before binding to widgets.

### 404 Not Found when calling https://yoursite.com/api/index.php/v1/content/articles

Cause: Either the Web Services plugins are not enabled in Joomla, the site runs Joomla 3 (which has no native REST API), or the server's URL rewriting rules are intercepting the /api/ path and returning a page-not-found response.

Solution: First, confirm your Joomla version: in the admin panel go to Help > System Information and check the Joomla Version field. If it shows 3.x, upgrade to Joomla 4 before proceeding. If it shows 4.x, go to the Plugin Manager and confirm both 'Web Services - Content' and 'API Authentication - Token' are enabled. If plugins are enabled but you still get 404, add a .htaccess rule that passes through the /api/ path: RewriteCond %{REQUEST_URI} ^/api/ [NC] then RewriteRule .* - [L]. Contact your hosting provider if you cannot modify .htaccess.

### The introtext field contains HTML tags that appear as raw text in the FlutterFlow widget

Cause: Joomla stores article content as HTML, and FlutterFlow's standard Text widget renders the string literally, showing <p>, <strong>, and <br> tags as visible characters instead of formatting.

Solution: To display formatted Joomla article content, add the flutter_html pub.dev package via a Custom Widget. In FlutterFlow, go to Custom Code > + Add > Widget, declare flutter_html as a dependency, and write a minimal HtmlWidget that accepts an htmlContent String parameter. Use this Custom Widget anywhere you want to render Joomla article bodies. Alternatively, strip HTML tags in a Custom Action using a Dart regex before binding to a standard Text widget if formatting is not important.

```
// Dart Custom Widget for HTML rendering (flutter_html)
import 'package:flutter_html/flutter_html.dart';

class HtmlContentWidget extends StatelessWidget {
  final String htmlContent;
  const HtmlContentWidget({required this.htmlContent});

  @override
  Widget build(BuildContext context) {
    return Html(data: htmlContent);
  }
}
```

## Frequently asked questions

### Does this integration work with Joomla 3?

No. Joomla 3 does not include a native Web Services REST API. Third-party REST API extensions exist for Joomla 3 but they are poorly maintained and use different URL structures. If your site runs Joomla 3, the recommended path is to migrate to Joomla 4 using Joomla's official upgrade tool, then follow this guide. Check your version at Help > System Information in the Joomla admin panel.

### Is it safe to put the Joomla API token directly in FlutterFlow for read-only content?

For truly public content (articles with public access level, read-only operations), storing the token client-side is a lower-risk tradeoff because the same content is publicly accessible without a token. However, the token is still tied to a user account that could have broader permissions, and exposing it allows anyone to make authenticated API calls at your server's expense. The safer default is always to use a backend proxy, especially if the token belongs to an account that can write content.

### Why are my JSON Paths returning null even though the Test tab shows data?

Joomla uses the JSON:API specification, which nests all fields under data[].attributes rather than at the top level. A path like $.title does not exist in a Joomla response — the correct path is $.data[*].attributes.title for list results or $.data.attributes.title for a single article. Regenerate your JSON Paths from a fresh pasted response and ensure all paths include the .attributes. segment.

### Can I create or update Joomla articles from the FlutterFlow app?

Yes. The Joomla 4 REST API supports POST to create articles and PATCH to update them. However, these operations require the API token to have at least Author-level permissions, and that token must never ship in the compiled app. Route all write operations through a Firebase Cloud Function or Supabase Edge Function proxy that holds the token server-side and forwards authenticated requests to your Joomla API.

### Can I filter articles by category in the FlutterFlow API Call?

Yes. Add a filter[category_id] query parameter to your GetArticles API Call using Joomla's bracket query parameter notation. In FlutterFlow, set this as a variable (categoryId of type Integer) and pass the Joomla category ID when triggering the call. You can also filter by tag, author, or publication state using similar filter[] parameters — check Joomla's official Web Services API documentation for the full list of supported filters.

---

Source: https://www.rapidevelopers.com/flutterflow-integrations/joomla
© RapidDev — https://www.rapidevelopers.com/flutterflow-integrations/joomla
