Skip to main content

Hydration

All props of each component that is needed for hydrating them the client side is stored in a HydrationRegistry class that renders a script tag in the document head via the core's AssetCollector. This creates a global FluidPrimitives window variable that can be used to initialize components on the client side.

Each component that uses the ui:ref ViewHelper automatically gets registered for hydration.

Initializing Components

To initialize components on the client side you can use the initAllComponentInstances function like this:

import { Collapsible } from 'fluid-primitives/primitives/collapsible';
import { initAllComponentInstances } from 'fluid-primitives';

(() => {
    initAllComponentInstances('collapsible', ({ props }) => {
        const collapsible = new Collapsible(props);
        collapsible.init();
        return collapsible;
    });
})();

This initializes all collapsible components on the page by extracting their props from the hydration data and passing them to the provided factory function where you can create and initialize the component instance. The initialized instance is then returned and stored in the FluidPrimitives.uncontrolledInstances window variable.

Ideally you include the initialization script in the root part of your component via the <f:asset.script> or <vite:asset> (Vite Asset Collector) ViewHelper, so its just loaded when you use the component.

Connecting Component Parts

On the Server with ui:ref

To connect parts of a component between server and client you can use the ui:ref ViewHelper.

This abstracts away verbose data attributes or classes to identifiy parts manually with something like myRootEl.querySelector('[my-complex-component-name-sub-part]'). Instead you can simply use the ComponentHydrator class to find parts by their name.

Inside of the Tooltip/Trigger.html template you would use the ui:ref ViewHelper like this:

<button {ui:ref(name: 'trigger' )}></button>

this would then result in

<button data-scope="tooltip" data-part="trigger" data-hydrate-tooltip="«rootId»"></button>

See more about ui:ref.

On the Client with ComponentHydrator

On the client side you can then use the ComponentHydrator class to find the part like this:

import { ComponentHydrator } from 'fluid-primitives';

const hydrator = new ComponentHydrator('tooltip', rootId);
const trigger = hydrator.getElement('trigger'); // or hydrator.getElements('trigger') for multiple elements

Most of the time you dont need the ComponentHydrator directly, because the Component base class already provides a getElement and getElements method that uses the ComponentHydrator internally.

Controlled Components

By default all components are uncontrolled, meaning that they manage their own state internally. If you want to control the state of a component from the outside, you can set the controlled prop to true. This will prevent the initAllComponentInstances function from initializing the component automatically. You then need to initialize the component manually.

Manually accessing hydration data with getHydrationData

For example when building a bigger custom component that uses one or more primitives internally, you might want to control the state of the primitives from the outside. In this case you would set the controlled prop to true and then initialize the primitives manually like this with the getHydrationData function inside your custom component:

import { Collapsible } from 'fluid-primitives/primitives/collapsible';
import { getHydrationData } from 'fluid-primitives';

export class MyCustomComponent {
    private collapsible: Collapsible;

    constructor(rootId: string) {
        const collapsibleData = getHydrationData('collapsible', `${rootId}-collapsible`);
        this.collapsible = new Collapsible({
            ...collapsibleData,
            onOpenChange: ({ open }) => {
                // do something when the collapsible is opened or closed
            },
        });
        this.collapsible.init();
    }
}