Pass Values to Components
Use props to give each copy of a component its own text and options.
A prop is a value passed into a component. For example, the same MenuButton can show Play in one place and Settings in another.
In a View that imports MenuButton:
<MenuButton label="Play" />
<MenuButton label="Settings" />Inside MenuButton, read the value:
<button>{{ props.label }}</button>The component tutorial includes the full files.
Pass a changing value
A plain attribute supplies text. Prefix an attribute with : to read an
expression instead:
<MenuButton :label="props.nextLabel" />Here the parent must receive nextLabel. Props can also contain numbers,
true/false values, arrays, or objects. A component can read them but should send
an event or action when it needs the owner to change them.
In defineComponent, props is a live, read-only object. Read changing values
inside computed() or watch() so later parent updates invalidate their
dependencies. A parent update replaces its prop values as one batch: watchers
see the complete new values, not a partially updated set. Unrelated prop changes
do not invalidate computations that did not read them. Nested objects and arrays
are read-only snapshots too; ask the owner to replace them through an event.
Destructuring a scalar prop once during setup captures its current value rather
than creating a reactive binding.
A returned script binding may evaluate to null. It is an intentional empty
value, not a missing variable. A missing or undefined binding still produces
a diagnostic. Use null when forwarding an optional value that the child owns
a default for.
Translated text props
Use label="$loc('GameUI::Confirm', 'Potwierdź')" where you pass the prop.
The component still reads {{ props.label }}. Text and its source reference pass
through nested components without a text slot or JavaScript wrapper.
See Translate Your UI for Languages, defaults and Culture.
Handle an optional value
Guard optional content with ui:if:
<p ui:if="props.description">{{ props.description }}</p>If description is omitted, the paragraph is not created. Reading a missing
prop in unguarded text or ordinary bound attributes produces an error.
Add an optional visual variant
<button class="menu-button"
:class="{ 'menu-button--danger': props.variant == 'danger' }">
{{ props.label }}
</button>Define both classes in the component CSS. The base class always remains. If
variant is omitted, no modifier is added. The label prop is still required.
Bind inline styles
:style accepts an object the same shape as :class, but its values become
CSS declarations instead of class-name toggles:
<div style="border-style:dashed"
:style="{ borderTopStyle: 'solid', '--accent-color': props.accent, display: props.hidden }">
</div>- Keys are camelCased (
borderTopStyle→border-top-style); a key starting with--is kept as-is as a CSS custom property. nullandfalsevalues omit that declaration entirely; every other value (includingtrue) is written out withString()-style formatting.- A
numbervalue gets an implicitpxunit appended (the same convention as React/Vue), so{ width: 77 }becomeswidth:77px;. A bare number without a unit is invalid CSS and is silently dropped, leaving the element at its CSS default (e.g. awidthreads as the parent's full width). The properties whose CSS Profile value is itself a bare number are never coerced:opacity,z-index,flex-grow,flex-shrink,line-height,font-weight,animation-iteration-count, and any--custom-property. A string value (already carrying its own unit, e.g.'50%') is never suffixed. - A static
style="..."attribute on the same element is kept and always applies first; the bound object's declarations are appended after it, so a bound value overrides a static one for the same property, independent of which one is written first in the template, the same static-first/bound-wins contract as:class. :stylealso accepts a plain string expression (e.g.:style="computedCss") instead of an object, appended after any staticstylethe same way.
Like :class, a :style object value is a full R5 expression (literal, prop
path, comparison, &&/||, $loc(...)), not JavaScript template literals
or + concatenation. See the small binding profile
below. Compute a full string with a unit (e.g. ${width}px) in <script> and
bind the plain identifier instead:
const boxStyle = computed(() => ({ width: `${store.panelWidth}px` }));<div :style="boxStyle"></div>The small binding profile
An R5 template expression ({{ }}, :attr="...", :class, :style, event
payloads) accepts only literals, props/local paths, comparisons, &&/||,
and $loc(...), never a function call or + string concatenation. Writing
'prefix-' + value or a `${value}px` template literal in a binding fails
to compile with CUI2050. Precompute the
value in <script> instead:
const audioMasterPercent = computed(() => percentText(store.settings.audioMaster));<p>{{ audioMasterPercent }}</p>This keeps every binding a plain read with no compiler-hosted string
formatting or arbitrary code. See docs/calmui-component-format.md for the
full expression grammar this page summarizes.