Skip to main content

Props, Attributes & the .props Contract

Dim components receive data from the parent as HTML attributes, the .props property, and light-DOM children. Understanding which channel to use prevents silent bugs and security issues.


Three input channels​

ChannelBest forExample
AttributesStrings, numbers, booleans, JSON objectstransitionId="3"
.propsFunctions, live objects, non-JSON data.props=${{ onSave, user }}
ChildrenComposition / slots<my-card>Hello</my-card>

Inside define(), Dim merges attributes and .props into a single props object passed to your functional component.


Case-insensitive prop names​

HTML lowercases attribute names. Dim resolves props case-insensitively:

// Parent sets attribute transitionId — stored as transitionid in DOM
html`<page-view transitionId="${id}"></page-view>`

// Inside component, both work via readProp():
props.transitionId === props.transitionid

Prefer .props for camelCase-heavy APIs to avoid ambiguity.


JSON object attributes​

The Dim html template helper supports React-like attr={{ ... }} syntax, but values are parsed with JSON.parse only — not evaluated as JavaScript:

// Valid JSON — works
html`<todo-item todo='{"id":1,"text":"Milk","done":false}'></todo-item>`

// Invalid — functions, undefined, trailing commas, unquoted keys
html`<todo-item todo={{ id: 1, onToggle: toggle }}></todo-item>`
// → console warning; use .props instead

Lit also auto-parses attribute values that start with { or [ when set as plain attributes.


Passing functions and callbacks​

Functions must use property binding, not attributes:

const handleDelete = (id) => setTodos(todos.filter((t) => t.id !== id));

return html`
<todo-list
.props=${{
todos,
onDelete: handleDelete,
}}
></todo-list>
`;

Attributes are always strings; a function stringified to the attribute "function …" cannot be called.


The .props property​

.props sets the Lit reactive property props: Object on the custom element:

html`<my-form .props=${{ values, errors, onSubmit }}></my-form>`

On each render, Dim merges:

  1. Parsed attributes
  2. Keys from this.props (.props wins on conflict via case-insensitive lookup)

Use .props when passing:

  • Event handlers (@click targets in child templates)
  • Class instances or circular structures
  • Values that change every render without attribute serialization

sharedDependencies injection​

Components receive hooks twice — import from dim.ts or use the second argument:

const Child = (props, { useState, html, useRef }) => { ... };

define({ tag: "my-child", component: Child });

Demos often prefer the injected object so federated or lazy-loaded modules share the same hook instances.

Additional injected helpers:

  • querySelector — scoped to the component shadow root
  • getRef(refName) — resolve another component’s useRef target
  • renderChildren — slot / HTML child content
  • keyed — Lit directive for stable list keys

Children and slots​

Light DOM children are captured in connectedCallback:

const Wrapper = (props, { html, renderChildren }) => {
return html`
<div class="wrapper">
${renderChildren(props.children)}
</div>
`;
};

Strings with custom element tags use <slot>; plain HTML may use unsafeHTML internally.

See 14. Composition patterns (planned) or Storybook nested components demo for advanced slot usage.


transitionId and other framework props​

Framework features read props before your component runs:

html`
<screen-host
transitionId="${viewId}"
transitionDuration="400"
transitionAutoDirection="false"
.props=${{ viewId, data }}
></screen-host>
`;

transitionId on attributes triggers auto view transitions even without .props.


Checklist​

  1. Primitives → attributes or JSON in templates
  2. Functions / objects with methods → .props
  3. camelCase API → prefer .props
  4. Never pass secrets in attributes (visible in DOM inspector)
  5. Encrypted state → useStore with { encryptionKey }, not props

Conclusion​

The .props contract is Dim’s escape hatch for everything HTML attributes cannot represent. JSON-only attribute parsing is a deliberate security and performance choice.