Skip to main content

Context

Composable components need to share data between parts. Context makes this possible without prop drilling.

Every component has a {context} variable containing shared state from its parent parts.

What's in Context

By default, context includes:

<!-- In Accordion/Item.html -->
<div data-item-id="{context.rootId}">
    <!-- Access any prop from Accordion/Root.html -->
</div>

Exposing Props to Context

Mark a prop to be available in child components with context="{true}":

<!-- In Accordion/Item.html -->
<ui:prop name="value" type="string" context="{true}" />

Now {context.value} is available in Trigger.html and Content.html.

Accessing Other Components' Context

Need data from a sibling or ancestor component? Use ui:context:

<ui:card.root>
    <ui:dialog.root>
        <!-- Inside the dialog, access the card's context -->
        <ui:context name="card" as="cardContext">
            <p>Card ID: {cardContext.rootId}</p>
        </ui:context>
    </ui:dialog.root>
</ui:card.root>

Custom Context Classes

For complex logic, create a PHP context class. This keeps templates clean and logic testable.

Setup

  1. Create a class extending AbstractComponentContext
  2. Name it {ComponentName}Context (e.g., AccordionContext)
  3. Register the namespace in your ComponentCollection
public function getContextNamespaces(): array
{
    return [
        'MyVendor\\MySitepackage\\Components\\Contexts',
    ];
}

Example

<?php

declare(strict_types=1);

namespace MyVendor\MySitepackage\Components\Contexts;

use Jramke\FluidPrimitives\Contexts\AbstractComponentContext;

class AccordionContext extends AbstractComponentContext
{
    public function getItemCount(): int
    {
        // Access context data
        $items = $this->get('items') ?? [];
        return count($items);
    }

    public function getItemState(array $item): object
    {
        $value = $item['value'] ?? null;
        $disabled = $item['disabled'] ?? null;

        $defaultValue = $this->get('defaultValue') ?? [];
        $rootDisabled = $this->get('disabled') ?? false;

        return (object)[
            'expanded' => in_array($value, (array)$defaultValue, true),
            'disabled' => $disabled ?? $rootDisabled,
        ];
    }
}

In templates:

<!-- Simple getter (no arguments) -->
<span>Total items: {context.itemCount}</span>

<!-- Method with arguments - use ui:call -->
<f:variable name="itemState">{context -> ui:call(method: 'getItemState', arguments: {0: itemProps})}</f:variable>

Available Methods in Context Classes

$this->get('propName');              // Get a context value
$this->getAllVariables();            // Get all context variables
$this->getRenderingContext();        // Current Fluid rendering context
$this->getParentRenderingContext();  // Parent component's rendering context
$this->getRequest();                 // Current HTTP request

Exposing Context to Client

Need computed values on the client side? Use the #[ExposeToClient] attribute:

use Jramke\FluidPrimitives\Attributes\ExposeToClient;

class AccordionContext extends AbstractComponentContext
{
    #[ExposeToClient]
    public function getDefaultValue(): ?string
    {
        // This will be available in client-side props
        return $this->get('defaultOpen') ? $this->get('items')[0] : null;
    }

    #[ExposeToClient(name: 'customName')]
    public function getSomething(): mixed
    {
        // Available as 'customName' in props, not 'something'
        return 'value';
    }
}

Lifecycle Methods

Run code before or after component rendering:

class DialogContext extends AbstractComponentContext
{
    public function beforeRendering(): void
    {
        // Setup, modify parent context, etc.
    }

    public function afterRendering(string &$html): void
    {
        // Post-process HTML, cleanup, etc.
    }
}

Dependency Injection

Inject TYPO3 services into context classes:

use Symfony\Component\DependencyInjection\Attribute\Autoconfigure;
use TYPO3\CMS\Core\Page\PageRenderer;

#[Autoconfigure(public: true)]
class MyContext extends AbstractComponentContext
{
    public function __construct(
        protected readonly PageRenderer $pageRenderer,
    ) {}
}

The #[Autoconfigure(public: true)] attribute is required for the component renderer to instantiate your context.