---
title: "Extbase plugins"
manual: "Handlebars"
version: "main"
permalink: "https://docs.typo3.org/permalink/cpsit/typo3-handlebars:extbase-plugin@main"
source: "Usage/ExtbasePlugin.rst"
rendered: "2026-09-29T09:05:57+00:00"
---

# Extbase plugins {#extbase-plugin}

`HandlebarsViewFactory` integrates Handlebars rendering into the Extbase
MVC stack. It implements `\TYPO3\CMS\Core\View\ViewFactoryInterface` and
is wired globally, so every Extbase controller that goes through the standard
view factory mechanism automatically benefits from it without any code changes.

When the factory detects an Extbase request it reads the
`handlebars` key from the plugin's TypoScript configuration and
returns a `HandlebarsView`. If no `handlebars` key is present
and the controller does not extend `HandlebarsController`, the factory
falls back to the Fluid view.

-   [TypoScript configuration](https://docs.typo3.org/permalink/cpsit/typo3-handlebars:typoscript-configuration@main)
-   [Default template name](https://docs.typo3.org/permalink/cpsit/typo3-handlebars:default-template-name@main)
-   [HandlebarsController](https://docs.typo3.org/permalink/cpsit/typo3-handlebars:handlebarscontroller@main)
-   [Fluid fallback](https://docs.typo3.org/permalink/cpsit/typo3-handlebars:fluid-fallback@main)

## TypoScript configuration {#extbase-plugin-typoscript}

Per-plugin Handlebars configuration lives under the `handlebars`
key in the plugin's TypoScript configuration:

```typoscript
plugin.tx_myextension_myplugin {
    handlebars {
        default {
            templateRootPaths.10 = EXT:my_extension/Resources/Private/Templates/Handlebars
            partialRootPaths.10 = EXT:my_extension/Resources/Private/Partials/Handlebars
        }
    }
}
```

### Resolution keys {#extbase-plugin-typoscript-keys}

The `handlebars` array supports four keys, resolved and merged
from least to most specific so that narrower entries override broader ones:

| Key | Applies to |
| --- | --- |
| `default` | All controllers in the plugin |
| `<ControllerAlias>` | One controller, all actions |
| `<ControllerAlias>::<actionName>` | One controller, one action |
| `<ControllerFQCN>` | Controller matched by fully-qualified class name |

```typoscript
plugin.tx_myextension_myplugin {
    handlebars {
        default {
            templateRootPaths.10 = EXT:my_extension/Resources/Private/Templates/Handlebars
            partialRootPaths.10 = EXT:my_extension/Resources/Private/Partials/Handlebars
        }
        Blog {
            templateRootPaths.20 = EXT:my_extension/Resources/Private/Templates/Blog
        }
        Blog::list {
            templateName = @blog-list
        }
    }
}
```

The controller alias is derived by Extbase from the controller class name
registered in `ExtensionUtility::configurePlugin()`: it is the short
class name without the `Controller` suffix. For the example above,
`\Vendor\MyExtension\Controller\BlogController` has the alias
`Blog`.

### Properties {#extbase-plugin-typoscript-properties}

Each resolution key accepts all properties of a
[HANDLEBARSTEMPLATE](https://docs.typo3.org/permalink/cpsit/typo3-handlebars:content-object@main) content object, for example
`templateName`, `templateRootPaths`,
`variables` or `dataProcessing`. Variables assigned
in the controller are passed to the template as well and are available to
data processors via `processedData`. For HTML requests, the
template file extension defaults to `hbs`.

## Default template name {#extbase-plugin-default-template}

When no `templateName` is configured, the factory derives one
automatically from the controller alias and action name:

```text
<ControllerAlias>/<actionName>
```

For `BlogController::listAction()` with alias `Blog` this
resolves to `Blog/list.hbs` under the configured template root paths.

## HandlebarsController {#extbase-plugin-handlebars-controller}

For controllers you own, extend
`\CPSIT\Typo3Handlebars\Controller\HandlebarsController` instead of
`ActionController`. This guarantees Handlebars rendering even when no
`handlebars` TypoScript key is present — the factory always returns
a `HandlebarsView` for these controllers:

**EXT:my_extension/Classes/Controller/BlogController.php**

```php
namespace Vendor\MyExtension\Controller;

use CPSIT\Typo3Handlebars\Controller\HandlebarsController;
use Psr\Http\Message\ResponseInterface;

final class BlogController extends HandlebarsController
{
    public function listAction(): ResponseInterface
    {
        $this->view->assign('posts', $this->postRepository->findAll());

        return $this->htmlResponse($this->renderView());
    }
}
```

## Fluid fallback {#extbase-plugin-fluid-fallback}

During an incremental migration you may need to keep some actions on Fluid
while others are already on Handlebars. Call `delegateRendering()` on the
view to hand off to the underlying Fluid view for that action:

```php
public function legacyAction(): ResponseInterface
{
    $this->view->assign('items', $this->repository->findAll());

    $content = $this->view instanceof HandlebarsView
        ? $this->view->delegateRendering()
        : $this->view->render();

    return $this->htmlResponse((string)$content);
}
```

> [!NOTE]
> **See also**
>
> -   [HANDLEBARSTEMPLATE content object](https://docs.typo3.org/permalink/cpsit/typo3-handlebars:content-object@main) — full property reference for
>     `HANDLEBARSTEMPLATE`
> -   [Template paths](https://docs.typo3.org/permalink/cpsit/typo3-handlebars:template-paths@main) — how template and partial root paths are
>     collected and prioritised
