Zum Hauptinhalt springen
tsecurity.de LIVE
Echtzeit-Radar & Feeds
Alle RSS Feeds
👥 Community & Social
YouTube Security VideosAndroid Police: THIS is Samsung's 5 year strategy? #shorts #tech #phone(24.09.2026 um 13:18 Uhr)
Windows Tipps & SecurityBatterietester für unter 10 Euro: So prüfen Sie leere Batterien schnell(24.09.2026 um 13:14 Uhr)
Windows Tipps & SecurityBentley bringt sein erstes Elektroauto auf den Markt(24.09.2026 um 13:49 Uhr)
Windows Tipps & SecurityAvocor integriert Korbyt-CMS in B-Series Displays(24.09.2026 um 13:05 Uhr)
Windows Tipps & SecuritydBTechnologies erweitert Opera-Familie um Nona-Serie(24.09.2026 um 13:15 Uhr)
Windows Tipps & SecurityJens Miedek wird Senior Vice President Sales bei Qvest(24.09.2026 um 13:20 Uhr)
Windows Tipps & SecurityBenQ bringt vier neue Boards mit KI-Beschleuniger(24.09.2026 um 13:33 Uhr)
YouTube Security VideosAndroid Police: THIS is Samsung's 5 year strategy? #shorts #tech #phone(24.09.2026 um 13:18 Uhr)
Windows Tipps & SecurityBatterietester für unter 10 Euro: So prüfen Sie leere Batterien schnell(24.09.2026 um 13:14 Uhr)
Windows Tipps & SecurityBentley bringt sein erstes Elektroauto auf den Markt(24.09.2026 um 13:49 Uhr)
Windows Tipps & SecurityAvocor integriert Korbyt-CMS in B-Series Displays(24.09.2026 um 13:05 Uhr)
Windows Tipps & SecuritydBTechnologies erweitert Opera-Familie um Nona-Serie(24.09.2026 um 13:15 Uhr)
Windows Tipps & SecurityJens Miedek wird Senior Vice President Sales bei Qvest(24.09.2026 um 13:20 Uhr)
Windows Tipps & SecurityBenQ bringt vier neue Boards mit KI-Beschleuniger(24.09.2026 um 13:33 Uhr)
Intelligence View
⚡ tsecurity.de Intelligence

Making Web Component properties behave closer to the platform

Built-in HTML elements' properties all share similar behaviors, that don't come for free when you write your own custom elements. Let's see what those behaviors are, why you'd want to implement them in your web components, and how to do…

0
↗ Quelle (dev.to)
Reagiere als Erste:r — dein Feedback zählt!

Built-in HTML elements' properties all share similar behaviors, that don't come for free when you write your own custom elements. Let's see what those behaviors are, why you'd want to implement them in your web components, and how to do it, including how some web component libraries actually don't allow you to mimic those behaviors.






Built-in elements' behaviors



I said it already: built-in elements' properties all share similar behaviors, but there are actually several different such shared behaviors. First, there are properties (known as IDL attributes in the HTML specification) that reflect attributes (also known as content attributes); then there are other properties that are unrelated to attributes. One thing you won't find in built-in elements are properties whose value will change if an attribute change, but that won't update the attribute value when they are changed themselves (in case you immediately thought of value or checked as counter-examples, the situation is actually a bit more complex: those attributes are reflected by the defaultValue and defaultChecked properties respectively, and the value and checked properties are based on an internal state and behave differently depending on whether the user already interacted with the element or not).






Type coercion



But I'll start with another aspect that is shared by all of them, whether reflected or not: typing. DOM interfaces are defined using WebIDL, that has types and extended annotations, and defines mapping of those to JavaScript. Types in JavaScript are rather limited: null, undefined, booleans, IEEE-754 floating-point numbers, big integers, strings, symbols, and objects (including errors, functions, promises, arrays, and typed arrays). WebIDL on the other hand defines, among others, 13 different numeric types (9 integer types and 4 floating point ones) that can be further annotated to change their overflowing behavior, and several string types (including enumerations).



The way those types are experienced by developers is that getting the property will always return a value of the defined type (that's easy, the element owns the value), and setting it (if not read-only) will coerce the assigned value to the defined type. So if you want your custom element to feel like a built-in one, you'll have to define a setter to coerce the value to some specific type. The underlying question is what should happen if someone assigns a value of an unexpected type or outside the expected value space?




Convert and validate the new value in a property custom setter.




You probably don't want to use the exact WebIDL coercion rules though, but similar, approximated, rules that will behave the same most of the time and only diverge on some edge cases. The reason is that WebIDL is really weird: for instance, by default, numeric values overflow by wrapping around, so assigning 130 to a byte (whose value space ranges from -128 to 127) will coerce it to… -126! (128 wraps to -128, 129 to -127, and 130 to -126; and by the way 256 wraps to 0; for the curious, BigInt.asIntN and BigInt.asUintN will do such wrapping in JS, but you'll have to convert numbers to BigInt and back); non-integer values assigned to integer types are truncated by default, except when the type is annotated with [Clamp], in which case they're rounded, with half-way values rounded towards even values (something that only happens natively in JS when setting such non-integer values to typed arrays: Math.round(2.5) is 3, but Int8Array.of(2.5)[0] is 2).



Overall, I feel like, as far as primitive/simple types are concerned, boolean, integers, double (not float), string (WebIDL's DOMString), and enumerations are all that's needed; truncating (or rounding, but with JavaScript rules), and clamping or enforcing ranges for integers. In other words, wrapping integers around is just weird, and what matters is coercing to the appropriate type and value space. Regarding enumerations, they're probably best handled by the reflection rules though (see below), and treated only as strings: no single built-in element has a property of a type that's a WebIDL enum.






Reflected properties



Now let's get back to reflected properties: most properties of built-in elements reflect attributes or similarly (but with specific rules) correspond to an attribute and change its value when set; non-reflected properties are those that either expose some internal state (e.g. the current value or validation state of a form field), computed value (from the DOM, such as the selectedIndex of a select, or the cellIndex of a table cell) or direct access to DOM elements (elements of a form, rows of a table, etc.), or that access other reflected properties with a transformed value (such as the valueAsDate and valueAsNumber of input). So if you want your custom element to feel like a built-in one, you'll want to use similar reflection wherever appropriate.




Have your properties reflect attributes by default.




The way reflection is defined is that the source of truth is the attribute value: getting the property will actually parse the attribute value, and setting the property will stringify the value into the attribute (note that this means possibly setting the attribute to an invalid value that will be corrected by the getter; an example of this is setting the type property of an input element to an unknown value: it will be reflected in the attribute as-is, but the getter will correct it text). This is at least how it theoretically works; in practice, the parsed value can be cached to avoid parsing every time the property is read; but note that there can be several properties reflecting the same attribute (the most known one probably being className and classList both reflecting the class attribute). Reflected properties can also have additional options, depending on their type, that will change the behavior of the getter and setter, not unlike WebIDL extended attributes.



Just like with WebIDL coercion rules, some HTML reflection rules clearly exist only for backwards compatibility (e.g. the limited to only positive numbers with fallback option for reflecting unsigned long properties). Also note that HTML only defines reflection for a limited set of types (if looking only at primitive/simple types, only non-nullable and nullable strings and enumerations, long, unsigned long, and double are covered, and none of the narrower integer types, big integers, or the unrestricted double that allows NaN and infinity).



You can see how Mozilla tests the compliance of their built-in elements

in the Gecko repository (the ok and is assertions are defined in their SimpleTest testing framework). And here's the Web Platform Tests' reflection harness, with data for each built-in element in sibling files, that almost every browser pass.



It's still not clear to me whether the rules that will pass an invalid value to the attribute, to be corrected by the property getter is something worth emulating, or if it's part of those backwards-compatibility rules. I'll assuming they're worth it for now.





Events



Most direct changes to properties and attributes don't fire events: user actions or method calls will both update a property and fire an event, but changing a property programmatically generally won't fire any event. There are a few exceptions though: the events of type ToggleEvent fired by changes to the popover attribute or the open attribute of details elements, or the select event when changing the selectionStart, selectionEnd or selectionDirection properties of input and textarea elements (if you know of others, let me know); but notably changing the value of a form element programmatically won't fire a change or input event. So if you want your custom element to feel like a built-in one, don't fire events from your property setters or other attribute changed callbacks, but fire an event when (just after) you programmatically change them.




Don't fire events from your property setters or other attribute changed callbacks.






Why you'd want to implement those



If you're you (your team, your company) are the only users of the web components (e.g. building an application out of web components, or an internal library of reusable components), then OK, don't use reflection if you don't need it, you'll be the only user anyway so nobody will complain. If you're publicly sharing those components, then my opinion is that, following the principle of least astonishment, you should aim at behaving more like built-in elements, and reflect attributes.



Similarly, for type coercions, if you're the only users of the web components, it's ok to only rely on TypeScript (or Flow or whichever type-checker) to make sure you always pass values of the appropriate type to your properties (and methods), but if you share them publicly then you should in my opinion coerce or validate inputs, in which case you'd want to follow the principe of least astonishment as well, and thus use rules similar to WebIDL and reflection behaviors. This is particularly true for a library that can be used without specific tooling, which is generally the case for custom elements.



For example, all the following design systems can be used without tooling (some of them provide ready-to-use bundles, others can be used through import maps): Google's Material Web, Microsoft's Fluent UI, IBM's Carbon, Adobe's Spectrum, Nordhealth's Nord, Shoelace, etc.





How to implement them



Now that we've seen what we'd want to implement, and why we'd want to implement it, let's see how to do it. First without, and then with libraries.





Vanilla implementation



In a vanilla custom element, things are rather straightforward:




class MyElement extends HTMLElement {
get reflected() {
const strVal = this.getAttribute("reflected");
return parseValue(strVal);
}
set reflected(value) {
const newValue = coerceType(value);
// …there might be additional validations here…
this.setAttribute("reflected", stringifyValue(newValue));
}
}






or with intermediate caching (note that the setter is identical, setting the attribute will trigger the attributeChangedCallack which will close the loop):




class MyElement extends HTMLElement {
#reflected;

get reflected() {
return this.#reflected;
}
set reflected(value) {
const newValue = coerceType(value);
// …there might be additional validations here…
this.setAttribute("reflected", stringifyValue(newValue));
}

static get observedAttributes() {
return [ "reflected" ];
}
attributeChangedCallback(name, oldValue, newValue) {
// Note: in this case, we know it can only be the attribute named "reflected"
this.#reflected = parseValue(newValue);
}
}






And for a non-reflected property (here, a read-write property representing an internal state):




class MyElement extends HTMLElement {
#nonReflected;
get nonReflected() {
return this.#nonReflected;
}
set reflected(value) {
const newValue = coerceType(value);
// …there might be additional validations here…
this.#nonReflected = newValue;
}
}






Because many rules are common to many attributes (the coerceType operation is defined by WebIDL, or using similar rules, and the HTML specification defines a handful of microsyntaxes for the parseValue and stringifyValue operations), those could be packaged up in a helper library. And with decorators coming to ECMAScript (and already available in TypeScript), those could be greatly simplified:




class MyElement extends HTMLElement {
@reflectInt accessor reflected;
@int accessor nonReflected;
}






I actually started building such a library, mostly as an exercise (and I already learned a lot, most of the above details actually). Depending on how things go, I'll probably publish it (if not on NPM, at least on GitHub).






With a library



Surprisingly, web component libraries don't really help us here.



First, like many libraries nowadays, most expect people to just pass values of the appropriate types (relying on type checking through TypeScript) and basically leave you handling everything including how to behave in the presence of unexpected values. While it's OK, as we've seen above, in a range of situations, there are limits to this approach and it's unfortunate that they don't provide tools to make it easier at least coercing types.



Regarding reflected properties, most libraries tend to discourage you from doing it, while (fortunately!) supporting it, if only minimally.



All libraries (that I've looked at) support observed attributes though (changing the attribute value updates the property, but not the other way around), and most default to this behavior.



Now let's dive into the how-to with Lit, FAST, and then Stencil (other libraries left as a so-called exercise for the reader).






With Lit



By default, Lit reactive properties (annotated with @property()) observe the attribute of the same (or configured) name, using a converter to parse the value if needed (by default only handling numbers through a plain JavaScript number coercion, booleans, strings, or possibly objects or arrays through JSON.parse(); but a custom converter can be given). If your property is not associated to any attribute (but needs to be reactive to trigger a render when changed), then you can annotate it with @property({ attribute: false }) or @state() (the latter is meant for internal state though, i.e. private properties).



To make a reactive property reflect an attribute, you'll add reflect: true to the @property() options, and Lit will use the converter to stringify the value too. This won't be done immediately though, but only as part of Lit's reactive update cycle. This timing is a slight deviation compared to built-in elements that's probably acceptable, but it makes it harder to implement some reflection rules (those that set the attribute to a different value than the one returned by the getter) as the converter will always be called with the property value (returned by the getter, so after normalization).



It should be noted that, surprisingly, Lit actively discourages reflecting attributes:




Attributes should generally be considered input to the element from its owner, rather than under control of the element itself, so reflecting properties to attributes should be done sparingly. It's necessary today for cases like styling and accessibility, but this is likely to change as the platform adds features like the :state pseudo selector and the Accessibility Object Model, which fill these gaps.




No need to say I disagree.



For type coercion and validation, Lit allows you to have your own accessors (and version 3 makes it even easier), so everything's ok here, particularly for non-reflected properties:




class MyElement extends LitElement {
#nonReflected;
get nonReflected() {
return this.#nonReflected;
}
@state()
set nonReflected(value) {
const newValue = coerceType(value);
// …there might be additional validations here…
this.#nonReflected = newValue;
}
}






Implementing an enumerated attribute for instance (where the attribute value could be different from the property value) would thus mean using a non-reactive property wrapping a private reactive property (this assumes Lit won't flag them as errors in future versions), and parsing the value in its getter:




class MyElement extends LitElement {
@property({ attribute: "reflected", reflect: true })
accessor #reflected = "";

get reflected() {
return parseValue(this.#reflected);
}
set reflected(value) {
const newValue = coerceType(value);
// …there might be additional validations here…
this.#reflected = stringifyValue(newValue);
}
}






or with intermediate caching (note that the setter is identical):




class MyElement extends LitElement {
@property({ attribute: "reflected", reflect: true })
accessor #reflected = "";

#parsedReflected = "";
get reflected() {
return this.#parsedReflected;
}
set reflected(value) {
const newValue = coerceType(value);
// …there might be additional validations here…
this.#reflected = stringifyValue(newValue);
}

willUpdate(changedProperties) {
if (changedProperties.has("#reflected")) {
this.#parsedReflected = parseValue(this.#reflected);
}
}
}






It might actually be easier to directly set the attribute from the setter and only rely on an observed property from Lit's point of view:




class MyElement extends LitElement {
#reflected = "";
get reflected() {
return this.#reflected;
}
@property()
set reflected(value) {
const newValue = coerceType(value);
// …there might be additional validations here…
const stringValue = stringifyValue(newValue);
// XXX: there might be a more optimized way
// than stringifying and then parsing
this.#reflected = parseValue(stringValue);
// Avoid unnecessarily triggering attributeChangedCallback
// that would reenter that setter.
if (this.getAttribute("reflected") !== stringValue) {
this.setAttribute("reflected", stringValue);
}
}
}






Note that at no point we made use of converters, the observed or reflected attributes always being of type string.



If we're OK reflecting only valid values to attributes, then things are simpler as we can use converters (we still need the custom setter for type coercion and validation):




const customConverter = {
fromAttribute(value) {
return parseValue(value);
},
toAttribute(value) {
return stringifyValue(value);
},
};

class MyElement extends LitElement {
#reflected = "";
get reflected() {
return this.#reflected;
}
@property({ reflect: true, converter: customConverter })
set reflected(value) {
const newValue = coerceType(value);
// …there might be additional validations here…
// XXX: this should use a more optimized conversion/validation
this.#reflected = parseValue(stringifyValue(newValue));
}
}









With FAST



I know FAST is not used that much but I wanted to cover it as it seems to be the only library that reflects attributes by default. By default it won't do any type coercion unless you use the mode: "boolean", which works almost like an HTML boolean attribute, except an attribute present but with the value "false" will coerce to a property value of false!



Otherwise, it works more or less like Lit, with one big difference: the converter's fromView is also called when setting the property (this means that fromView receives any external value, not just string values from the attribute). But unfortunately this doesn't really help us as most coercion rules need to throw at one point and we want to do it only in the property setters, never when parsing attribute values; and those rules that don't throw will have possibly different values between the attribute and the property getter (push invalid value to the attribute, sanitize it on the property getter).



This means that in the end the solutions are almost identical to the Lit ones (here using TypeScript's legacy decorators though; and applying the annotation on the private property, that cannot for some reason be declared private):




class Element extends FASTElement {
@attr({ attribute: "reflected" })
__reflected = "";

get reflected() {
return parseValue(this.#reflected);
}
set reflected(value) {
const newValue = coerceType(value);
// …there might be additional validations here…
this.__reflected = stringifyValue(newValue);
}
}






or with intermediate caching (note that the setter is identical):




class MyElement extends LitElement {
@attr({ attribute: "reflected" })
__reflected = "";

private __reflectedChanged(oldValue, newValue) {
this._parsedReflected = newValue;
}

private _parsedReflected;
get reflected() {
return this._parsedReflected;
}
set reflected(value) {
const newValue = coerceType(value);
// …there might be additional validations here…
this.__reflected = stringifyValue(newValue);
}
}






Relying on an observed attribute only (mode: "fromView") and explicitly setting the attribute from the property setter does not seem to work though.



If we're OK only reflecting valid values to attributes, then things are simpler as we can use converters (we still need the custom setter for type coercion and validation, and thus a separate reactive property):




const customConverter = {
fromView(value) {
return parseValue(value);
},
toView(value) {
return stringifyValue(value);
},
};

class MyElement extends FASTElement {
@attr({ attribute: "reflected ", converter: customConverter })
__reflected = "";

get reflected() {
return this.__reflected;
}
set reflected(value) {
const newValue = coerceType(value);
// …there might be additional validations here…
this.__reflected = newValue;
}
}






For non-reflected properties, you'd want to use @observable instead of @attr, except that it doesn't work on custom accessors, so you'd have to do it manually:




class MyElement extends FASTElement {
private _nonReflected = "";
get nonReflected() {
Observable.track(this, 'nonReflected');
return this._nonReflected;
}
set nonReflected(value) {
const newValue = coerceType(value);
// …there might be additional validations here…
this._nonReflected = newValue;
Observable.notify(this, 'nonReflected');
}
}









With Stencil



First a disclosure: I never actually used Stencil, only played with it a bit locally in a hello-world project while writing this post.



Stencil is kind of special. It supports observable attributes through the @Prop() decorator, and reflected ones through @Prop({ reflect: true }). It will however reflect default values to attributes when the component initializes, doesn't support custom converters, and like FAST will convert an attribute value of "false" to a boolean false. You also have to add mutable: true to the @Prop() if the component modifies its value (Stencil assumes properties and attributes are inputs to the component, not state of the component).



A @Prop() must be public too, and cannot have custom accessors. You can use a @Watch() method to do some validation, but throwing from there won't prevent the property value from being updated; you can revert the property to the old value from the watch method, but other watch methods for the same property will then be called twice, and not necessarily in the correct order (depending on declaration order).



You cannot expose properties on the element's API if they are not annotated with @Prop(), making them at a minimum observe an attribute.



In other words, a Stencil component cannot, by design, feel like a built-in custom element (another thing specific to Stencil: besides @Prop() properties, you can expose methods through @Method but they must be async).

CTI Threat Relationship Graph3 Knoten / 2 Relationen
CVE / Incident Software MITRE ATT&CK CWE Weakness IoC
SOC Incident Playbook: Remote Code Execution (RCE) Defense
title: Detect Exploitation - Making Web Component properties behave closer to the platform
id: 55f2a485-2049-4d7a-b2ea-454f8d51d4af
status: experimental
description: Automatisch generierte SIEM-Erkennungsregel basierend auf CTI Intelligence
references:
  - https://tsecurity.de/
author: iShareStuff CTI Automated Detection Engine
date: 2026-09-24
logsource:
  category: network_connection
  product: any
detection:
  selection:
      CommandLine|contains:
        - 'exploit'
  condition: selection
falsepositives:
  - Legitime administrative Zugriffe oder Penetrationstests
level: high
tags:
  - attack.initial_access
rule CTI_Threat_Indicator {
    meta:
        author = "iShareStuff CTI Automated Detection Engine"
        date = "2026-09-24"
        description = "YARA Signature for "
    strings:
        $str = "Making Web Component propertie" ascii wide
    condition:
        any of them
}
tsecurity.de Cognitive Threat RAG
Fokus-Vektor:

Kognitive Analyse für identifizierte Bedrohung: Erhöhte Bedrohungslage im Bereich Making Web Component properties behave c.... Basierend auf 368k Vektor-Korrelationen werden sofortige Isolationsmaßnahmen für betroffene Endpunkte empfohlen.

🛡️ Angriffsfläche & Exposure

Netzwerk/Remote-Zugriff ohne Vorauthentifizierung möglich.

Empfohlene Sofortmaßnahmen
  • 1. Perimeter-Inspektion: Relevante Portfreigaben und exponierte Endpunkte unverzüglich scannen.
  • 2. Patch-Applikation: Hersteller-Hotfix einspielen oder betroffene Daemons in isolierte DMZ-Segmente überführen.
  • 3. Telemetrie & EDR-Alerts: Prozessaufrufe und Child-Processes auf anomale Shell-Spawns überwachen.
🔗 Semantisch verwandte Zero-Days MariaDB 11.7 VEC
Ähnliche Beiträge
🔍 Verwandte News

Auch interessante Nachrichten Making Web Component properties behave closer to the platform

Thematisch verwandte Begriffe: Making, Component, properties, behave · 6 Treffer

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Zum Aktualisieren ziehen
ZERO-DAY CVE-2026-97152 | Nanomsg versions 0.5-beta through 1.x before 1.2.3 has a remotely exploi…
Advisory →
TTS Reader • tsecurity.de Voice
tsecurity.de Icon
tsecurity.de App
Offline-Lesen, Eilmeldungen & 0ms Ladezeit

Installiere tsecurity.de direkt auf deinen Home-Bildschirm für das ultimative Vollbild-Magazinerlebnis ohne Browser-Leisten.

Nächster Beitrag
Themen-Radar & Intelligence Matrix
Echtzeit-Taxonomie nach Angriffsvektoren & Plattformen

tsecurity.de Live Threat Radar

🔴 LIVE RADAR
MONITORING
AKTIV
CVE-DATENBANK
LIVE
🔍
Community Radar & Live Chat
Sentinel Bot online • Live-Stream
Dein Cluster: Security Explorer
Match:
lädt…
Verbindung zum Community-Stream wird aufgebaut...
Bearbeitungsmodus — Senden überschreibt deine Nachricht
Community-Puls — was gerade passiert
lädt…
Aktivitäten deiner Analysten
lädt…
Neues Thema oder Eilmeldung einreichen

Reiche interessante Links, Zero-Days oder Debatten ein. Die Community entscheidet per Upvote über die Veröffentlichung.

Heiß diskutierte Einreichungen
🔖 Gespeicherte Artikel
📂 Keine gespeicherten Artikel vorhanden.
Zurück Ziehen Vor
Links: vorheriger Artikel Rechts: nächster Artikel unten: schließen
News NIS-2 Frühwarnung Tier-1 Intel TTP ⏱️ 3 Min vor 10 Min
Artikeldaten werden geladen...

Zurück: vorheriger Vor: nächster
↗ Original-Quelle
Social Reaktionen Deine Reaktion zählt
Einstufung & Relevanz-Poll 0 Stimmen
In sozialen Netzwerken teilen 1-Klick