---
title: "Feature: Seed definition format"
manual: "Data Factory"
version: "main"
source: "Changelog/1.0/Feature-SeedDefinitionFormat.rst"
rendered: "2026-09-27T14:36:23+00:00"
---

# Feature: Seed definition format {#feature-seed-definition-format}

## Description {#description}

A **seed set** describes the content of a TYPO3 installation in YAML. It is a
directory `Configuration/DataFactory/<name>/` inside any active extension, and
it is made of two kinds of file.

`config.yml` describes the **set**:

**packages/my_extension/Configuration/DataFactory/demo/config.yml**

```yaml
identifier: demo
title: 'Demo page tree'
description: 'Pages, content elements and the site they are reachable through.'

scenarios:
  - Scenario.yaml

files:
  - identifier: placeholder
    source: 'Files/placeholder.svg'
    folder: 'demo'

references:
  - file: placeholder
    table: tt_content
    uid: 2000
    field: assets

sites:
  - identifier: main
    rootPage: 1000
```

Every key set of the descriptor is **closed** \- at the top level, on a
`files` entry, on a `references` entry and on a `sites` entry
alike. An unknown key is refused, naming the known ones, because
`scenario:` instead of `scenarios:` would otherwise be an import
that reports success and writes nothing. `identifier`, `title` and
`scenarios` are required.

The scenario files it names describe the **records**, in the YAML scenario
format of `typo3/testing-framework`:

**packages/my_extension/Configuration/DataFactory/demo/Scenario.yaml**

```yaml
entitySettings:
  '*':
    nodeColumnName: 'pid'
    columnNames: {id: 'uid', language: 'sys_language_uid'}
    defaultValues: {pid: 0}
  page:
    isNode: true
    tableName: 'pages'
    parentColumnName: 'pid'
    languageColumnNames: ['l10n_parent', 'l10n_source']
    defaultValues: {hidden: 0, doktype: 1}
  content:
    tableName: 'tt_content'
    columnNames: {title: 'header', type: 'CType'}
    defaultValues: {hidden: 0, colPos: 0}

entities:
  page:
    - self: {id: 1000, title: 'Demo', slug: '/', is_siteroot: 1}
      entities:
        content:
          - self: {id: 2000, title: 'A frontend to look at', type: 'header'}
      children:
        - self: {id: 1100, title: 'About', slug: '/about'}
          languageVariants:
            - self: {id: 1101, title: 'DE: Über uns', language: 1, slug: '/ueber-uns'}
```

That format is not this extension's invention: it is the one the TYPO3 Core
writes its own functional test fixtures in, and the scenario files shipped in
the core are worked examples of it.

Properties of the format:

-   `entitySettings` declares how a table is written - its name, its node
    and parent columns, its column aliases, its language columns, its default
    values and its value instructions. The entry `'*'` holds the defaults
    for the declared entities. An entity name is not a table name, so one table
    can be written under two names with different defaults.
-   `entities` declares the records. Everything inside `self:` that
    is not `id` is a field and is written as it stands, so a table needs
    no support in this extension to be seedable.
-   `children:` nests further records of the same entity through the
    parent column, and a nested `entities:` block puts records of other
    entities onto a node. Records come out in the order they are declared,
    whatever table they belong to.
-   `languageVariants:` is a first-class translation construct: the
    columns named by `languageColumnNames` are filled with the uids of
    the ancestors of the variant. A translation of a translation is built with
    the full chain and reaches the database without it - a `DataHandler`
    limitation TYPO3 Core documents on its own fixtures, and one the format
    documentation names.
-   `versionVariants:` writes a workspace version of a record, and
    `version:` in place of `self:` writes a record that only exists
    in a workspace.
-   `actions:` becomes a `DataHandler` command - `move`,
    `delete` and `discard` \- run after every record exists, so an
    action can name a record the same set creates.
-   **Every record has a uid before it is written**: the declared `id`,
    or a dynamic one from `10000` upwards. Every one of them is checked
    against the installation up front, and an import that would collide is
    refused with the records that are in the way.
-   `scenarios` may name several files. They are composed into **one**
    scenario: `entitySettings` merged with the later file winning,
    `entities` appended per entity name. One composed scenario rather
    than one per file, because the dynamic uids are handed out per scenario.
-   `imports` splits the descriptor itself over several files, merging
    the imported lists into the importing one. A resource that cannot be read
    fails the import rather than being skipped.

## Impact {#impact}

Extensions can ship the content an installation starts from, and that content is
reviewable, versioned and reproducible like any other part of the repository -
in a format that a TYPO3 developer has most likely already written by hand, for
a functional test.

The complete reference is in the [Configuration](../../Configuration/Index.html#configuration) chapter.
