Modern websites frequently rely on AJAX (Asynchronous JavaScript and XML) to deliver dynamic, responsive user experiences without reloading the entire page. In TYPO3, we can harness the power of AJAX by setting up a dedicated typeNum endpoint in TypoScript and handling asynchronous calls with an Extbase controller. This blog post, a guide to mastering AJAX in TYPO3, will walk you through each step—from configuring TypoScript for AJAX to returning JSON responses and consuming them in your frontend JavaScript.
What is the short answer?
To add AJAX to TYPO3, expose an endpoint that returns JSON and call it with fetch() or XMLHttpRequest. Use a PAGE object with its own typeNum for a quick Extbase endpoint, a PSR-15 middleware for lightweight routes, or an Extbase action that returns jsonResponse(). All three work in TYPO3 v13.4 LTS and v14.
Key takeaways
- A typeNum creates a separate page type. With disableAllHeaderCode = 1 the response contains no page HTML.
- Do not set config.no_cache = 1 for the whole page type. The TypoScript reference recommends disabling the cache only for the object that needs it, for example with COA_INT.
- Extbase actions must return a ResponseInterface. $this->jsonResponse() is the documented shortcut for JSON.
- A PSR-15 middleware can answer before the page is rendered, but it gives you neither Extbase nor Fluid.
- Check access inside the endpoint. Since TYPO3 14.2, Extbase offers the #[Authorize] and #[RateLimit] attributes.
Tested with / valid for: TYPO3 v13.4 LTS and v14. Updated: November 2026
Which AJAX method fits my TYPO3 project?
Choose typeNum when you already have an Extbase plugin, a middleware for small standalone endpoints, and an Extbase JSON action when the logic belongs in a controller with repositories.
| Method | Works in | Best for | Caveat |
|---|---|---|---|
| PAGE with typeNum (TypoScript) | v13.4 LTS and v14 | Reusing an existing Extbase plugin as JSON endpoint | Goes through frontend rendering. Handle caching per object (COA_INT) instead of config.no_cache. |
| PSR-15 middleware | v13.4 LTS and v14 | Small, fast endpoints such as status checks | No Extbase, no Fluid. You do the routing check and input validation yourself. |
| Extbase action with jsonResponse() | v13.4 LTS and v14 | Business logic with repositories and models | The action must return a ResponseInterface. |
Sources: PAGE examples, Middlewares, ActionController in the official TYPO3 documentation.
Why Use AJAX in TYPO3?
- Improved User Experience: No need to reload the entire page—just update specific components.
- Better Performance: Fetch only necessary data, reducing server load and page load times.
- Modular and Maintainable: Keep your logic organized by isolating AJAX calls in a dedicated controller/action.
1. Create an AJAX Endpoint with TypoScript
The first step is to create a special PAGE object in TypoScript and assign a unique typeNum to handle your AJAX requests.
ajax_page = PAGE
ajax_page {
typeNum = 123456 # Unique number for the AJAX call
config {
disableAllHeaderCode = 1
additionalHeaders = Content-type:application/json
xhtml_cleaning = 0
admPanel = 0
debug = 0
no_cache = 1
}
10 = USER
10 {
userFunc = TYPO3\CMS\Extbase\Core\Bootstrap->run
extensionName = YourExtension
pluginName = YourPlugin
vendorName = YourVendor
controller = YourController
action = yourAjaxAction
}
} Key Points
1. typeNum: Assign a unique integer to distinguish this AJAX request from other TYPO3 page types.
2. disableAllHeaderCode: Ensures no extra HTML headers are included—ideal for JSON responses.
3. USER: Routes the request to your Extbase controller/action, which will process the request and return data.
2. Implement the Extbase Controller
Next, create (or update) an Extbase controller action specifically for handling the AJAX request. By leveraging the JsonView, TYPO3 can automatically return a JSON response, making it easy to consume in your JavaScript.
namespace Vendor\YourExtension\Controller;
use TYPO3\CMS\Extbase\Mvc\Controller\ActionController;
use TYPO3\CMS\Extbase\Mvc\View\JsonView;
class YourController extends ActionController
{
protected $defaultViewObjectName = JsonView::class; // Return JSON response
public function yourAjaxAction()
{
// Your business logic here
$data = [
'message' => 'Hello, this is a response from the AJAX call',
'status' => true,
];
// Assign data to the view for JSON output
$this->view->assign('value', $data);
}
} Key Points
1. JsonView: Automatically converts the assigned PHP array/object into JSON output.
2. Business Logic: Perform any server-side operations here—e.g., database queries, validations, or custom business rules.
3. Trigger the AJAX Request from JavaScript
Finally, you need a way to call the newly created AJAX endpoint from your frontend. Below is an example of using plain JavaScript (XMLHttpRequest); however, you can also use fetch() or jQuery’s $.ajax() if preferred.
// Create an XMLHttpRequest object
var xhr = new XMLHttpRequest();
// Define the URL (with the typeNum parameter)
var url = "/?type=123456"; // Replace '123456' with your actual typeNum
// Open the request
xhr.open("POST", url, true);
xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
// On successful response
xhr.onload = function() {
if (xhr.status >= 200 && xhr.status < 300) {
// Parse JSON response
var response = JSON.parse(xhr.responseText);
console.log(response); // Handle the server response data
} else {
console.error('Request failed. Status: ' + xhr.status);
}
};
// On request error
xhr.onerror = function() {
console.error('AJAX request failed');
};
// Data to send
var data = "param1=value1¶m2=value2";
// Send the request
xhr.send(data); Key Points
1. xhr.open("POST", url, true): Set the HTTP method (GET or POST), target URL, and asynchronous flag.
2. xhr.setRequestHeader(): (Optional) Use this to set content types or custom headers.
3. Response Handling: JSON responses can be parsed with JSON.parse() for easy access.
Free TYPO3 Guides
Read our free TYPO3 guides for developers and integrators, ready to use in your next project.
4. (Optional) Configure Clean URLs with Routing
For TYPO3 v9 and above, you can make your AJAX endpoint more user-friendly by setting up route enhancers. In your config.yaml, define a route for your typeNum:
routeEnhancers:
AjaxType:
type: Simple
defaultController: 'Vendor\YourExtension\Controller\YourController::yourAjaxAction'
requirements:
type: '\d+'
default: '' This lets you craft pretty URLs instead of using /?type=123456. Adjust your JavaScript request accordingly.
Real-World Example
A typical scenario might involve listing records from an extension such as one our TYPO3 extension Helpdesk use:
listAction = PAGE
listAction {
typeNum = 741852
config {
disableAllHeaderCode = 1
additionalHeaders = Content-type:application/json
xhtml_cleaning = 0
admPanel = 0
}
10 = USER
10 < styles.content.get
10 {
userFunc = TYPO3\CMS\Extbase\Core\Bootstrap->run
extensionName = NsHelpdesk
pluginName = Helpdesk
vendorName = NITSAN
controller = Tickets
switchableControllerActions {
Tickets {
1 = list
}
}
# Carry plugin settings along in the AJAX call
stdWrap.trim = 1
select {
where = list_type = "nshelpdesk_helpdesk"
}
renderObj < tt_content.list.20.nshelpdesk_helpdesk
}
} By referencing /?type=741852 (or your fancy route if configured), you can asynchronously fetch a list of support tickets from Helpdesk and handle them in your JavaScript code.
How do I return JSON from a middleware or an Extbase action?
Both alternatives skip the typeNum page type. A PSR-15 middleware answers early in the request, an Extbase action returns its JSON with jsonResponse().
Option A: PSR-15 middleware
TYPO3 implements PSR-15 middlewares for incoming requests. This example from the TYPO3 documentation returns JSON for the path /check:
<?php
declare(strict_types=1);
namespace MyVendor\MyExtension\Middleware;
use Psr\Http\Message\ResponseFactoryInterface;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\MiddlewareInterface;
use Psr\Http\Server\RequestHandlerInterface;
class StatusCheckMiddleware implements MiddlewareInterface
{
public function __construct(
private readonly ResponseFactoryInterface $responseFactory,
) {}
public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
{
if ($request->getRequestTarget() === '/check') {
$response = $this->responseFactory->createResponse()
->withHeader('Content-Type', 'application/json; charset=utf-8');
$response->getBody()->write(json_encode(['status' => 'ok']));
return $response;
}
return $handler->handle($request);
}
}
Register it in the frontend stack of EXT:my_extension/Configuration/RequestMiddlewares.php:
<?php
return [
'frontend' => [
'my-extension/status-check' => [
'target' => \MyVendor\MyExtension\Middleware\StatusCheckMiddleware::class,
],
],
];
Option B: Extbase action with jsonResponse()
Extbase controller actions must return a ResponseInterface, as the ActionController documentation states. For JSON, return the shortcut $this->jsonResponse() instead of the JsonView from the example above:
public function yourAjaxAction(): ResponseInterface
{
$data = ['message' => 'Hello from the AJAX call', 'status' => true];
return $this->jsonResponse(json_encode($data));
}
Update for the TypoScript example
Since TYPO3 12.3 you can register an Extbase plugin with the EXTBASEPLUGIN content object instead of USER with Bootstrap->run. The old way still works, as the changelog notes:
ajax_page.10 = EXTBASEPLUGIN
ajax_page.10.extensionName = YourExtension
ajax_page.10.pluginName = YourPlugin
Looking for ready-made functionality instead of writing your own? Browse our TYPO3 extensions. For related reading, see how to build a custom TYPO3 middleware and how to migrate switchable controller actions in Extbase.
How do I secure and cache an AJAX endpoint?
Treat every endpoint as a public URL: validate input, check who may call it, and cache only responses that are safe to share.
- Use GET for read-only requests and POST for requests that change data, and validate every parameter on the server.
- Since TYPO3 14.2, #[Authorize] can require a frontend login or user groups for an Extbase action.
- Since TYPO3 14.2, #[RateLimit] limits requests per IP address. It only works for uncached Extbase actions.
- Instead of config.no_cache = 1, wrap only the dynamic object in COA_INT, as the TypoScript reference shows.
Common mistakes and troubleshooting
- HTML around the JSON: set config.disableAllHeaderCode = 1 on the PAGE object.
- Browser treats the answer as HTML: send the header Content-type:application/json, or use jsonResponse() in Extbase.
- Endpoint returns the normal page: check that the typeNum is unique and the TypoScript is loaded.
- Old data after a change: the response was cached, so clear the cache and make the dynamic object uncached.
- Action fails with an error: make sure the action returns a ResponseInterface.
More questions
What is the difference between typeNum and a middleware?
A typeNum is a page type that TYPO3 renders through the frontend, so it can reuse plugins and TypoScript. A middleware runs earlier in the request and returns a response directly. It is lighter, but you build routing and validation yourself.
Do I need Extbase for AJAX in TYPO3?
No. A PSR-15 middleware can return JSON without Extbase, as the TYPO3 documentation shows. Extbase is useful when you want repositories, models and validation in a controller.
Can I use fetch() instead of XMLHttpRequest?
Yes. fetch() is a browser API and works with any TYPO3 endpoint that returns JSON. Point it at the same URL, for example /?type=123456, and read the answer with response.json().
Is config.no_cache = 1 required for AJAX?
No. Many tutorials set it, but the TypoScript reference advises disabling the cache only for the object that must stay dynamic, for example with COA_INT, and leaving other caches untouched.
Upgrade Playbook for TYPO3
Planning the move to a current TYPO3 version? Follow our step-by-step upgrade playbook.
Conclusion
Implementing AJAX in TYPO3 using TypoScript’s typeNum and Extbase is straightforward and powerful. By separating your AJAX logic into a dedicated controller action, you keep the system clean, modular, and easy to maintain. Whether you’re updating small components or building a full-fledged single-page application, TYPO3’s flexibility accommodates it all.
Got questions or unique use cases? Drop them in the comments or reach out to the T3Planet community. We’d love to hear how you’re using AJAX in your TYPO3 projects!
Happy AJAX-ing with TYPO3!