---
title: "Layouts and partials"
manual: "Handlebars"
version: "main"
permalink: "https://docs.typo3.org/permalink/cpsit/typo3-handlebars:migration-from-fluid-layouts@main"
source: "Guides/MigrationFromFluid/LayoutsAndPartials.rst"
rendered: "2026-09-29T09:05:57+00:00"
---

# Layouts and partials {#migration-from-fluid-layouts}

Fluid's layout system — `<f:layout>`, `<f:section>`, and
`<f:render section="...">` — is one of the first things developers look for
when switching template engines. EXT:handlebars ships an equivalent mechanism
implemented as the `extend`, `block`, and `content` helpers, modelled
after the [handlebars-layouts](https://github.com/shannonmoeller/handlebars-layouts)
convention.

-   [How the systems compare](https://docs.typo3.org/permalink/cpsit/typo3-handlebars:how-the-systems-compare@main)
-   [Side-by-side example](https://docs.typo3.org/permalink/cpsit/typo3-handlebars:side-by-side-example@main)
-   [Default content in blocks](https://docs.typo3.org/permalink/cpsit/typo3-handlebars:default-content-in-blocks@main)
-   [Appending and prepending content](https://docs.typo3.org/permalink/cpsit/typo3-handlebars:appending-and-prepending-content@main)
-   [Using partials without a layout](https://docs.typo3.org/permalink/cpsit/typo3-handlebars:using-partials-without-a-layout@main)

## How the systems compare {#migration-from-fluid-layouts-concept}

Fluid uses a **push** model: a child template declares which layout it inherits
and then *pushes* named sections into the layout's named slots. Handlebars uses
the same model, but the building blocks are ordinary helpers rather than
dedicated language constructs.

| Fluid | Handlebars |
| --- | --- |
| `<f:layout name="Default" />` | `{{#extend "default"}} … {{/extend}}` |
| `<f:render section="Main">` (in layout) | `{{#block "main"}} … {{/block}}` |
| `<f:section name="Main">` (in template) | `{{#content "main"}} … {{/content}}` |

The `content` helper supports an optional `mode` hash argument
(`replace` / `append` / `prepend`) that controls
how the child's content is merged with the layout block's default. `replace`
is the default and matches Fluid's behaviour.

## Side-by-side example {#migration-from-fluid-layouts-example}

**Layout file — Fluid**:

**EXT:my_extension/Resources/Private/Layouts/Default.html**

```html
<!DOCTYPE html>
<html>
<head>
    <f:render section="Head" optional="true" />
</head>
<body>
    <header>
        <f:render section="Header" />
    </header>
    <main>
        <f:render section="Main" />
    </main>
    <footer>
        <f:render section="Footer" optional="true" />
    </footer>
</body>
</html>
```

**Layout file — Handlebars**:

**EXT:my_extension/Resources/Private/Partials/default.hbs**

```handlebars
<!DOCTYPE html>
<html>
<head>
    {{#block "head"}}{{/block}}
</head>
<body>
    <header>
        {{#block "header"}}{{/block}}
    </header>
    <main>
        {{#block "main"}}{{/block}}
    </main>
    <footer>
        {{#block "footer"}}{{/block}}
    </footer>
</body>
</html>
```

---

**Child template — Fluid**:

**EXT:my_extension/Resources/Private/Templates/MyElement.html**

```html
<f:layout name="Default" />

<f:section name="Head">
    <title>{header}</title>
</f:section>

<f:section name="Header">
    <h1>{header}</h1>
</f:section>

<f:section name="Main">
    <p>{bodytext}</p>
</f:section>
```

**Child template — Handlebars**:

**EXT:my_extension/Resources/Private/Templates/my-element.hbs**

```handlebars
{{#extend "default"}}
    {{#content "head"}}
        <title>{{header}}</title>
    {{/content}}

    {{#content "header"}}
        <h1>{{header}}</h1>
    {{/content}}

    {{#content "main"}}
        <p>{{bodytext}}</p>
    {{/content}}
{{/extend}}
```

> [!NOTE]
> Handlebars layouts are resolved as *partials*, so the layout file must be
> placed in one of the configured partial root paths, not in the template root
> paths. By convention, `default.hbs` lives under
> `Resources/Private/Partials/`.

## Default content in blocks {#migration-from-fluid-layouts-default-content}

A `{{#block}}` can hold default markup that is used verbatim when
the child provides no matching `{{#content}}` for that slot. This is
the equivalent of Fluid's `optional="true"` on `<f:render section="...">`
combined with a fallback in the layout.

**EXT:my_extension/Resources/Private/Partials/default.hbs**

```handlebars
<footer>
    {{#block "footer"}}
        <p>&copy; {{year}} My Site</p>
    {{/block}}
</footer>
```

A child template that does not declare a `{{#content "footer"}}` block
will render the default copyright line automatically.

## Appending and prepending content {#migration-from-fluid-layouts-append-prepend}

The `mode` argument lets a child *add* to a block rather than replace
it. This has no direct Fluid equivalent and is often used for accumulating
`<script>` or `<link>` tags:

**EXT:my_extension/Resources/Private/Templates/my-element.hbs**

```handlebars
{{#extend "default"}}
    {{#content "head" mode="append"}}
        <link rel="stylesheet" href="/assets/my-element.css">
    {{/content}}

    {{#content "main"}}
        …
    {{/content}}
{{/extend}}
```

## Using partials without a layout {#migration-from-fluid-layouts-partials-only}

Not every template needs a full layout. Reusable snippets that were Fluid
partials map directly to Handlebars partials with no extra ceremony — just
create a `.hbs` file in the partial root path and include it with
`{{> Name}}`.

> [!NOTE]
> **See also**
>
> -   [Writing templates](https://docs.typo3.org/permalink/cpsit/typo3-handlebars:templates@main) — template names, layouts and built-in helpers
> -   [Partials](https://docs.typo3.org/permalink/cpsit/typo3-handlebars:migration-from-fluid-syntax-partials@main) — partial inclusion syntax
> -   [Template paths](https://docs.typo3.org/permalink/cpsit/typo3-handlebars:template-paths@main) — how to configure partial root paths
