Skip to main content

File Structure

Organize your components for maintainability as your design system grows.

Basic Structure

Components live in Resources/Private/Components/ within your sitepackage.

Single-Part Components

Simple components like buttons go in a folder matching their name:

Components/
└── ui/
    └── Button/
        └── Button.html

Multi-Part Components

Composable components have a Root.html plus additional parts:

Components/
└── ui/
    └── Accordion/
        ├── Root.html      # Main wrapper
        ├── Item.html      # Individual accordion item
        ├── Trigger.html   # Clickable header
        └── Content.html   # Expandable content

As your component library grows, separate primitive building blocks from composed components:

Components/
├── ui/                         # Primitives (small, unopinionated)
│   ├── Button/
│   ├── Accordion/
│   ├── Dialog/
│   ├── Tooltip/
│   └── Alert/
│       ├── Root.html
│       ├── Icon.html
│       ├── Title.html
│       └── Content.html
├── Alert/                      # Composed component (opinionated)
│   └── Alert.html
└── RegisterDialog/             # Other composed components
    └── RegisterDialog.html

Why this split?

Template Path Configuration

Register both paths so ui/ components don't need the extra prefix:

$templatePaths->setTemplateRootPaths([
    // First: ui/ subfolder (so <ui:button> works, not <ui:ui.button>)
    ExtensionManagementUtility::extPath('my_sitepackage', 'Resources/Private/Components/ui'),
    // Second: root Components folder
    ExtensionManagementUtility::extPath('my_sitepackage', 'Resources/Private/Components'),
]);

Composed Components

Creating wrapper components reduces repetitive markup. Here's an Alert that composes the primitive parts:

Components/Alert/Alert.html:

<ui:prop name="title" type="string" />
<ui:prop name="text" type="string" optional="{true}" />
<ui:useProps name="ui:alert.root" props="{0: 'variant'}" />

<ui:alert.root class="{class}" variant="{variant}">
    <ui:alert.icon />
    <ui:alert.title>
        <h3>{title}</h3>
    </ui:alert.title>
    <f:if condition="{text}">
        <ui:alert.content>
            <p>{text}</p>
        </ui:alert.content>
    </f:if>
</ui:alert.root>

Usage:

<!-- Simple API for common cases -->
<ui:alert title="Heads up" text="Something important happened" variant="warning" />

<!-- Or use primitives directly for custom layouts -->
<ui:alert.root variant="error">
    <ui:alert.icon />
    <ui:alert.content> <strong>Error:</strong> Custom layout with multiple paragraphs... </ui:alert.content>
</ui:alert.root>

This pattern gives you both convenience and flexibility.