Skip to content

[Idea] Disable read-only proxy in SSR v2 #5126

Open
@nolanlawson

Description

@nolanlawson

SSR v2 (and v1, for that matter) makes use of getReadOnlyProxy from observable-membrane to ensure that child components do not try to mutate their parent components' props.

@api props
connectedCallback() {
  this.props.foo = 'bar' // throws in SSR
}

Note

Note this is not exactly how engine-dom works – in that world, a child trying to modify its parent's props ends up doing a no-op. (This is why component authors often do JSON.parse(JSON.stringify()) to clone their props.) The original intention was to avoid two-way data flow – e.g. a parent rendering <div>{props.foo}</div>, where props.foo could have been mutated by a child.

In SSR, the read-only proxy is a nice guardrail. However, it comes with a fairly substantial perf cost: ~44% in one benchmark (PoC: ab3e2f9):

Screenshot 2025-01-10 at 2 49 11 PM

To be fair: there is less risk in SSR of a child truly mutating its parent, since rendering is one-time and top-down. There is still, however, potential for mischief if a child ends up modifying its siblings (e.g. because the parent passes in a prop and that prop is shared among siblings).

Thoughts:

  • Should we put this proxying behind a flag like process.env.NODE_ENV !== 'production'?
  • Should we just remove it entirely?

The downside of removing it entirely is that we would potentially be looser on the server than on the client. This could actually matter for SSR-only cases, where we don't have the extra validation of running components client-side (where children-mutating-parents is already disallowed). But maybe it's still worth it?

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions