Skip to main content

Topwire Features

Besides providing a seamles Hotwire Turbo integration, Topwire comes with additional concepts and an APIs, that gets you solve common tasks more quickly and elegantly. Have fun exploring the features.

Starting with Turbo Frames

Small effort, great result

Tick the Topwire checkbox of the content element, to wrap a TYPO3 form into a Turbo-Frame. With that, the form does not reload the whole page any more and your users stay on the page right where they left off.

This works with any third party plugin and without changing a single line of code, adding JavaScript or overriding Fluid templates.

Check out the message form example!

Leave a message

The message will be submitted, but nothing will be stored or forwarded. Mandatory fields are marked with *

Magic checkbox

This is a standard form with the TYPO3 form extension. No templates have been adjusted, only a checkbox is checked in the form content element.

All TypoScript it takes:

Connecting the checkbox in the content element with Topwire Frame functionality, requires a single line of TypoScript.

The second line is optional. When no custom frame id is specified, an id is automatically generated from the uid of the element.

TypoScript
# Wrap content elements with a Turbo frame, when checkbox is checked in the content element
tt_content.stdWrap.turboFrameWrap.field = tx_turbo_wrap_in_frame

# Read the frame id from a text field in the content element
tt_content.stdWrap.turboFrameWrap.frameId.field = tx_turbo_frame_id

Adding Turbo Frames to your Fluid templates

Render any plugin in any Fluid template

Topwire's context view helpers can be used to render plugins where you need them, without any additional configuration. Use the view helpers, specify the plugin and optionally even an action that should be rendered or the Fluid template section that should be rendered.

Fluid
<html
    xmlns:topwire="http://typo3.org/ns/Topwire/ViewHelpers"
    data-namespace-typo3-fluid="true">

<topwire:context.plugin 
    extensionName="MyExtension" 
    pluginName="MyPlugin" 
    action="list" 
    section="MySection"
>
    <topwire:context.render />
</topwire:context.plugin>

</html>

Add a Turbo Frame to third party plugins

Third party plugins can be rendered and scoped within a frame without touching any code or template.

Fluid
<html
    xmlns:topwire="http://typo3.org/ns/Topwire/ViewHelpers"
    data-namespace-typo3-fluid="true">

<topwire:context.plugin extensionName="FeLogin" pluginName="Login">
    <topwire:turbo.frame id="login" wrapResponse="true">
        <topwire:context.render />
    </topwire:turbo.frame>
</topwire:context.plugin>

</html>
Login Box

Any content element can be rendered in a frame

Content elements that are used anywhere in the TYPO3 installation, can be rendered in any Fluid template and be scoped in frame.

Fluid
<html
    xmlns:topwire="http://typo3.org/ns/Topwire/ViewHelpers"
    data-namespace-typo3-fluid="true">

<topwire:context.contentElement uid="44">
    <topwire:turbo.frame id="form-content" wrapResponse="true">
        <topwire:context.render />
    </topwire:turbo.frame>
</topwire:context.contentElement>

</html>
Form Content Element

Leave a message

The message will be submitted, but nothing will be stored or forwarded. Mandatory fields are marked with *

Every content that is rendered in a Turbo frame can be loaded asynchronously

Only the loading placeholder will be part of the fully rendered page and cached. The contents of the frame will be loaded asynchrounously by Hotwire Turbo. Very useful for content that must be always up to date and therefore can not be cached.

Fluid
<html
    xmlns:topwire="http://typo3.org/ns/Topwire/ViewHelpers"
    data-namespace-typo3-fluid="true">

<topwire:context.plugin extensionName="FeLogin" pluginName="Login">
    <topwire:turbo.frame id="login2" src="async" wrapResponse="true">
        <div class="d-flex justify-content-center m-5">
            <div class="spinner-border text-primary" role="status">
                <span class="visually-hidden">Loading...</span>
            </div>
        </div>
    </topwire:turbo.frame>
</topwire:context.plugin>

</html>
Login Box (async)
Loading...

Using Turbo frames in custom plugins

When developing your own plugins and Fluid templates, Turbo frames can be used to devide parts of the plugin into individual sections wrapped in frames.

Counter Demo
Fluid
<html
    xmlns:f="http://typo3.org/ns/TYPO3/CMS/Fluid/ViewHelpers"
    xmlns:topwire="http://typo3.org/ns/Topwire/ViewHelpers"
    data-namespace-typo3-fluid="true">

<topwire:context.plugin>
    <topwire:turbo.frame id="counter" morph="true">
        <f:render partial="Counter/Plain" arguments="{_all}"/>
    </topwire:turbo.frame>
</topwire:context.plugin>

</html>

Flexible and expressive Fluid view helpers

Rendering TypoScript snippets

You can use also use Topwire's context view helpers to render any defined TypoScript path and the rendered result will directly be embedded in the page. If needed, wrap the result with a Turbo frame view helper, declare it to be async and the result will be fetched on page load instead. That is very handy e.g. if the rendered content can not be cached with the rest of the page.

Fluid
<html
    xmlns:topwire="http://typo3.org/ns/Topwire/ViewHelpers"
    data-namespace-typo3-fluid="true">

<topwire:context.typoScript typoScriptPath="lib.sideBar">
    <!-- Directly embed the result of lib.sideBar -->
    <topwire:context.render />
</topwire:context.typoScript>

<topwire:context.typoScript typoScriptPath="lib.sideBar">
    <!-- Load the result of lib.sideBar after the full page is loaded -->
    <!-- The HTML here acts as temporary content (loading spinner) -->
    <f:turbo.frame src="async">
        Loading ...
    </f:turbo.frame>
</topwire:context.typoScript>

</html>

Link up your Extbase actions

Extbase standalone responses

Your Extbase controller code can simply return the desired response. In the Fluid template that is linking the action, you can easily declare that the linked action should not be embedded in a HTML page, done.

No more extra TypoScript page type delcaration, no additional middleware code, everything is handled transparently by Topwire.

Extbase Controller
<?php
declare(strict_types=1);
namespace Topwire\Examples\Controller;

use Psr\Http\Message\ResponseInterface;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Extbase\Mvc\Controller\ActionController;

class ExampleController extends ActionController
{
    protected function jsonAction(): ResponseInterface
    {
        $data = [
            'hello' => 'world',
        ];
        return $this->jsonResponse(json_encode($data, JSON_THROW_ON_ERROR))
            ->withHeader(
                'Content-Disposition',
                'attachment; filename="hello.json"'
            );
    }
}
Fluid
<html
    xmlns:f="http://typo3.org/ns/TYPO3/CMS/Fluid/ViewHelpers"
    xmlns:topwire="http://typo3.org/ns/Topwire/ViewHelpers"
    data-namespace-typo3-fluid="true">

<topwire:context.plugin pluginName="Json">
    <h3>Link to a download action in your plugin</h3>
    <f:link.action
        action="json"
        class="btn btn-primary"
        additionalParams="{topwire: {type: 'context'}}"
    >
        Download JSON
    </f:link.action>
</topwire:context.plugin>

</html>
Link to any plugins using default view helpers

Link to a download action in your plugin

Download JSON

Link anything from anywhere

TypoScript and PHP linking API

Sometimes it is necessary to link from Extbase actions from within PHP code, or from TypoScript.

Topwire got you covered! Declare what plugin action you want to link in additional arguments, and Topwire will transform it to an URI that only renders the contents of this action, without the surrounding HTML of the page.

TypoScript
lib.topwireLinkExample = TEXT
lib.topwireLinkExample.typolink.parameter.data = page:uid
lib.topwireLinkExample.typolink.topwire.type = plugin
lib.topwireLinkExample.typolink.topwire.extensionName = TopwireExamples
lib.topwireLinkExample.typolink.topwire.pluginName = Json
Extbase Controller
<?php
declare(strict_types=1);
namespace Topwire\Examples\Controller;

use Psr\Http\Message\ResponseInterface;
use TYPO3\CMS\Core\Http\RedirectResponse;
use TYPO3\CMS\Extbase\Mvc\Controller\ActionController;

class ExampleController extends ActionController
{
    protected function exampleAction(): ResponseInterface
    {
        $uri = $this->uriBuilder
            ->reset()
            ->setArguments([
                'topwire' => [
                    'type' => 'context',
                ]
            ])
            ->uriFor('json');
        return new RedirectResponse($uri, 303);
    }
}