Renderers

This page describes shared renderer APIs such as ComponentRenderer, LitRenderer, and FragmentRenderer, used by UI components. For predefined XML renderers, supported attributes, and component-specific examples, see the corresponding component documentation.

Renderers are particularly useful for:

  • Enhanced visual representation: They enable the inclusion of visual elements beyond plain text, such as icons, images, progress bars, or others, to make data more easily understandable and engaging. This can improve the overall user experience and clarity of the UI.

    radio button group renderer
  • Conditional rendering: Renderers can implement logic to change the visual appearance based on the data’s value or other contextual information. For instance, a renderer might display a green checkmark for successful records and a red checkmark for failed ones.

    renderer 2
  • Complex layouts: For situations requiring more complex visual layouts than the default rendering offers, custom renderers provide the flexibility to arrange and display data in non-standard ways. This could involve combining several UI elements within a single component cell or creating entirely custom layouts.

    renderer 3
If you only need to change how a value is displayed, for example how a date or number appears, use a formatter.

Component Renderer

ComponentRenderer renders items using UI components instead of plain text. Assign it programmatically with setRenderer() or provide it from a controller using @Supply.

ComponentRenderer creates UI components for rendered items and can affect performance on large datasets. If you only need to render HTML-like content, use LitRenderer.

ComponentRenderer can be created in different ways, depending on how you want to create and update components. The examples below show the available options.

Single Callback

Use this when a single function can generate a new component instance directly from the model item.

@Supply(to = "selectWithRenderer", subject = "renderer") (1)
private ComponentRenderer<Button, Department> selectWithRendererRenderer() {
    return new ComponentRenderer<>(item -> { (2)
        Button button = uiComponents.create(Button.class); (3)
        button.setText(item.getName());
        button.setIcon(VaadinIcon.DESKTOP.create());
        return button; (4)
    });
}
1 @Supply binds the renderer to selectWithRenderer.
2 The function receives the current item.
3 The component instance is created and configured inside the same callback.
4 The function returns the component for the current item.

Separate Creation And Binding

Use this when component creation and item binding should be handled separately.

@Supply(to = "dataGridCheckbox.active", subject = "renderer") (1)
private Renderer<User> dataGridCheckboxActiveRenderer() {
    return new ComponentRenderer<>(
            () -> {
                JmixCheckbox checkbox = uiComponents.create(JmixCheckbox.class); (2)
                checkbox.setReadOnly(true); (3)
                return checkbox; (4)
            },
            (checkbox, item) -> checkbox.setValue(item.getActive()) (5)
    );
}
1 @Supply binds the renderer to the active column of dataGridCheckbox.
2 The supplier creates a JmixCheckbox component instance.
3 The checkbox is set to read-only mode.
4 The supplier returns the component instance.
5 The consumer receives the component and the current item, then sets the checkbox value from item.getActive().

Explicit Updates

Use this when item updates require more control than the previous examples provide. The update callback can either reuse the current component instance or replace it with a new one.

@Supply(to = "dataGridStatus.status", subject = "renderer")
private Renderer<User> createUpdatableStatusRenderer() {
    return new ComponentRenderer<>(
            item -> {
                Span span = uiComponents.create(Span.class); (1)
                updateSpanBadge(span, item); (2)
                return span; (3)
            },
            (component, item) -> {
                if (item.getOnboardingStatus() == null) { (4)
                    updateUnknownStatus(component); (5)
                    return component; (6)
                } else {
                    return createStatusBadge(item); (7)
                }
            }
    );
}
1 The creation callback creates the initial Span component instance.
2 The initial component is populated and styled as a badge for the current item.
3 The creation callback returns the component.
4 The update callback checks whether the current item has a status value.
5 If the value is missing, the callback updates the current component instance.
6 The current component is returned unchanged.
7 If the value is present, the callback creates and returns a new component for that item.

More ComponentRenderer examples are available in UI components that support it:

LitRenderer

LitRenderer uses HTML and Lit’s data-binding syntax to render content efficiently. It is a lightweight alternative to component-based renderers and helps preserve performance when many components are rendered or updated frequently.

LitRenderer allows you to define an HTML template and bind properties from an entity to template variables:

@Supply(to = "dataGridLit.userInfo", subject = "renderer")
private Renderer<User> dataGridLitUserInfoRenderer() {
    return LitRenderer.<User>of("${item.firstName}<br>${item.lastName}<br>${item.email}")
            .withProperty("firstName", User::getFirstName)
            .withProperty("lastName", User::getLastName)
            .withProperty("email", User::getEmail);
}
renderer 5

You can use LitRenderer in the following UI components:

Fragment Renderer

Renderers for virtualList, dataGrid and other components can be defined using fragments. A special fragmentRenderer XML element is used to declaratively describe a renderer.

Like regular fragments, a fragmentRenderer is defined using a descriptor and a Java controller.

  1. Create FragmentRenderer XML-descriptor

    The descriptor can contain an optional instanceContainer for the entity that will be used by the renderer.

    <fragment xmlns="http://jmix.io/schema/flowui/fragment">
        <data>
            <instance id="userDc" class="com.company.onboarding.entity.User">
                <loader id="userDl"/>
                <fetchPlan extends="_base"/>
            </instance>
        </data>
        <content>
            <hbox>
                <icon icon="ANGLE_DOUBLE_RIGHT"/>
                <formLayout id="form" dataContainer="userDc">
                    <textField property="username" readOnly="true"/>
                    <textField property="firstName" readOnly="true"/>
                    <textField property="lastName" readOnly="true"/>
                    <textField property="email" readOnly="true"/>
                </formLayout>
            </hbox>
        </content>
    </fragment>
    If the instance container is not defined, you can use the FragmentRenderer.getItem() method to handle rendering.
  2. Create FragmentRenderer Java Controller

    The fragment renderer class should extend the FragmentRenderer base class with type parameters indicating the root component and rendered entity, for example:

    @FragmentDescriptor("user-fragment.xml") (1)
    @RendererItemContainer("userDc") (2)
    public class UserFragment extends FragmentRenderer<HorizontalLayout, User> {
    }
    1 The @FragmentDescriptor annotation specifies a string value that is a path to an XML file which is used for the fragment initialization.
    2 The @RendererItemContainer annotation is used to specify the data container that accepts the rendered entity.
  3. Use FragmentRenderer in a View

    Select the component where you want to render items and include the fragmentRenderer element. This element requires a class attribute specifying the fully qualified name (FQN) of a class extending the abstract io.jmix.flowui.fragmentrenderer.FragmentRenderer class.

    <virtualList itemsContainer="usersDc">
        <fragmentRenderer
                class="com.company.onboarding.view.component.virtuallist.UserFragment"/>
    </virtualList>

    To add a fragmentRenderer in Jmix Studio, select the UI component in the view descriptor XML or in the Jmix UI structure panel and click on the Add→FragmentRenderer button in the Jmix UI inspector panel.

    The fragmentRenderer element also supports loading properties, just like the fragment element.

renderer 6

You can use fragmentRenderer in the following UI components: