Skip to content

feat: new core decorators @PropSerialize & @AttrDeserialize#6387

Merged
johnjenkins merged 19 commits intomainfrom
serialize-deserialize
Oct 1, 2025
Merged

feat: new core decorators @PropSerialize & @AttrDeserialize#6387
johnjenkins merged 19 commits intomainfrom
serialize-deserialize

Conversation

@johnjenkins
Copy link
Copy Markdown
Contributor

@johnjenkins johnjenkins commented Sep 16, 2025

What is the current behavior?

Following on from #6384 it surfaced a number of rough edges Stencil has around serializing properties to attributes and de-serializing attributes to properties.

Previously, Stencil has 'baked-in' certain serialization behaviours then use meta heuristics to decide when to use them.

e.g. 'When a property has an unknown type, it's incoming set value is a string, and that string starts with [ or { let's JSON parse()'

This behaviour is opaque and error-prone.

What is the new behavior?

Fixes #6247
Partially fixes #6216
Partially fixes #5110

2 new decorators have been added @PropSerialize and @AttrDeserialize - both having a similar footprint as the @Watch decorator.

@PropSerialize allows devs to explicitly set property > attribute serializers - turning a complex value into a string.
A good use case might be to use this only on the server during SSR - the serialized value being stringified in the resulting HTML can be ingested during client-side hydration.

@AttrDeserialize allows devs to explicitly set attribute > property translators. So setting a date attribute value of 2025-09-16 can now be immediately translated into a Date object.

Documentation

import { AttrDeserialize, Build, Component, h, Prop, PropSerialize } from '@stencil/core';

interface User {
  userName: string;
  avatarUrl: string;
  posts: any[]
}

@Component({
  tag: 'user-login-panel',
})
export class UserLogin {
  @Prop() user: User;

  // On the server *only* let's represent the user's data as an attribute
  // this allows the browser to get the data immediately without having to do a client-side fetch

  @PropSerialize('user')
  userSerialize(newVal: User) {
    if (Build.isBrowser) {
      return null;
    } 
    try { return JSON.stringify(newVal); } 
    catch (e) { return null; }
  }
  
  // Whenever we have an attribute (including on client init)
  // let's turn it back into an object that we can use and render

  @AttrDeserialize('user')
  userDeserialize(newVal: string) {
    try { return JSON.parse(newVal); } 
    catch (e) { return null; }
  }

  async componentWillLoad() {
    
    // On the server *only*, let's do a secret login involving private keys etc.
    
    if (Build.isServer) {  
      // Because we have a serializer method, 
      // setting a value automatically reflects it to the dom attribute 
      
      this.user = login(credentials);
    }    
  }

  render() {
    if (this.user) return (`Welcome ${this.user.userName}!`);
    else return (`Please login`);
  }
}

TODO - update docs site

Does this introduce a breaking change?

  • Yes
  • No

Testing

  • New unit tests, testing desired compiler output
  • New spec tests within Jest checking deserialize / serialize behaviour within mockdoc
  • New wdio component tests checking deserialize / serialize behaviour within browser. New SSR > Client hydration test checking a common workflow
  • Updated e2e tests that were removed in fix(runtime): stop eager json parsing for unknown and any type bindings #6384; re-implementing tests using the new decorators

Other information

@johnjenkins johnjenkins added the Request For Comments Seeking commentary on an issue or PR from the community label Sep 16, 2025
@johnjenkins
Copy link
Copy Markdown
Contributor Author

johnjenkins commented Sep 17, 2025

What do people think about the names @PropSerialize and @AttrDeserialize? Alternatives could be

  • @PropStringify / @AttrParse
  • @PropToAttr / @AttrToProp

Something else?

@exorex
Copy link
Copy Markdown

exorex commented Sep 19, 2025

I lean toward keeping @PropSerialize and @AttrDeserialize because they’re explicit, align with serialization terminology, and fit well within a framework context like Stencil.

@johnjenkins johnjenkins marked this pull request as ready for review September 30, 2025 14:32
@johnjenkins johnjenkins requested a review from a team as a code owner September 30, 2025 14:32
@johnjenkins johnjenkins removed the Request For Comments Seeking commentary on an issue or PR from the community label Sep 30, 2025
@johnjenkins johnjenkins merged commit 967c234 into main Oct 1, 2025
69 checks passed
@johnjenkins johnjenkins deleted the serialize-deserialize branch October 1, 2025 19:43
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

2 participants