For the complete documentation index, see llms.txt. This page is also available as Markdown.

Recipes

Recipes are copy-pasteable starting points as examples

Each one is a complete, single-file component.

The examples below keep their CSS in an inline <style> block so each one can be pasted and run on its own. In real screens, define your styles in the platform's styling feature instead and keep only the class names in your component.


A simple editor widget

Wraps LabeledEditor, edits one value, keeps no local state for data.

import React from "react";
import LabeledEditor from "components/editors/LabeledEditor";

function TextEditor(props) {
  const { data, onChange, valueType } = props;
  const isNumeric = valueType === "integer" || valueType === "number";

  const handleChange = (e) => {
    let value = e.target.value;
    if (value === "") value = undefined;
    else if (valueType === "integer") {
      const parsed = parseInt(value, 10);
      value = isNaN(parsed) ? undefined : parsed;
    } else if (valueType === "number") {
      const parsed = Number(value);
      value = isNaN(parsed) ? undefined : parsed;
    }
    onChange(value);
  };

  return (
    <LabeledEditor {...props}>
      <input
        className="Rie-input Rie-input-text"
        type={isNumeric ? "number" : "text"}
        value={data === undefined ? "" : data}
        onChange={handleChange}
        {...(valueType === "number" && { step: "any" })}
        {...(valueType === "integer" && { step: "1" })}
      />
    </LabeledEditor>
  );
}

export default TextEditor;

Three rules this follows:

  • no useState for data, since the platform owns the value,

  • label is never defined; it comes from the field's schema,

  • undefined clears the value, which removes the field from the record.

When you do need local state

Only for high-frequency input. Keep a local mirror and throttle the update:

A custom list with grouped cards

List with server-side filtering and paging

A custom list receives the whole result set, so drive paging yourself through onFilter:


A custom editor

Two things to notice: props.events() is a function call and the onSave it returns takes no arguments: it reads the record and the changes itself, and runs validation.


Reusing the standard editors

Your sections and layout, the platform's field editors:


A custom page that fetches its own data

Custom pages get no data by default. Two options.

Option A: query directly

For a filtered query, remember the versionId so results refresh after a save:

Option B: get the standard data contract

This needs props.schema, so configure the menu entry to use the UI definition for that type.


Calling the backend directly

When the standard record operations aren't enough:

For a file, use global.downloadWrapper(url) instead. It handles the filename and the save dialog.


Translations, icons, tooltips, conditions


Expressions, including lookups

Synchronous, no context needed:

Asynchronous, when the expression fetches another record:


Remembering UI state per user


Using an npm package

esm.sh/... with no https://. The package is downloaded when your code is compiled and bundled into your component, so prefer a platform module when one will do.

Last updated