EXT:headless 

Version

main

Language

en

Description

Headless allows you to render JSON from TYPO3 content. You can customize output by changing types, names and nesting of fields.

This extension provides the backend part (JSON API) for TYPO3 PWA solution. The frontend part exists as JavaScript application nuxt-typo3 which consumes the JSON API and renders the content using Nuxt framework of VueJS. You can find the frontend documentation here.

Keywords

headless, json, api

Copyright

2020 by TYPO3 Association

Authors
  • Tymoteusz Motylewski (Macopedia)
  • Łukasz Uznański (Macopedia)
  • Adam Marcinkowski (Macopedia)
  • Vaclav Janoch (ITplusX)
Email

extensions@macopedia.pl

License

This extension is published under the GNU General Public License v2.0

Rendered

Tue, 07 Oct 2025 10:07:16 +0000

Feedback & Credits

If you have any questions just drop a line in our #initiative-pwa Slack channel.

Special thanks goes to macopedia.com company, which is sponsoring development of this solution.

TYPO3

The content of this document is related to TYPO3 CMS, a GNU/GPL CMS/Framework available from typo3.org .

For Contributors

You are welcome to help improve this guide if you missing something. Just click on "Edit me on GitHub" on the top right to submit your change request or report a problem

Table of Contents

Introduction 

This extension is designed to facilitate seamless integration between TYPO3 and modern frontend frameworks by providing a robust and flexible headless CMS solution. Its stable release, it ensures reliable performance and multiple features.

What does it do? 

The headless extension provides a JSON API that serves as an endpoint for various types of applications. It utilizes standard TYPO3 features to render the page tree structure and page content into JSON format. The JSON response object and content elements can be customized using TypoScript.

Features

  • JSON API for content elements
  • JSON API for navigation page tree structure
  • Comprehensive support for language and translation configurations (e.g., fallback)
  • Easy extensibility with custom fields or custom Content Elements (CEs)
  • Integration support for EXT:felogin and EXT:form
  • Integration support for EXT:news (see additional extension EXT:headless_news)
  • Integration support for EXT:solr (EXT:headless_solr)
  • Integration support for EXT:powermail (EXT:headless_powermail)
  • Integration support for EXT:gridelements (EXT:headless_gridelements)
  • Integration support for EXT:container (EXT:headless_container)

Additional Benefits

  • Performance Optimization: The headless architecture enhances performance by delivering only the necessary data to the client, reducing load times and improving the user experience.
  • Scalability: Easily scale your application by decoupling the backend from the frontend, allowing for independent scaling based on demand.
  • Flexibility: Provides the flexibility to integrate with various frontend frameworks and libraries, enabling developers to choose the best tools for their specific use case.
  • Future-Proofing: Stay ahead of the curve with a modern headless CMS approach, ensuring your application remains relevant and adaptable to future technological advancements.

This extension is designed to empower developers to build dynamic, responsive, and high-performing web applications while leveraging the powerful features of TYPO3. Whether you are building a single-page application (SPA), a progressive web app (PWA), or any other type of frontend application, this extension provides the tools you need to succeed.

Installation 

To install and configure the headless extension, follow these detailed steps. First, ensure TYPO3 is installed and running (refer to t3install:start for the standard TYPO3 installation process). Once TYPO3 is operational, proceed with the following steps to install the headless extension:

  1. Install and activate the extension: - Using Composer (recommended): Run the following command in your terminal: `` composer require friendsoftypo3/headless ` - Alternatively, use the Extension Manager with the extension key headless`.
  2. Create a new root page: - This page will serve as the JSON API endpoint for your application. - Open the TYPO3 backend, navigate to the page tree, and create a new page at the root level. Name this page appropriately (e.g., "API Root").

    Root page for the API endpoint
  3. Create a new root template: - In the TYPO3 backend, go to Site Management > TypoScript (or Web > Template for versions prior to v12). - Add the headless static template by including it in the "Include static (from extensions)" section. - Save your changes. You should now be able to see the JSON output when displaying the page.
  4. Create a site configuration: - The site configuration is essential as it defines the URL that will serve as the API endpoint for the frontend application. - In TYPO3 v10 and above, an autogenerated site configuration may already exist. You can rename and configure this site to suit your needs.

Configuration 

Feature Flags 

To modify the settings for this extension, you can use either LocalConfiguration.php or AdditionalConfiguration.php.

headless.storageProxy

Enables the ability to set a storage proxy in the site configuration (and its variants) & serve files via proxy from the same domain.

Feature flag requires TYPO3 >= 10.4.10.

WARNING If you install TYPO3 >= 10.4.18, please update ext:headless to version >= 2.5.3.

$GLOBALS['TYPO3_CONF_VARS']['SYS']['features']['headless.storageProxy'] = true;
Copied!

headless.redirectMiddlewares

Enables new and replaces core middlewares for handling redirects. Headless mode requires redirects to be handled by the frontend app.

$GLOBALS['TYPO3_CONF_VARS']['SYS']['features']['headless.redirectMiddlewares'] = true;
Copied!

To enable headless support for EXT:redirect, add the following flag to your site configuration's YAML file:

headless: true
Copied!

headless.nextMajor

Enables new APIs/behaviors of ext:headless, which may contain breaking changes and require an upgrade path for your application. Use with caution.

$GLOBALS['TYPO3_CONF_VARS']['SYS']['features']['headless.nextMajor'] = true;
Copied!

headless.elementBodyResponse

Available since 2.6.

Enables clean output middleware for plugins. Clean output is available for POST/PUT/DELETE method requests. To get clean output for plugins on a page, enable this flag and add headless to the site configuration, then send the responseElementId field with the ID of the plugin in the body with plugin data.

LocalConfiguration.php

$GLOBALS['TYPO3_CONF_VARS']['SYS']['features']['headless.elementBodyResponse'] = true;
Copied!

Site configuration:

Add the following flag to your site configuration's YAML file:

headless: true
Copied!

Example POST request with plugin form. Replace #ELEMENT_ID# with the ID of the plugin from the page response.

POST https://example.tld/path-to-form-plugin
Content-Type: application/x-www-form-urlencoded

responseElementId=#ELEMENT_ID#&tx_form_formframework[email]=email&tx_form_formframework[name]=test...
Copied!

To find a nested element, use the new flag responseElementRecursive, where responseElementId is the child (nested element). Example request:

POST https://example.tld/path-to-form-plugin
Content-Type: application/x-www-form-urlencoded

responseElementId=#ELEMENT_ID#&responseElementRecursive=1&tx_form_formframework[email]=email&tx_form_formframework[name]=test...
Copied!

headless.simplifiedLinkTarget

Available since 2.6.

Enables simplified target links' property.

$GLOBALS['TYPO3_CONF_VARS']['SYS']['features']['headless.simplifiedLinkTarget'] = true;
Copied!

Simplified output returns only the value, e.g., _blank for the target attribute instead of the HTML string target="_blank".

headless.jsonViewModule

Available since 3.0.

Enables the experimental JsonView backend module, which allows previewing the page JSON response in the backend module when passing specific pageType, pageArguments, usergroups, language.

This flag requires an additional extension friendsoftypo3/headless-dev-tools.

$GLOBALS['TYPO3_CONF_VARS']['SYS']['features']['headless.jsonViewModule'] = true;
Copied!

headless.workspaces

Enables EXT:workspaces preview support.

$GLOBALS['TYPO3_CONF_VARS']['SYS']['features']['headless.workspaces'] = true;
Copied!

To enable headless support for EXT:workspaces, add the following flag to your site configuration's YAML file:

headless: true
Copied!

headless.pageTitleProviders

Enables support for the PageTitle API.

$GLOBALS['TYPO3_CONF_VARS']['SYS']['features']['headless.pageTitleProviders'] = true;
Copied!

Availability of Feature Toggles by Version 

Flag

2.x

3.x

4.x

FrontendBaseUrlInPagePreview

available

removed

removed

headless.frontendUrls

>= 2.5

available

removed

headless.storageProxy

>= 2.4

available

available

headless.redirectMiddlewares

>= 2.5

available

available

headless.nextMajor

>= 2.2

currently not used

currently not used

headless.elementBodyResponse

>= 2.6

available

available

headless.simplifiedLinkTarget

>= 2.6

removed

not available

headless.jsonViewModule

not available

>= 3.0

>= 3.0

headless.workspaces

not available

>= 3.1

>= 3.1

headless.pageTitleProviders

not available

not available

>= 4.2.3

EXT:form 

If EXT:form is enabled in the TYPO3 instance, EXT:headless provides support for handling forms in headless mode.

Standard forms designed in the form editor in TYPO3 backend should work out of the box, but the headless extension supports additional small tweaks/features to help frontend developers better handle forms on their end.

All options are added in YAML files with standard form configuration in TYPO3.

i18n

In many cases in headless mode, frontend developers need some translated strings for common elements like buttons, help messages, etc.

With EXT:headless, you can add additional configuration in the root line of the form config:

i18n:
  identifier: 'i18n'
  properties:
     someButtonLabel: 'Submit or Cancel'
     someHelpMessage: 'You need to fill out this form'
     requiredFields: 'These fields are required'
Copied!

The above block will be automatically translated by provided XLF files like a standard form in fluid.

This block will be translated & available in the "i18n" part of the response. More about form output can be found in the Form Decorator section.

Form Decorator

The headless extension provides an out-of-the-box simple decorator for form definition output. The decorator simplifies the response and provides an API to customize your response for your specific needs.

In the rendering options of the form, you can define your custom project/form decorator. If the option is not defined, the headless extension defaults to:

FriendsOfTYPO3\Headless\Form\Decorator\FormDefinitionDecorator

You can override this at any time by specifying it in the form's config YAML:

renderingOptions:
  formDecorator: Your-Vendor\YourExtension\Form\CustomDefinitionDecorator
Copied!

More about form output decorators can be found in customize form output.

Validators

To help frontend developers create validation handling in a frontend context, you can add small tweaks to form element definitions to ease development for your frontend team.

In the form element definition, you can add an option to errorMessage for your defined validators with the error code value. This code will be picked up and translated by standard TYPO3 XLF form files.

For example:

renderables:
   -
      type: 'Page'
      identifier: 'page-1'
      label: 'Step'
      renderables:
         -
            properties:
               options:
                  Mr: 'Mr'
                  Mrs: 'Mrs'
               elementDescription: ''
               fluidAdditionalAttributes:
                  required: required
            type: 'RadioButton'
            identifier: 'salutation'
            label: 'Salutation'
            validators:
               -
                 identifier: 'NotEmpty'
                 errorMessage: 1221560910
Copied!

When creating a RegexValidator, there are some differences when handling regular expressions by PHP & JS. To help frontend developers create consistent frontend/backend validation, we introduced a small option for regex validators in TYPO3.

For example:

renderables:
   -
      type: 'Page'
      renderables:
         -
           type: 'Text'
           identifier: 'testField'
           label: 'Test field'
           validators:
             -
                identifier: 'RegularExpression'
                options:
                  regularExpression: '/^[a-z]+$/'
                FERegularExpression:
                  expression: '^[a-z]+$'
                  flags: 'i'
                errorMessage: 1221565130
Copied!

If the headless form decorator finds the option FERegularExpression in the validator definition, it will override options.regularExpression with the value of the FERegularExpression option before sending the output to the frontend developer.

Custom Options

When you need a select/radio/checkbox with custom options fetched from, for example, a database or another external source, you need to create a Custom FormModel. In headless mode, we do not render HTML and render all the options, so we introduced a small interface:

FriendsOfTYPO3\Headless\Form\CustomOptionsInterface

and customOptions in the definition of the form element:

- defaultValue: ''
  type: 'SingleSelectWithCountryList'
  identifier: 'country'
  label: 'Country'
  properties:
     customOptions: 'YourVendor\Your-Ext\Domain\Model\YourCustomOptionClassImplementingInterface'
Copied!

When the above option is set with a class that implements the correct interface, the options of the select element will be replaced by the values returned by the specified class.

To make rendering of the element easier for frontend developers, we introduced the option to override the type returned to the frontend developer. For example, when you set FEOverrideType in the renderingOptions of a custom element:

type: 'SingleSelectWithCountryList'
renderingOptions:
  FEOverrideType: 'Select'
Copied!

We use this value to override the type, so the response to the frontend developer will be:

{
  "type": "Select"
}
Copied!

instead of:

{
  "type": "SingleSelectWithCountryList"
}
Copied!

JSON Redirect

EXT:headless supports handling finishers. For example, after handling correctly sent form data, you can use TYPO3 core's RedirectFinisher to redirect to a thank you page. To have more control on the frontend side, we provide in the headless extension:

JsonRedirectFinisher

This is based on the core RedirectFinisher but, instead of delay & statusCode options, has an option for a message that can be handled by the frontend developer to display a message for the user before redirecting to the defined page.

Also, JsonRedirect does not redirect by itself but generates a message (default is null) and URI for redirection by the frontend developer.

To use JsonRedirect, define it in the setup.yaml of your extension form's setup:

TYPO3:
  CMS:
    Form:
      prototypes:
        standard:
          finishersDefinition:
            JsonRedirect:
              implementationClassName: 'FriendsOfTYPO3\Headless\Form\Finisher\JsonRedirectFinisher'
Copied!

[BETA] JsonView Backend Module 

JsonView Module icon with label

The JsonView module is an experimental approach for previewing JSON responses of a page in different contexts like pageType, page arguments, usergroup, language, and show/hide hidden content.

!WARNING This is an experimental module, please don't use it on a production environment at this time.

Root page for the API endpoint Root page for the API endpoint

PageTypeModes

You can set the context in which you want to preview a page.

By default, there are 3 settings available:

  • default - standard response with page data and content
  • initialData - standard response from pageType=834
  • detailNews (commented out) - example of calling the detail action of the news extension for test purposes
pageTypeModes:
  default:
    title: Default page view
    pageType: 0
    bootContent: 1
    parserClassname: FriendsOfTYPO3\Headless\Service\Parser\PageJsonParser

  initialData:
    title: Initial Data
    pageType: 834
    parserClassname: FriendsOfTYPO3\Headless\Service\Parser\DefaultJsonParser

#  Example of detail news preset
#
#  detailNews:
#    title: Detail news
#    pageType: 0
#    bootContent: 1
#    arguments:
#      tx_news_pi1:
#        action: detail
#        controller: News
#        news: 1
Copied!

Custom YAML Configuration

You can always create your own YAML configuration and set it in the extension configuration.

Root page for the API endpoint

Content element categories 

Headless default configuration for content element categories in TypoScript lib.contentElement is to look in root (page uid=0). However depending on your project needs this may not ideal.

(as of TYPO3 v12 you cannot use together pidInList = root and recursive = 99)

Alternative approach for categories may be to use categories from current rootPage which mitigates this problem.

To begin you need to unset previous pidInList value

lib.contentElement.fields.categories.10.select.pidInList >
Copied!

If you need categories from current rootPage

lib.contentElement.fields.categories.10.select.pidInList >
lib.contentElement.fields.categories.10.select.pidInList.data = leveluid : 0
Copied!

You can add 'recursive' if categories may be stored under current rootPage

lib.contentElement.fields.categories.10.select.pidInList >
lib.contentElement.fields.categories.10.select.pidInList.data = leveluid : 0
lib.contentElement.fields.categories.10.select.recursive = 99
Copied!

Preview Hidden Pages 

How to configure your application to preview hidden pages? 

The headless extension supports the preview functionality for hidden pages. However, proper configuration is required to ensure your application works as expected.

Since there is no concept of cross-domain cookies, ensure that both your backend and frontend servers share the same root domain (e.g., backend: api.domain.com, frontend: domain.com). Then, set the root domain as cookieDomain (note the dot at the beginning).

$GLOBALS['TYPO3_CONF_VARS']['BE']['cookieDomain'] = '.domain.com';
Copied!

If your frontend application correctly passes all cookies from the backend, you should be able to preview content associated with the root domain.

(Optional) Make the backend application available via a proxy, e.g., domain.com/headless. This step is not strictly necessary for the application to run but can help resolve various issues in the long run.

XML Sitemap 

Since ext:headless 4.0 there's no custom code regarding the XML sitemaps apart from the templates used to render the XML sitemap's source code.

Troubleshooting 

In case the URLs listed in the "sitemap index" file (usually /sitemap.xml) aren't "frontend URLs" (but their API variant), consider setting one of the two following configurations:

  • either frontendApiProxy being defined in the site's config.yaml
  • or this in the site's settings.yaml:
headless:
  sitemap:
    key: frontendBase
Copied!

Developer 

This chapter will explain different usecases for developer working with headless extension.

New cObjects 

EXT:headless comes with a bunch of new cObjects to be used via TypoScript:

  • BOOL
  • FLOAT
  • INT
  • JSON
  • CONTENT_JSON

BOOL, FLOAT and INT are basically like TEXT (with value and stdWrap properties!) but make sure their result is being cast to bool, float or int.

JSON 

To build and render a JSON object into your page output.

lib.meta = JSON
lib.meta {
  if.isTrue = 1
  fields {
    title = TEXT
    title {
      field = seo_title
      stdWrap.ifEmpty.cObject = TEXT
      stdWrap.ifEmpty.cObject {
        field = title
      }
    }
    robots {
      fields {
        noIndex = BOOL
        noIndex.field = no_index
      }
    }
    ogImage = TEXT
    ogImage {
      dataProcessing {
        10 = FriendsOfTYPO3\Headless\DataProcessing\FilesProcessor
        10 {
          as = media
          references.fieldName = og_image
          processingConfiguration {
            returnFlattenObject = 1
          }
        }
      }
    }
  }
  dataProcessing {
  }
  stdWrap {
  }
}
Copied!

The JSON cObjects comes with a bunch of properties: if, fields, dataProcessing and stdWrap.

Property `if`

Can be used to decide whether or not to render the object.

Property `fields`

Array of cObjects. With special properties per item:

  • intval/floatval/boolval to cast the result to int, float or bool.
  • ifEmptyReturnNull to return null in case it's empty
  • ifEmptyUnsetKey to remove the item in case it's empty
  • dataProcessing to render data processors (have a look at lib.meta.ogImage for example)

Property `dataProcessing`

This property can be used to render data processors such as MenuProcessor.

Property `stdWrap`

This property can be used to run stdWrap to the already rendered JSON output.

CONTENT_JSON 

This cObject basically behaves like TYPO3's CONTENT, the main difference is that content elements are grouped by colPol & encoded into JSON by default.

CONTENT_JSON has the same options as CONTENT but also offers two new options for edge cases in json context.

merge

This option allows to generate another CONTENT_JSON call in one definition & then merge both results into one dataset (useful for handling slide feature of CONTENT cObject).

lib.content = CONTENT_JSON
lib.content {
  table = tt_content
  select {
    orderBy = sorting
    where = {#colPos} != 1
  }
  merge {
    table = tt_content
    select {
      orderBy = sorting
      where = {#colPos} = 1
    }
    slide = -1
  }
}
Copied!

doNotGroupByColPos = 0(default)|1

This option allows to return a flat array (without grouping by colPos) but still encoded into JSON.

lib.content = CONTENT_JSON
lib.content {
  table = tt_content
  select {
    orderBy = sorting
    where = {#colPos} != 1
  }
  doNotGroupByColPos = 1
}
Copied!

Internal Extbase plugins 

To integrate a custom frontend plugin which returns its data inside the JSON object, we have to do the following:

Follow the standard proceeding to register and configure extbase plugins:

Create the DemoController.php:

class DemoController extends \TYPO3\CMS\Extbase\Mvc\Controller\ActionController {
  public function indexAction() {
    return json_encode([
       'foo' => 'bar',
       'settings' => $this->settings
    ]);
  }
}
Copied!

Use the plugin through TypoScript:

tt_content.list =< lib.contentElementWithHeader
tt_content.list {
  fields {
    content {
      fields {
        data = CASE
        data {
          key.field = list_type
          demoplugin_type = USER
          demoplugin_type {
            userFunc = TYPO3\CMS\Extbase\Core\Bootstrap->run
            vendorName = Vendor
            extensionName = ExtName
            pluginName = DemoPlugin
            controller = Demo
            settings {
              test = TEXT
              test.value = The demo is working
            }
          }
        }
      }
    }
  }
}
Copied!

Clear the cache and in the response we will see the following JSON output (shortened):

{
  "content": {
    "colPos0": [{
      "type": "demoplugin_type",
      "appearance": {...},
      "content": {
        "data": {
          "foo": "bar",
          "test": {
            "value": "The demo is working",
            "_typoScriptNodeValue": "TEXT"
          }
        }
      }
    }]
  }
}
Copied!

Integrating external plugins 

The integration of other extension plugins is pretty simple. We're providing the headless_news extension as an example of how it works.

Main part is a user function definition to run a plugin from TypoScript:

tt_content.list =< lib.contentElementWithHeader
tt_content.list {
  fields {
    content {
      fields {
        data = CASE
        data {
          key.field = list_type
          news_pi1 = USER
          news_pi1 {
            userFunc = TYPO3\CMS\Extbase\Core\Bootstrap->run
            vendorName = GeorgRinger
            extensionName = News
            pluginName = Pi1
            controller = News
            view < plugin.tx_news.view
            persistence < plugin.tx_news.persistence
            settings < plugin.tx_news.settings
          }
        }
      }
    }
  }
}
Copied!

For any other plugin, just change the vendorName, extensionName, pluginName and controller options, and import needed constant and setup values (like for view, persistence and settings in this case).

Then use the constants of that extension to overwrite the paths to the fluid templates:

plugin.tx_news {
  view {
    templateRootPath = EXT:headless_news/Resources/Private/News/Templates/
    partialRootPath = EXT:headless_news/Resources/Private/News/Partials/
    layoutRootPath = EXT:headless_news/Resources/Private/News/Layouts/
  }
}
Copied!

As last step we need to re-implement the template logic to generate JSON instead of HTML structure. We do this by creating Fluid templates at the location specified in the previous step.

Because we don't enforce any standard for the JSON structure, we are pretty free here to adjust the structure to our needs (or to the requests of our frontend developer).

Here is the shortened List.html template which generates news items into a JSON array:

<f:spaceless>
  {"list": [<f:for each="{news}" as="newsItem" iteration="newsIterator">
  <f:if condition="{settings.excludeAlreadyDisplayedNews}">
    <f:then>
      <n:format.nothing>
        <n:excludeDisplayedNews newsItem="{newsItem}"/>
      </n:format.nothing>
    </f:then>
  </f:if>
  <f:render section="NewsListView" arguments="{newsItem: newsItem,settings:settings,iterator:iterator}" />
    {f:if(condition: newsIterator.isLast, else: ',')}
  </f:for>],
  "settings":
  <f:format.raw>
    <f:format.json value="{
      orderBy: settings.orderBy,
      orderDirection: settings.orderDirection,
      templateLayout: settings.templateLayout,
      action: 'list'
    }"/>
  </f:format.raw>
  }
</f:spaceless>
Copied!

Create custom content elements 

To add custom content elements we can straight follow the native approach of TYPO3 and fluid_styled_content.

The only difference to make it work with headless is the configuration of the frontend template in TypoScript. There is an overwritten content object reference in lib.contentElement which we can use, as well as an extended object with a header definition lib.contentElementWithHeader:

tt_content.demo >
tt_content.demo =< lib.contentElementWithHeader
tt_content.demo {
  fields {
    content {
      fields {
        demoField = TEXT
        demoField.value = This is a demo content-element
        bodytext = TEXT
        bodytext {
          field = bodytext
          parseFunc =< lib.parseFunc_RTE
        }
        demoSubfields {
          fields {
            demoSubfield = TEXT
            demoSubfield.value = Nested field
          }
        }
      }
    }
  }
}
Copied!

The definition of fields can be nested until various depth to reflect our desired JSON structure. Also the use of dataProcessing is possible the native way like in any other content elements (see content element definitions of this extension).

Create custom TypoScript 

To add a default TypoScript object (such as CONTENT) to the fields of your page object you need to make sure to render it a valid JSON.

Here's an example of how you can create a JSON array of multiple objects from a custom DB table:

lib.page {
  fields {
    related = CONTENT
    related {
      table = tx_myextension_domain_model_things
      select {
        pidInList = this
      }
      renderObj = JSON
      renderObj {
        fields {
          title = TEXT
          title.field = title
          link = TEXT
          link.typolink.parameter.field = uid
          link.typolink.returnLast = url
        }
        # Add recognizable token at the end of this item
        stdWrap.wrap = |###BREAK###
      }
      stdWrap {
        # Wrap items into square brackets
        innerWrap = [|]

        # Replace 'inner tokens' by comma, remove others
        split {
          token = ###BREAK###
          cObjNum = 1 |*|2|*| 3
          1 {
            current = 1
            stdWrap.wrap = |
          }

          2 < .1
          2.stdWrap.wrap = ,|

          3 < .1
        }
      }
    }
  }
}
Copied!

Meta data override 

Here's an example of how to override the meta object by data from a DB record:

lib.meta.stdWrap.override.cObject = JSON
lib.meta.stdWrap.override.cObject {
  if.isTrue.data = GP:tx_news_pi1|news
  dataProcessing.10 = FriendsOfTYPO3\Headless\DataProcessing\DatabaseQueryProcessor
  dataProcessing.10 {
    table = tx_news_domain_model_news
    uidInList.data = GP:tx_news_pi1|news
    uidInList.intval = 1
    pidInList = 0
    max = 1
    as = records
    fields < lib.meta.fields
    fields {
      title = TEXT
      title.field = title
      subtitle = TEXT
      subtitle.field = teaser
      description = TEXT
      description.field = bodytext
    }

    returnFlattenObject = 1
  }
}
Copied!

TypoScript DataProcessors 

This extension provides a couple of handy DataProcessors.

For further information have a look into the default TypoScript to see them in action.

EXT:form & form output decorators 

EXT:headless out of box provides for developers:

  • FriendsOfTYPO3\Headless\Form\Decorator\FormDefinitionDecorator
  • FriendsOfTYPO3\Headless\Form\Decorator\AbstractFormDefinitionDecorator
  • FriendsOfTYPO3\Headless\Form\Decorator\DefinitionDecoratorInterface

FormDefinitionDecorator is default decorator and outputs

form: {
  id: "ContactForm-1",
  api: {
    status: null,
    errors: null,
    actionAfterSuccess: null,
      page: {
        current: 0,
        nextPage: null,
        pages: 1
      }
  },
  i18n: {},
  elements: []
}
Copied!

You can anytime extend & customize for your needs simply by creating a custom decorator which implements DefinitionDecoratorInterface or extend the provided AbstractFormDefinitionDecorator which provides the ability to override the definition of each element or the whole form definition.

After creating a custom decorator you can attach it to your form simply by setting formDecorator in the rendering options of the form, see more

Snippets 

See issue #136

Data Processors 

DatabaseQueryProcessor 

It's the EXT:headless equivalent of TYPO3's own DatabaseQueryProcessor.

10 = FriendsOfTYPO3\Headless\DataProcessing\DatabaseQueryProcessor
10 {
  table = tt_content
  pidInList = 123
  as = contents
  fields {
    header = TEXT
    header {
      field = header
    }
    bodytext = TEXT
    bodytext {
      field = bodytext
      parseFunc =< lib.parseFunc_RTE
    }
  }
}
Copied!

Apart from the properties of TYPO3's DatabaseQueryProcessor (if, table, as and dataProcessing) it provides the following properties:

  • fields
  • overrideFields
  • returnFlattenObject (Default: 0)
  • returnFlattenLegacy (Default: 1)

ExtractPropertyProcessor 

Extract a single (maybe nested) property from a given array.

Example see below in section on FilesProcessor.

FilesProcessor 

lib.meta.fields.ogImage = TEXT
lib.meta.fields.ogImage {
  dataProcessing {
    # Use the column 'og_image' to render an array with all relevant
    # information (such as the publicUrl)
    10 = FriendsOfTYPO3\Headless\DataProcessing\FilesProcessor
    10.as = media
    10.references.fieldName = og_image
    10.processingConfiguration.returnFlattenObject = 1

    # Extract only property 'publicUrl' from the above created array
    20 = FriendsOfTYPO3\Headless\DataProcessing\ExtractPropertyProcessor
    20.key = media.publicUrl
    20.as = media
  }
}
Copied!

FlexFormProcessor 

This DataProcessor allows to process a flexform field such as tt_content.pi_flexform and optionally override its property values.

10 = FriendsOfTYPO3\Headless\DataProcessing\FlexFormProcessor
10 {
  fieldName = pi_flexform
  as = flexform
  overrideFields {
    fieldA = TEXT
    fieldA {
      value = 123
    }
  }
}
Copied!

GalleryProcessor 

See Images GalleryProcessor

RootSiteProcessor 

10 = FriendsOfTYPO3\Headless\DataProcessing\RootSitesProcessor
10 {
   as = sites
   # allow to override provider of data for output processor, if empty defaults to FriendsOfTYPO3\Headless\DataProcessing\RootSiteProcessing\SiteProvider
   # your-class implementing FriendsOfTYPO3\Headless\DataProcessing\RootSiteProcessing\SiteProviderInterface
   # example value: Vendor\Project\RootSiteProcessing\CustomSiteProvider
   siteProvider =
   # allow to override output of processor, if empty defaults to FriendsOfTYPO3\Headless\DataProcessing\RootSiteProcessing\SiteSchema
   # your-class implementing FriendsOfTYPO3\Headless\DataProcessing\RootSiteProcessing\SiteSchemaInterface
   # example value: Vendor\Project\RootSiteProcessing\CustomSiteSchema
   siteSchema =
   # provider configuration, if empty defaults to 'sorting' field from pages table
   # example value = custom_sorting
   sortingField =
   # if empty defaults to sort by "sorting" field from `pages` table
   # your-class implementing FriendsOfTYPO3\Headless\DataProcessing\RootSiteProcessing\SiteSortingInterface
   # example value: Vendor\Project\RootSiteProcessing\CustomSorting
   sortingImplementation =
   # list of uid of root pages should be returned, i.e. you have 5 root pages(1,2,3,4,5), but two (4,5) of not ready to display, so you can hide it
   # example value = 1,2,3
   allowedSites =
   # automatically fetch root sites from another page/separator and filter sites yaml configs by returned list from database
   # very useful when you have multi site setup in one instance.
   # example value = 1
   sitesFromPid =
   # if empty defaults to uid,title,sorting - list of columns to fetch from database and provided for SiteSchema/DomainSchema to use
   # example value = uid,title,sorting
   dbColumns =
   # if empty defaults to "title" field from pages table, get site name from database
   # example value = your-custom-field-from-pages-table
   titleField =
}
Copied!

Image rendering 

FileUtility 

The file rendering in EXT:headless is handled by FileUtility which renders the following JSON for each file:

"publicUrl": "https://www.example.org/fileadmin/_processed_/6/c/csm_my-image_51125112.jpg"
"properties": {
  "mimeType": "image/jpeg",
  "type": "image",
  "filename": "csm_my-image_51125112.jpg",
  "link": null,
  "linkData": null,
  "originalUrl": "https://www.example.org/fileadmin/my-image.jpg",
  "uidLocal": "123",
  "fileReferenceUid": "234",
  "size": "50 KB",
  "dimensions": {
    "width": "300",
    "height": "100",
  },
  "cropDimensions": {
    "width": "300",
    "height": "100",
  },
  "crop": { ... },
  "autoplay": null,
  "extension": null
}
Copied!

The file rendering can be simplified via legacyReturn = 0 processing configuration flag

lib.meta {
  fields {
    ogImage = TEXT
    ogImage {
      dataProcessing {
        10 = FriendsOfTYPO3\Headless\DataProcessing\FilesProcessor
        10 {
          as = media
          references.fieldName = og_image
          processingConfiguration {
            legacyReturn = 0
          }
        }
      }
    }
  }
}
Copied!

Will output:

"url": "https://www.example.org/fileadmin/_processed_/6/c/csm_my-image_51125112.jpg"
"mimeType": "image/jpeg",
"type": "image",
"filename": "csm_my-image_51125112.jpg",
"originalUrl": "https://www.example.org/fileadmin/my-image.jpg",
"link": null,
"uidLocal": "123",
"fileReferenceUid": "234",
"size": "50 KB",
"dimensions": {
   "width": "300",
    "height": "100",
},
"cropDimensions": {
  "width": "300",
  "height": "100",
},
"crop": { ... },
"autoplay": null,
"extension": null
Copied!

EnrichFileDataEvent 

FileUtility is emitting the event EnrichFileDataEvent for manipulating the properties array.

To add a listener add this to your Configuration/Services.yaml:

My\Extension\EventListener\TweakFileData:
  tags:
    - name: event.listener
      identifier: 'tweak-file-data'
      event: FriendsOfTYPO3\Headless\Event\EnrichFileDataEvent
Copied!

FilesProcessor 

headless provides its own FilesProcessor to render files.

Here's an example of how the og_image of a page is being rendered via TypoScript:

lib.meta {
  fields {
    ogImage = TEXT
    ogImage {
      dataProcessing {
        10 = FriendsOfTYPO3\Headless\DataProcessing\FilesProcessor
        10 {
          as = media
          references.fieldName = og_image
          processingConfiguration {
            returnFlattenObject = 1
          }
        }
      }
    }
  }
}
Copied!

Configuration 

The rendering configuration can be set via the property processingConfiguration and provides the following sub-properties:

  • legacyReturn (0|1): Allows to control new simplified output or old system (old system by default)
  • linkResult (0|1): Allows to define if file object should return only url of defined link or whole LinkResult object
  • cacheBusting (0|1): Allows to enable cacheBusting urls for processed files
  • conditionalCropVariant (0|1): Allows conditionally autogenerate files with defined variants if set (if not all variants are returned)
  • processPdfAsImage (0|1): Enabled optional processing pdf files as image (default off)
  • processSvg (0|1): Enabled optional processing svg files (default off)
  • properties.byType (0|1): Allows filter file properties by type (i.e. do not return video properties on images)
  • properties.defaultFieldsByType (coma separated list of fields): Default fields for when enabled option properties.byType
  • properties.defaultImageFields (coma separated list of fields): Default fields for image type when enabled option properties.byType
  • properties.defaultVideoFields (coma separated list of fields): Default fields for video type when enabled option properties.byType
  • properties.includeOnly (string, comma separated): Configure what file properties to return
  • properties.flatten (0|1): Flatten nested properties (dimensions array) to use with properties.includeOnly
  • returnFlattenObject: without that flag an array of (multiple) images is rendered. Set this if you're only rendering 1 image and want to reduce nesting.
  • delayProcessing: can be used to skip processing of images (and have them simply collected with the FilesProcessor), in order to have them processed by the next processor in line (which is generally GalleryProcessor).
  • fileExtension: can be used to convert the images to any desired format, e.g. webp.
  • autogenerate: `retina2x`: set this to render an additional image URI in high quality (200%). lqip: set this to render an additional image URI with low quality (10%). * also custom defined size & file formats see example below
10 = FriendsOfTYPO3\Headless\DataProcessing\FilesProcessor
10 {
   ...
   processingConfiguration {
      delayProcessing = 1
   }
}
20 = FriendsOfTYPO3\Headless\DataProcessing\GalleryProcessor
20 {
   ...
   autogenerate {
      retina2x = 1
      customFileWebp {
         fileExtension = webp
         factor = 1.0
      }
      customTinyJpg {
         fileExtension = jpg
         factor = 0.2
      }
   }
}
Copied!
10 = FriendsOfTYPO3\Headless\DataProcessing\FilesProcessor
10 {
   ...
   processingConfiguration {
      # (1 by default, return new format of file object)
      legacyReturn = 0
      # Return whole LinkResult object instead simple url
      linkResult = 1
      # check if we need to conditionally check if we should generate crop variants
      conditionalCropVariant = 1
      # Generate cacheBusting urls for images and video files
      cacheBusting = 1
      properties {
         # return props by mimeType
         byType = 1
         # return only properties defined below
         includeOnly = alternative,width,height
         # you can also alias fields
         # includeOnly = alternative as alt,width,height
         # with includeOnly you can use option `flatten` to flatten dimensions array
         flatten = 1
      }
   }
}
Copied!

GalleryProcessor 

Configuration 

The rendering configuration can be set directly. No processingConfiguration property available!

  • maxGalleryWidth: set to the core constant {$styles.content.textmedia.maxW}
  • maxGalleryWidthInText: set to the core constant {$styles.content.textmedia.maxWInText}
  • columnSpacing: set to the core constant {$styles.content.textmedia.columnSpacing}
  • borderWidth: set to the core constant {$styles.content.textmedia.borderWidth}
  • borderPadding: set to the core constant {$styles.content.textmedia.borderPadding}
  • autogenerate `retina2x` lqip

FAQ 

How to use EXT:felogin? 

Using EXT:felogin with the headless extension follows the standard setup as detailed in the felogin documentation.

To test the login functionality without a frontend interface, you can use browser extensions such as RESTer (Firefox) or Insomnia REST Client (Chrome) to simulate form submissions. If the response contains a set-cookie header, the login was successful.

Does EXT:headless work with other extensions? 

Yes, the output of virtually any extension can be rendered into the JSON response. For detailed information, refer to the integration of external plugins section of this documentation. Additionally, you can review the code of headless_news as an example of how this integration works.

How to handle redirects in a headless setup? 

In a headless setup, redirects should be managed by the frontend application. You can enable and replace core middlewares for handling redirects by setting the headless.redirectMiddlewares feature flag:

$GLOBALS['TYPO3_CONF_VARS']['SYS']['features']['headless.redirectMiddlewares'] = true;
Copied!

Ensure that your frontend application is equipped to handle redirects as specified in the JSON responses.

Can I use custom fields or content elements with EXT:headless? 

Yes, EXT:headless supports the customization of JSON responses using TypoScript. You can define custom fields or content elements and extend the JSON output to include these customizations.

For example, to add a custom field, you can modify the TypoScript setup like this:

lib.customField = TEXT
lib.customField.value = My Custom Field
Copied!

This value can then be included in the JSON response as needed.

How to configure language and translation settings? 

EXT:headless fully supports TYPO3's language and translation configurations, including fallback settings. To configure languages, follow these steps:

  1. Define your languages in the site configuration YAML file.
  2. Ensure that your content elements and page properties are translated according to TYPO3's multilingual guidelines.

For example, in your site configuration:

languages:
  - title: English
    enabled: true
    base: /en/
    typo3Language: default
    locale: en_US.UTF-8
    iso-639-1: en
    navigationTitle: English
    hreflang: en
    direction: ltr
    flag: global
  - title: German
    enabled: true
    base: /de/
    typo3Language: de
    locale: de_DE.UTF-8
    iso-639-1: de
    navigationTitle: Deutsch
    hreflang: de
    direction: ltr
    flag: de
Copied!

The JSON API will respect these settings and provide the appropriate language versions of the content.

How to enable clean output for plugins in EXT:headless? 

To enable clean output middleware for plugins, which is available for POST/PUT/DELETE method requests, follow these steps:

  1. Set the headless.elementBodyResponse feature flag in LocalConfiguration.php:

    $GLOBALS['TYPO3_CONF_VARS']['SYS']['features']['headless.elementBodyResponse'] = true;
    Copied!
  2. Add the headless flag to your site configuration's YAML file:

    headless: true
    Copied!
  3. Send the responseElementId field with the ID of the plugin in the body of the plugin data during requests.

For example, a POST request might look like this:

POST https://example.tld/path-to-form-plugin
Content-Type: application/x-www-form-urlencoded

responseElementId=#ELEMENT_ID#&tx_form_formframework[email]=email&tx_form_formframework[name]=test...
Copied!

To handle nested elements, use the responseElementRecursive flag:

POST https://example.tld/path-to-form-plugin
Content-Type: application/x-www-form-urlencoded

responseElementId=#ELEMENT_ID#&responseElementRecursive=1&tx_form_formframework[email]=email&tx_form_formframework[name]=test...
Copied!

What is the JsonView backend module? 

The JsonView backend module is an experimental feature that allows previewing the JSON response of a page in different contexts such as pagetype, page arguments, usergroup, and language.

To enable the JsonView backend module, set the headless.jsonViewModule feature flag:

$GLOBALS['TYPO3_CONF_VARS']['SYS']['features']['headless.jsonViewModule'] = true;
Copied!

You can then preview the JSON response directly from the TYPO3 backend.

JsonView Module icon with label

Sitemap