Props System
How Prop Types Work
Prop type plugins are intermediaries between component JSON Schema definitions and source plugins. Each prop in a component definition is annotated with a PropTypeInterface instance during discovery. The PropTypePluginManager determines the prop type by checking JSON Schema compatibility:
- If the prop uses a
$refwithui-patterns://prefix, the prop type is resolved directly (e.g.,ui-patterns://booleanmaps to thebooleanprop type). - Otherwise, all prop type definitions are checked via
CompatibilityChecker::isCompatible(), sorted by priority. - If no match is found, the
unknownfallback type is used (which hides the prop from forms).
Built-in Prop Types
| Prop Type ID | JSON Schema | Default Source | Converts From | YAML Shortcut |
|---|---|---|---|---|
string |
type: string |
textfield |
number, url, identifier | ui-patterns://string |
number |
type: [number, integer] |
number |
-- | ui-patterns://number |
boolean |
type: boolean |
checkbox |
-- | ui-patterns://boolean |
url |
type: string, format: iri-reference |
url |
-- | ui-patterns://url |
identifier |
Machine-name pattern | textfield |
string | ui-patterns://identifier |
enum |
type: [string,number,integer], enum: [] |
select |
-- | ui-patterns://enum |
enum_list |
Array of enums | selects |
-- | ui-patterns://enum_list |
enum_set |
Array with uniqueItems: true |
checkboxes |
-- | ui-patterns://enum_set |
links |
Array of link objects | menu |
-- | ui-patterns://links |
list |
Array of strings/numbers | list_textarea |
-- | ui-patterns://list |
attributes |
Object with pattern properties | attributes |
-- | ui-patterns://attributes |
variant |
String enum (auto-generated) | select |
string | ui-patterns://variant |
slot |
(internal) | component |
string | -- |
unknown |
(fallback) | -- | -- | -- |
Type Conversion
Prop types declare convert_from in their attribute, enabling automatic value conversion between compatible types:
string <- number, url, identifier
slot <- string
identifier <- string
variant <- string
Those four are the complete set — only StringPropType, SlotPropType, IdentifierPropType and VariantPropType declare convert_from. Every other prop type accepts only its native sources.
The PropTypePluginManager builds a directed graph of conversion paths. When a source produces a value for a prop type it does not natively support, the system walks the shortest conversion path. For example, a URL source can provide a value for a string prop because string declares convert_from: ['url'], and a textfield or token source can fill an identifier or variant prop for the same reason.
One exception to watch: conversion deliberately skips sources tagged widget:dismissible (path, url, number, attributes, list_textarea). SourcePluginManager::getConvertibleDefinitionsPerPropertyId() forces ["widget:dismissible" => FALSE] into the tag filter, so path never appears as a converted source for a string prop even though string converts from url.
Normalize and Preprocess Pipeline
Each prop type implements two processing stages:
normalize()-- Runs before SDC validation. Converts incoming values to JSON Schema-valid form (e.g.,StringPropType::normalize()renders render arrays to strings;BooleanPropType::normalize()converts"1"totrue).preprocess()-- Runs after validation, before template rendering. Prepares template-friendly values. OnlyAttributesPropTypeandLinksPropTypeoverride it;PropTypePluginBase::preprocess()returns the value unchanged, so most prop types do nothing at this stage.
StringPropType::normalize() is where the escaping decision is made, and it changed after 2.0.15 (issue #3611167, "Escape at render, not in sources"). Trust now follows the PHP type of the value, not the prop's contentMediaType:
contentMediaType: text/plain->strip_tags(), always, even on aMarkupvalue.- A
MarkupInterfacevalue (Markup::create(),TranslatableMarkup) is passed through untouched and stays trusted. - A renderable or render array is rendered through Drupal's render pipeline and the result is wrapped in
Markup::create(). - A plain PHP string is left as a plain string and Twig autoescapes it at render time.
That last rule is the one to internalise: as of 2.0.19 a plain string prop no longer emits raw HTML. To pass raw HTML through a string prop, the source must hand over a Markup object. In 2.0.15, StringPropType::preprocess() wrapped every non-text/plain string in Markup::create(); that override is gone.
The meta:enum Extension
Non-standard but supported by UI Patterns, meta:enum provides human-readable labels for enum values in forms:
position:
type: "string"
"$ref": "ui-patterns://enum"
enum: ["top", "bottom", "left", "right"]
"meta:enum":
top: "Top"
bottom: "Bottom"
left: "Left"
right: "Right"
Common Mistakes
| Mistake | Why It Is Wrong |
|---|---|
Creating complex nested JSON Schema instead of using $ref shortcuts |
UI Patterns may not recognize complex schemas and mark them as unknown type, hiding the prop from forms entirely. Use ui-patterns:// references. |
| Expecting unknown-typed props to appear in forms | Props with unknown type get zero source plugins and are hidden. Fix the JSON Schema to match a known prop type. |
| Assuming all prop types are interchangeable | Conversion paths are directional. A string source works for slots (string -> slot), but a slot source never works for string props. |
Putting format: uri instead of format: iri-reference |
The URL prop type matches iri-reference (supports internationalized URLs). Using uri may fall back to a plain string type. |