Rent a car: A custom item renderer
The default dropdown item template is just a label. Real applications usually want richer items — a thumbnail, the title, secondary metadata. GolemUI lets you supply a custom item renderer for that.
Step 1 — Enrich the data
Section titled “Step 1 — Enrich the data”The car items now carry an emoji thumbnail and a price:
items: [ { id: 'compact', label: 'Compact', img: '🚗', price: 35 }, { id: 'suv', label: 'SUV', img: '🚙', price: 75 }, { id: 'convertible', label: 'Convertible', img: '🏎️', price: 110 }, { id: 'luxury', label: 'Luxury', img: '🚘', price: 180 },],Step 2 — Implement the renderer
Section titled “Step 2 — Implement the renderer”Item renderers are framework-specific components.
import * as React from 'react';import type { ReactItemRenderer } from '@golemui/react';
type Car = { id: string; label: string; img: string; price: number };
export const CarItemRenderer: ReactItemRenderer<Car> = ({ item }) => ( <div className="car-item"> <span className="car-item__img">{item.img}</span> <span className="car-item__label">{item.label}</span> <span className="car-item__price">${item.price}/day</span> </div>);import { Component } from '@angular/core';import * as Angular from '@golemui/angular';
type Car = { id: string; label: string; img: string; price: number };
@Component({ standalone: true, selector: 'car-item-renderer', template: ` <div class="car-item"> <span class="car-item__img">{{ item.img }}</span> <span class="car-item__label">{{ item.label }}</span> <span class="car-item__price">\${{ item.price }}/day</span> </div> `,})export class CarItemRenderer implements Angular.AngularItemRenderer<Car> { item!: Car;}import { LitElement, html } from 'lit';import { customElement, property } from 'lit/decorators.js';
type Car = { id: string; label: string; img: string; price: number };
@customElement('car-item-renderer')export class CarItemRenderer extends LitElement { @property({ attribute: false }) item!: Car;
override createRenderRoot() { return this; }
override render() { return html` <div class="car-item"> <span class="car-item__img">${this.item.img}</span> <span class="car-item__label">${this.item.label}</span> <span class="car-item__price">$${this.item.price}/day</span> </div> `; }}Step 3 — Add styles
Section titled “Step 3 — Add styles”A few lines of CSS to lay the renderer out:
.car-item { display: flex; align-items: center; gap: var(--gui-space-3);}.car-item__img { font-size: 1.4em; }.car-item__label { flex: 1; }.car-item__price { color: var(--gui-color-fg--secondary); }Step 4 — Register the renderer
Section titled “Step 4 — Register the renderer”Add it to formConfig.itemRenderers:
const formConfig = { itemRenderers: { carItemRenderer: CarItemRenderer, },};Step 5 — Reference it from the dropdown
Section titled “Step 5 — Reference it from the dropdown”The dropdown’s itemRenderer prop carries the name you registered:
import { gui } from '@golemui/gui-shared';
export default [ gui.inputs.dropdown('car', { labelField: 'label', valueField: 'id', itemRenderer: 'carItemRenderer', items: [ { id: 'compact', label: 'Compact', img: '🚗', price: 35, }, { id: 'suv', label: 'SUV', img: '🚙', price: 75, }, { id: 'convertible', label: 'Convertible', img: '🏎️', price: 110, }, { id: 'luxury', label: 'Luxury', img: '🚘', price: 180, }, ], label: 'Select car', validator: { type: 'string', required: true, }, }), gui.inputs.dropdown('collectOffice', { labelField: 'label', valueField: 'id', items: [ { id: 'lhr', label: 'London Heathrow', }, { id: 'cdg', label: 'Paris CDG', }, { id: 'fra', label: 'Frankfurt Main', }, ], label: 'Collect from office', validator: { type: 'string', required: true, }, }), gui.inputs.booleanInput('differentReturn', { label: 'Choose a different return location', }), gui.inputs.dropdown('returnOffice', { labelField: 'label', valueField: 'id', items: [ { id: 'lhr', label: 'London Heathrow', }, { id: 'cdg', label: 'Paris CDG', }, { id: 'fra', label: 'Frankfurt Main', }, ], label: 'Return to office', validator: { type: 'string', required: true, }, }), gui.inputs.rangeCalendar('rentalDates', { label: 'Rental dates', validator: { type: 'array', required: true, minItems: 2, maxItems: 2, }, }), gui.inputs.radiogroup('rentalType', { options: [ { label: 'Daily', value: 'daily', }, { label: 'Weekly', value: 'weekly', }, { label: 'Monthly', value: 'monthly', }, ], label: 'Rental type', validator: { type: 'string', required: true, }, }), gui.inputs.booleanInput('driverOver25', { label: 'Driver aged over 25', validator: { type: 'boolean', const: true, required: true, }, }), gui.inputs.booleanInput('hasDiscountCode', { label: 'I have a discount code', }), gui.inputs.textInput('discountCode', { label: 'Discount code', validator: { type: 'string', required: true, minLength: 4, }, }), gui.actions.button({ label: 'Reserve', uid: 'submit', onClick: 'submit', }),];{ "states": { "differentReturn": "$form.differentReturn === true", "hasDiscount": "$form.hasDiscountCode === true" }, "form": [ { "kind": "input", "type": "dropdown", "path": "car", "label": "Select car", "validator": { "type": "string", "required": true }, "props": { "labelField": "label", "valueField": "id", "itemRenderer": "carItemRenderer", "items": [ { "id": "compact", "label": "Compact", "img": "🚗", "price": 35 }, { "id": "suv", "label": "SUV", "img": "🚙", "price": 75 }, { "id": "convertible", "label": "Convertible", "img": "🏎️", "price": 110 }, { "id": "luxury", "label": "Luxury", "img": "🚘", "price": 180 } ] } }, { "kind": "input", "type": "dropdown", "path": "collectOffice", "label": "Collect from office", "validator": { "type": "string", "required": true }, "props": { "labelField": "label", "valueField": "id", "items": [ { "id": "lhr", "label": "London Heathrow" }, { "id": "cdg", "label": "Paris CDG" }, { "id": "fra", "label": "Frankfurt Main" } ] } }, { "kind": "input", "type": "toggle", "path": "differentReturn", "label": "Choose a different return location" }, { "kind": "input", "type": "dropdown", "path": "returnOffice", "label": "Return to office", "include": { "in": ["differentReturn"] }, "validator": { "type": "string", "required": true }, "props": { "labelField": "label", "valueField": "id", "items": [ { "id": "lhr", "label": "London Heathrow" }, { "id": "cdg", "label": "Paris CDG" }, { "id": "fra", "label": "Frankfurt Main" } ] } }, { "kind": "input", "type": "rangeCalendar", "path": "rentalDates", "label": "Rental dates", "validator": { "type": "array", "required": true, "minItems": 2, "maxItems": 2 } }, { "kind": "input", "type": "radiogroup", "path": "rentalType", "label": "Rental type", "validator": { "type": "string", "required": true }, "props": { "options": [ { "label": "Daily", "value": "daily" }, { "label": "Weekly", "value": "weekly" }, { "label": "Monthly", "value": "monthly" } ] } }, { "kind": "input", "type": "toggle", "path": "driverOver25", "label": "Driver aged over 25", "validator": { "type": "boolean", "const": true, "required": true } }, { "kind": "input", "type": "toggle", "path": "hasDiscountCode", "label": "I have a discount code" }, { "kind": "input", "type": "textinput", "path": "discountCode", "label": "Discount code", "include": { "in": ["hasDiscount"] }, "validator": { "type": "string", "required": true, "minLength": 4 } }, { "kind": "action", "type": "button", "label": "Reserve", "uid": "submit", "on": { "click": "submit" } } ]}See also
Section titled “See also”- Features / Item Renderers — wiring renderers in
formConfig. - Extending GolemUI / Item Renderers — the renderer contract per framework.